apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
Rating
Variant↳ other

Form

Rating

Star (or any glyph) rating form control. One component covers interactive (user picks a rating) and read-only (display an average) modes with full W3C Slider keyboard, half-step or fractional precision, RTL, and hidden-input form integration.

Rating

The form-grade rating control. One component covers two jobs:

  • Interactive — <Rating defaultValue={0} onChange={…}> lets users pick a value.
  • Read-only display — <Rating value={3.71} readOnly precision="exact"> paints an average.

Both modes share the same role="slider", the same icon stack, the same label/description/helper wiring, and the same hidden-input form integration. There's nothing to learn twice.

Overview — interactive stars and read-only average

Loading preview…
Overview.tsx

Why this exists

Star ratings are a universal form pattern — product reviews, support surveys, NPS, content rating — but they're full of subtle mistakes when hand-rolled:

  • ❌ Stars are buttons, so users hear "button, button, button, button, button" through a screen reader.
  • ❌ No keyboard support, or arrow keys jump the wrong direction in RTL.
  • ❌ Half-stars get geometry wrong on touch / on retina / in RTL.
  • ❌ Hover preview commits on mobile because there's no real hover.
  • ❌ Form libraries see no value because there's no underlying <input>.

<Rating> fixes every one of those. The root carries role="slider" (single focus stop), each star is aria-hidden, the W3C Slider keyboard pattern is applied with RTL-aware arrow flipping, half-steps use clip-path on a layered fill (pixel-perfect), pointer math is gated to mouse (touch never gets a sticky preview), and a hidden <input name=value> ships the value to every form library.

Anatomy

tsx
┌──────────────────────────────────────────────────────┐
│  Rate your experience *               ← label        │
│  Tap a star to submit                 ← description  │
│  ★ ★ ★ ★ ☆   3 of 5                   ← track + value│
│  Please rate before submitting        ← error/helper │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│  Rate your experience *               ← label        │
│  Tap a star to submit                 ← description  │
│  ★ ★ ★ ★ ☆   3 of 5                   ← track + value│
│  Please rate before submitting        ← error/helper │
└──────────────────────────────────────────────────────┘
  • track — the role="slider" row. Tab-stops here.
  • star (×N) — aria-hidden, layered <empty> + <filled> SVG. The filled layer is clipped from the trailing edge so any fraction (whole / half / 0.71) renders pixel-perfect.
  • label / description / helper / error — wired via the shared useFormFieldA11y hook.

Examples

Basic — uncontrolled, default scale of 5

Loading preview…
Basic.tsx

Controlled — value lifted into parent state

Loading preview…
Controlled.tsx

Half-step — click left half of star N for N − 0.5

Loading preview…
HalfStep.tsx

Read-only with exact fractional fill (averages)

Loading preview…
ReadOnlyExact.tsx

Custom scales — 3, 10, anything

Loading preview…
CustomScale.tsx

Custom glyph — swap any ReactNode in

Loading preview…
HeartGlyph.tsx

Sizes — sm / md / lg

Loading preview…
Sizes.tsx

Colors — warning (default) / primary / success / danger / neutral

Loading preview…
Colors.tsx

Show the numeric value next to the stars

Loading preview…
ShowValue.tsx

allowClear — click the current value to reset

Loading preview…
AllowClear.tsx

Disabled — no focus, no interaction

Loading preview…
Disabled.tsx

Read-only — static display

Loading preview…
ReadOnly.tsx

With label, description, and helper text

Loading preview…
WithLabel.tsx

Error state — required field, aria-invalid wired in

Loading preview…
ErrorState.tsx

Inside a form — hidden input ships the value

Loading preview…
InForm.tsx

Realistic — Rating + Textarea + Submit composition

Loading preview…
ProductReviewCard.tsx

Props

PropTypeDefaultNotes
valuenumber—Controlled value. Pair with onChange.
defaultValuenumber0Uncontrolled initial value.
onChange(value, meta) => void—meta.source is 'click' | 'keyboard' | 'clear'.
maxnumber5Number of stars.
precision1 | 0.5 | 'exact'1'exact' is read-only-only and renders arbitrary fractional fill.
allowClearbooleanfalseClicking the current value sets it to 0.
iconReactNodeinline starFilled glyph.
emptyIconReactNodeinline starEmpty glyph. Defaults to icon (with fill="none" outline if you don't pass one).
readOnlybooleanfalseStatic display; disables click + keyboard but stays focusable for AT.
disabledbooleanfalseRemoves focus and interaction.
requiredbooleanfalseSets aria-required + required on the hidden input.
namestring—Hidden-input name for HTML form submission. Value 0 posts as empty string.
size'sm' | 'md' | 'lg''md'Star size + gap density.
color'warning' | 'primary' | 'success' | 'danger' | 'neutral''warning'Filled-glyph color. Empty layer always uses text-border-default.
showValuebooleanfalseAppends "{value} of {max}" next to the stars.
labelReactNode—Visible label above the stars.
descriptionReactNode—Hint below the label.
helperTextReactNode—Bottom helper. Hidden when error is set.
errorReactNode—Bottom error. Sets aria-invalid="true" + data-invalid="true".
hideLabelbooleanfalseVisually hides the label (sr-only).
ariaLabelstring'Rating'Accessible name when no visible label is provided.
formatValueText(value, max) => stringen stringOverride the aria-valuetext announcement. Defaults to "{v} out of {max} stars".

Keyboard

Follows the W3C Slider pattern:

KeyAction
→ / ↑Increase by precision (RTL: → decreases)
← / ↓Decrease by precision (RTL: ← increases)
HomeSet to 0 (when allowClear) or the smallest step
EndSet to max
PageUpIncrease by 1 whole star
PageDownDecrease by 1 whole star
Digit 0–9Jump to that value (0 only when allowClear)

A11y

  • Root is role="slider" with aria-valuemin={0}, aria-valuemax={max}, aria-valuenow={value}, aria-valuetext populated via formatValueText. Each star is aria-hidden.
  • readOnly sets aria-readonly="true" + tabIndex={0} (focusable for AT, not interactive).
  • disabled sets aria-disabled="true" + tabIndex={-1}.
  • required sets aria-required="true"; error sets aria-invalid="true".
  • Label / description / helper / error are wired via useFormFieldA11y (shared hook used by <Input>, <Textarea>, <Select>).
  • Error text carries role="alert" so AT announces validation failures as they land.
  • Hover preview never updates aria-valuenow — only committed values are announced.
  • axe-core: 0 violations across all modes (verified in Rating.a11y.test.tsx).

RTL

  • Stars visually flip — "star 1" is on the right under dir="rtl".
  • Arrow keys flip — ArrowRight decreases in RTL, matching W3C Slider.
  • Pointer math mirrors — clicking the right edge in RTL still picks "star 1".
  • The filled clip grows from the leading edge in both directions.

Theming

<Rating> is in the theme registry as Rating. Override slots via theme.components.Rating:

ts
const theme = createTheme({
  components: {
    Rating: {
      defaultProps: { color: 'primary', size: 'lg' },
      styleOverrides: {
        track: 'gap-2',
        starFilled: 'drop-shadow-sm',
        label: 'uppercase tracking-wide',
      },
    },
  },
});
const theme = createTheme({
  components: {
    Rating: {
      defaultProps: { color: 'primary', size: 'lg' },
      styleOverrides: {
        track: 'gap-2',
        starFilled: 'drop-shadow-sm',
        label: 'uppercase tracking-wide',
      },
    },
  },
});

Slot names: wrapper, label, description, track, star, starEmpty, starFilled, valueText, helperText.

See also

  • <Slider> — for arbitrary numeric ranges where stars aren't the right metaphor.
  • <RadioGroup> — for distinct, named choices (Yes / No / Maybe) rather than ordinal scales.

Props

PropTypeDefaultDescription
allowClearbooleanfalseClicking the currently-selected value clears it to 0.
ariaLabelstring—Override accessible name when no visible label is provided.
colorResponsiveValue<RatingColor>'warning'Glyph color.
defaultValuenumber0Uncontrolled initial value. Use `value` for controlled.
descriptionReactNode—Hint text between the label and the stars.
disabledboolean—Mirrors the native `disabled` attribute. Blocks all interaction.
emptyIconReactNode—Empty-state glyph. Defaults to the outline version of the filled glyph.
errorReactNode—Bottom error text. Sets `aria-invalid="true"` when present.
formatValueTextRatingValueFormatter—Override the `aria-valuetext` string. Defaults to `"{value} out of {max} stars"`.
helperTextReactNode—Bottom helper text. Hidden when `error` is set.
hideLabelbooleanfalseVisually hide the label (still sr-only for AT).
iconReactNode—Filled-state glyph. Defaults to an inline SVG star.
labelReactNode—Visible label above the stars (becomes the accessible name when set).
maxnumber5Number of stars in the scale.
namestring—Hidden-input name for HTML form submission.
onChangeRatingChangeHandler—Called on every committed value change.
precisionenum1Selection granularity.
readOnlyboolean—Render the control as static display. Disables click / keyboard.
requiredboolean—Sets `aria-required="true"` and `required` on the hidden input.
showValuebooleanfalseAppend a `"{value} of {max}"` text node next to the stars.
sizeResponsiveValue<RatingSize>'md'Visual density.
sxSx—Theme-aware inline style object (resolves palette / spacing / radius tokens to CSS vars).
valuenumber—Controlled value. Pair with `onChange`.