Rating Group
Pick a rating from a range of values.
import { Div, signal, Span } from "@implementjs/core";
import { RatingGroup, RatingGroupItem } from "@/lib/components/ui/rating-group";
export default function RatingGroupDemo() {
const value = signal(3);
return Div(
{ class: "flex flex-col items-center gap-2" },
RatingGroup(
{ value, "aria-label": "Rate this library" },
...Array.from({ length: 5 }, (_, index) => RatingGroupItem({ index })),
),
Span(
{ class: "text-sm text-muted-foreground tabular-nums" },
value.bind((v) => `${v} out of 5`),
),
);
}A rating group is a row of steps — usually stars — for choosing a value out of a maximum. RatingGroup is the root and the single focusable control; each RatingGroupItem is one visual step you fill with an icon.
import { RatingGroup, RatingGroupItem } from "@implementjs/primitives";
RatingGroup(
{ "aria-label": "Rate this product" },
...Array.from({ length: 5 }, (_, index) => RatingGroupItem({ index }, StarIcon())),
);
Each part accepts optional props and children — pass a props object when you need attributes, or pass children directly. See createComponent. Extra props are forwarded onto the underlying Divs. Items take a zero-based index; the item represents the rating index + 1.
Value and range
value defaults to 0 inside a range from min (0) to max (5). Pass a number to seed it, or a signal to control it from outside:
const rating = signal(3);
RatingGroup(
{ value: rating, "aria-label": "Rate this product" },
...Array.from({ length: 5 }, (_, index) => RatingGroupItem({ index })),
);
Clicking a step sets the value. Clicking the first step when it is already the value clears the rating back to 0 (when min is 0).
Half steps and hover
Pass allowHalf to work in half steps: the pointer's position inside an item picks the half, arrow keys move by 0.5, and an item halfway filled gets data-state="partial".
While the pointer moves across the group, the steps preview the value it would take; the preview reverts on leave. Pass hoverPreview: false to turn that off. readonly shows a value that cannot be changed, and disabled also removes the group from the Tab order.
Keyboard
The root is a single Tab stop. Arrow keys adjust the value by one step (or half with allowHalf), Home and End jump to min and max, PageUp and PageDown always move by a whole step, and typing a number picks it directly.
Accessibility
The root announces as a slider — role="slider" with aria-valuenow, aria-valuemin, and aria-valuemax — because a rating is one value out of a range, not a set of separate buttons. Items are role="presentation". Two things to know:
- The default
aria-labelis"Rating"and the defaultaria-valuetextreads"3 out of 5". Pass your own to be specific:aria-label: "Rate this product". - Keep the icons inside items
aria-hidden; the root carries all the semantics.
Styling
The root sets data-rating-group-root and items set data-rating-group-item, with data-state as "active", "partial", or "inactive", plus data-value, data-orientation, data-disabled, and data-readonly:
RatingGroupItem(
{ index, class: "group/star" },
StarIcon({ "aria-hidden": true, class: "size-5 group-data-[state=active]/star:fill-current" }),
);
Fill against data-state — solid for "active", half for "partial" when you use allowHalf.
API Reference
RatingGroup
The root and the single focusable control. Announces as a slider: role="slider" with the aria value attributes. Renders a Div; extra props are forwarded onto it.
| Prop | Type | Default | Description |
|---|---|---|---|
value | Signal<number> | number | 0 | The current rating. Pass a signal to control it from outside; a number seeds uncontrolled state. |
min | number | 0 | The lowest value the rating can take. |
max | number | 5 | The highest value the rating can take. |
allowHalf | boolean | false | Work in half steps: pointer position picks the half, arrows move by 0.5. |
readonly | boolean | false | The value can be read but not changed. |
disabled | Signal<boolean> | boolean | false | Prevents changes and removes the group from the Tab order. |
hoverPreview | boolean | true | Preview the value under the pointer before clicking. |
orientation | "horizontal" | "vertical" | "horizontal" | The axis pointer positions are measured along for half steps. |
required | boolean | false | Sets aria-required on the group. |
| Data attribute | Value |
|---|---|
[data-rating-group-root] | Present |
[data-orientation] | "horizontal" | "vertical" |
[data-disabled] | Present when disabled |
[data-readonly] | Present when readonly |
RatingGroupItem
One visual step. role="presentation" — the root carries the semantics. Fill it with an icon and style against data-state. Renders a Div; extra props are forwarded onto it.
| Prop | Type | Default | Description |
|---|---|---|---|
index* | number | — | Zero-based position; the item represents the rating index + 1. |
disabled | Signal<boolean> | boolean | false | Ignores pointer input on this item. |
| Data attribute | Value |
|---|---|
[data-rating-group-item] | Present |
[data-state] | "active" | "partial" | "inactive" |
[data-value] | The rating the item represents |
[data-orientation] | "horizontal" | "vertical" |
[data-disabled] | Present when disabled |
[data-readonly] | Present when readonly |