<!-- Protocore Design System — Rating -->
# Rating

- **Category:** Inputs (`inputs`)
- **Slug:** `inputs/rating`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Rating } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/inputs/rating

> Star/mark rating — N sharp symbols with hover preview, keyboard, and optional half steps.

## When to use it

Use **Rating** to capture or display a **small ordinal score** — a review, a confidence level, a severity. For an exact value on a wide continuous range, use a **Slider**; for a few named choices, a **SegmentedControl** or **RadioGroup** reads clearer. Keep the default sharp mark to stay on-brand — pass a custom `symbol` only when the metaphor genuinely helps. In `readOnly` mode it renders as a single labelled image, so aggregate half-values display cleanly without implying interactivity.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `allowHalf` | `boolean` | no | `false` | Allow half-symbol precision (0.5 steps). |
| `aria-label` | `string` | no | `Rating` | Accessible label for the group. |
| `className` | `string` | no | — | — |
| `count` | `number` | no | `5` | Number of symbols. |
| `defaultValue` | `number` | no | `0` | Initial value when uncontrolled. |
| `disabled` | `boolean` | no | `false` | Disable interaction and mute the marks. |
| `onValueChange` | `((value: number) => void)` | no | — | Fires with the newly picked value. |
| `readOnly` | `boolean` | no | `false` | Render a static, non-interactive display of `value`. |
| `size` | `enum` | no | `md` | Symbol size: `sm` (16) · `md` (20, default) · `lg` (28). |
| `style` | `CSSProperties` | no | — | — |
| `symbol` | `ReactNode` | no | `( <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"> <path d="M12 1.5 14.4 9.6 22.5 12 14.4 14.4 12 22.5 9.6 14.4 1.5 12 9.6 9.6Z" /> </svg> )` | Custom symbol node (styled via `currentColor`); defaults to a sharp mark. |
| `value` | `number` | no | — | Controlled value (0…count). |

## Examples

### Basics

Five sharp marks by default. Hover to preview, click to set. The picked value fills the marks with the accent; `onValueChange` reports the new number.

```tsx
import { useState } from "react";
import { Rating, Text } from "@protocore/pds";

export default function Demo() {
  const [value, setValue] = useState(4);

  return (
    <div style={{ display: "grid", gap: 12 }}>
      <Rating value={value} onValueChange={setValue} aria-label="Deploy confidence" />
      <Text size="sm" color="muted">
        Confidence: {value} / 5
      </Text>
    </div>
  );
}
```

### Half steps & read-only

Set `allowHalf` for 0.5 precision — each mark splits into two hit zones. Use `readOnly` to render a static, fractional display (e.g. an aggregate score) as an image.

```tsx
import { useState } from "react";
import { Rating, Text } from "@protocore/pds";

export default function Demo() {
  const [value, setValue] = useState(3.5);

  return (
    <div style={{ display: "grid", gap: 16 }}>
      <div style={{ display: "grid", gap: 6 }}>
        <Text size="sm" color="muted">
          Editable (half steps) — {value}
        </Text>
        <Rating value={value} onValueChange={setValue} allowHalf aria-label="Service rating" />
      </div>
      <div style={{ display: "grid", gap: 6 }}>
        <Text size="sm" color="muted">
          Read-only aggregate
        </Text>
        <Rating value={4.5} readOnly aria-label="Average rating 4.5 of 5" />
      </div>
    </div>
  );
}
```

## Do & don't

**Do**

- Give the group an accessible name via `aria-label` or a wrapping `Field`.
- Use `readOnly` to display scores that shouldn't be edited.
- Enable `allowHalf` only when half precision applies.
- Keep the sharp default mark unless a custom metaphor aids meaning.

**Don't**

- Don't use it for exact values on a wide range — use a Slider.
- Don't use rounded stars; the default mark is sharp (pass a `symbol` to override).
- Don't leave it unlabelled; it announces as a radio group with no name.
- Don't set `count` beyond ~10 — the scale stops reading.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `Tab` | Move focus into the group (onto the selected mark). |
| `Arrow Right / Up` | Increase the rating by one step (0.5 with allowHalf). |
| `Arrow Left / Down` | Decrease the rating by one step. |
| `Home / End` | Set the rating to 0 / the maximum. |

**Notes**

- Interactive ratings are a `role="radiogroup"` of `role="radio"` marks, each named "N out of count".
- Roving tabindex keeps a single tab stop; arrow keys move the value and hovering previews it.
- Read-only ratings render as a single `role="img"` labelled "value out of count" with no radios.

## Related

`slider`, `segmented-control`, `radio-group`, `field`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
