All components xssmmdlg
Rating Group
Star rating input with keyboard-accessible items across four sizes.
123456789101112131415161718import { RatingGroup } from "#components/ui/ark-ui/rating-group.tsx";import { indigoPalette } from "@stylexblocks/ui/themes/palettes.ts";
export function Example() { return ( // each Item styles its star from the machine's highlighted state <RatingGroup.Root count={5} defaultValue={3} size="md" palette={indigoPalette} aria-label="Rating"> <RatingGroup.Control> {[1, 2, 3, 4, 5].map((index) => ( <RatingGroup.Item key={index} index={index} /> ))} <RatingGroup.HiddenInput /> </RatingGroup.Control> </RatingGroup.Root> );}
// controlled: <RatingGroup.Root value={value} onValueChange={(d) => setValue(d.value)}>Sizes
Install
1npx stylexblocks add rating-groupSource
components/ui/ark-ui/rating-group.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163import * as stylex from "@stylexjs/stylex";import { RatingGroup as ArkRatingGroup } from "@ark-ui/react/rating-group";import { colorPalette } from "@stylexblocks/ui/color-palette.stylex.ts";import { semanticColors } from "@stylexblocks/ui/semantic-tokens/colors.stylex.ts";import { createSlotRecipeContext } from "#lib/ark-ui/create-slot-recipe-context.tsx";import { defineSlotRecipe, resolveSlotStyles } from "#lib/recipe-props.ts";import { ratingGroupItemIndicatorIconVars } from '@stylexblocks/ui/slot-recipes/rating-group.stylex.ts'
import { fontSizes } from '@stylexblocks/ui/tokens/font-sizes.stylex.ts'import { lineHeights } from '@stylexblocks/ui/tokens/line-heights.stylex.ts'
const ratingGroupRoot = stylex.create({ base: { display: 'inline-flex', },})
const ratingGroupControl = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', },})
const ratingGroupItem = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', userSelect: 'none', },})
const ratingGroupItemSizes = stylex.create({ xs: { fontSize: fontSizes.sm, lineHeight: lineHeights.sm, }, sm: { fontSize: fontSizes.md, lineHeight: lineHeights.md, }, md: { fontSize: fontSizes.xl, lineHeight: lineHeights.xl, }, lg: { fontSize: fontSizes._2xl, lineHeight: lineHeights._2xl, },})
const ratingGroupItemIndicator = stylex.create({ base: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '1em', height: '1em', position: 'relative', [ratingGroupItemIndicatorIconVars.stroke]: 'currentColor', [ratingGroupItemIndicatorIconVars.width]: '100%', [ratingGroupItemIndicatorIconVars.height]: '100%', [ratingGroupItemIndicatorIconVars.display]: 'inline-block', [ratingGroupItemIndicatorIconVars.flexShrink]: 0, [ratingGroupItemIndicatorIconVars.position]: 'absolute', [ratingGroupItemIndicatorIconVars.left]: 0, [ratingGroupItemIndicatorIconVars.top]: 0, },})
const ratingGroupItemIndicatorIcon = stylex.create({ base: { stroke: ratingGroupItemIndicatorIconVars.stroke, width: ratingGroupItemIndicatorIconVars.width, height: ratingGroupItemIndicatorIconVars.height, display: ratingGroupItemIndicatorIconVars.display, flexShrink: ratingGroupItemIndicatorIconVars.flexShrink, position: ratingGroupItemIndicatorIconVars.position, left: ratingGroupItemIndicatorIconVars.left, top: ratingGroupItemIndicatorIconVars.top, },})
export type RatingGroupSize = keyof typeof ratingGroupItemSizes
const ratingGroupRecipe = defineSlotRecipe({ slots: { root: { styles: ratingGroupRoot }, control: { styles: ratingGroupControl }, item: { styles: ratingGroupItem, sizes: ratingGroupItemSizes }, itemIndicator: { styles: ratingGroupItemIndicator }, }, defaultVariants: { size: "md" },});
const chrome = stylex.create({ starFilled: { color: colorPalette.solid }, starEmpty: { color: semanticColors.borderEmphasized }, cursorPointer: { cursor: "pointer" },});
function StarIcon({ filled }: { filled: boolean }) { return ( <svg viewBox="0 0 24 24" fill={filled ? "currentColor" : "none"} strokeWidth="1.5" aria-hidden="true" {...stylex.props(ratingGroupItemIndicatorIcon.base)} > <path d="M12 3.2 14.7 8.8l6.1.9-4.4 4.3 1 6.1-5.4-2.9-5.4 2.9 1-6.1L3.2 9.7l6.1-.9L12 3.2Z" /> </svg> );}
function ItemNode({ children, ...props}: React.ComponentProps<typeof ArkRatingGroup.Item>) { return ( <ArkRatingGroup.Item {...props}> {children ?? ( <ArkRatingGroup.ItemContext> {(item) => ( <span {...stylex.props( ratingGroupItemIndicator.base, item.highlighted ? chrome.starFilled : chrome.starEmpty, )} > <StarIcon filled={item.highlighted} /> </span> )} </ArkRatingGroup.ItemContext> )} </ArkRatingGroup.Item> );}
const { withProvider, withContext } = createSlotRecipeContext(ratingGroupRecipe);
/** * Rating Group — Ark UI's rating machine wearing the rating-group slot * recipe. `<RatingGroup.Root size>` resolves the recipe once; each Item * styles its star from the machine's highlighted state. */export const RatingGroup = { Root: withProvider(ArkRatingGroup.Root, "root", "RatingGroupRoot"), Control: withContext(ArkRatingGroup.Control, "control", "RatingGroupControl"), Item: withContext( ItemNode, (v) => [...resolveSlotStyles(ratingGroupRecipe.slots.item, v), chrome.cursorPointer], "RatingGroupItem", ), HiddenInput: ArkRatingGroup.HiddenInput,};
export type RatingGroupRootProps = React.ComponentProps<typeof RatingGroup.Root>;export type RatingGroupControlProps = React.ComponentProps<typeof RatingGroup.Control>;export type RatingGroupItemProps = React.ComponentProps<typeof RatingGroup.Item>;