Rating
Rating is two components wearing one name. Ask for a score and it is a real <fieldset> of native radios — arrow keys, one tab stop, a native form post, none of it written here. Show a score and it is a picture, which is the only way a fill can stop inside an icon.
- Stable
- React 19
- Native radio group
- Fractional fill
- 24px targets
<Rating name="score" label="How was the delivery?" helper="Optional, and it stays anonymous." />That is the whole editable API. count defaults to 5, clearable to true, and the
group’s name is generated when you do not pass one — native radios need a shared name to
be a group at all, so the component invents one rather than ship five separate groups.
To show a score instead of asking for one, pass readOnly:
<Rating readOnly value={4.3} />Two modes, two elements
Section titled “Two modes, two elements”This is the fact everything else on the page follows from:
| Editable | readOnly |
|
|---|---|---|
| Root element | <fieldset role="radiogroup" class="wayra-rating"> |
<div class="wayra-rating" data-readonly="true"> |
| Controls | one <input type="radio"> per score |
none |
| Accessible name | the <legend>, or your aria-label |
role="img" on the items row, named by formatLabel |
| Fill values | 0, 50 or 100 per icon | anything, under precision |
| Forms | posts a string | does not participate |
An editable rating cannot paint 4.3, because a radio can only be picked or not. A read-only one cannot be 4.3 and focusable, because there is nothing to focus. Keeping them as one component with one prop between them is what stops the two from drifting apart.
Picking a score
Section titled “Picking a score”Code
<Ratingname="score"label="How was the delivery?"helper="Optional, and it stays anonymous."/><fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md"><legend class="wayra-rating-legend">How was the delivery?</legend><div class="wayra-rating-items"><input class="wayra-rating-clear" type="radio" name="usage" value="" aria-label="No rating" /><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="usage" value="1" aria-label="1 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="usage" value="2" aria-label="2 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="usage" value="3" aria-label="3 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="usage" value="4" aria-label="4 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="usage" value="5" aria-label="5 of 5" /></label></span></div><p class="wayra-rating-hint">Optional, and it stays anonymous.</p></fieldset>Everything a group needs is already in the platform, so there is no keyboard code in this component:
| Key | Behavior |
|---|---|
Tab |
Reaches the row once — the chosen score, or “No rating” when there is none. |
← → ↑ ↓ |
Moves through the row and selects as it goes. Native, and it wraps. |
Space |
Selects the focused score. |
Clearing
Section titled “Clearing”A rating differs from a radio group in one behaviour, and it needs two affordances because one of them is invisible to the keyboard and the other to the pointer:
- Click the score you are already on and the rating goes back to none. The browser fires
no
changefor a radio that is already checked, so the component listens for the click. - ArrowLeft from the first icon reaches a visually hidden radio with
value=""that sits before the row. It is checked while nothing is, which is also whyTablands there.
Both are clearable, on by default. Turn it off and a score, once given, can only be changed:
<Rating name="score" clearable={false} />Showing a score
Section titled “Showing a score”readOnly is where the fractional fill lives. Each icon is drawn twice — an empty layer and
a filled one — and the filled copy is clipped to an exact percentage, so 4.3 of 5 is four
whole icons and one filled 30% of the way across, not “four and a half”.
precision is the rounding applied before that:
Code
<Rating readOnly value={4.3} /><Rating readOnly value={4.3} precision="half" /><Rating readOnly value={4.3} precision="full" /><div class="wayra-rating" data-color="warning" data-size="md" data-readonly="true"><span class="wayra-rating-legend">exact — 30% of the fifth icon</span><div class="wayra-rating-items" role="img" aria-label="4.3 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:30"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-size="md" data-readonly="true"><span class="wayra-rating-legend">half — snapped to a half icon</span><div class="wayra-rating-items" role="img" aria-label="4.3 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-size="md" data-readonly="true"><span class="wayra-rating-legend">full — rounded down to 4</span><div class="wayra-rating-items" role="img" aria-label="4.3 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span></div></div>precision |
Rounds to | 4.3 becomes | Use it for |
|---|---|---|---|
exact (default) |
nothing | 4.3 | an average with a number beside it |
half |
the nearest half icon | 4.5 | a compact list where a decimal is noise |
full |
the nearest whole icon | 4 | a summary that should not imply precision |
The percentage is public: --wayra-rating-fill is an inline custom property on each item,
0 to 100, and the fill layer clips itself from it. Hand-written markup sets it the same
way.
Half steps
Section titled “Half steps”half splits every icon into two hits, so the row offers 0.5, 1, 1.5, … and the arrow keys
take twice as many steps to cross it.
Code
<Rating name="score" half label="Half steps" /><Rating name="score" label="Whole steps" /><fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md" data-half="true"><legend class="wayra-rating-legend">Half steps — 10 arrow-key stops</legend><div class="wayra-rating-items"><input class="wayra-rating-clear" type="radio" name="half-demo" value="" aria-label="No rating" /><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit" data-half="start"><input class="wayra-rating-input" type="radio" name="half-demo" value="0.5" aria-label="0.5 of 5" /></label><label class="wayra-rating-hit" data-half="end"><input class="wayra-rating-input" type="radio" name="half-demo" value="1" aria-label="1 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit" data-half="start"><input class="wayra-rating-input" type="radio" name="half-demo" value="1.5" aria-label="1.5 of 5" /></label><label class="wayra-rating-hit" data-half="end"><input class="wayra-rating-input" type="radio" name="half-demo" value="2" aria-label="2 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit" data-half="start"><input class="wayra-rating-input" type="radio" name="half-demo" value="2.5" aria-label="2.5 of 5" /></label><label class="wayra-rating-hit" data-half="end"><input class="wayra-rating-input" type="radio" name="half-demo" value="3" aria-label="3 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit" data-half="start"><input class="wayra-rating-input" type="radio" name="half-demo" value="3.5" aria-label="3.5 of 5" /></label><label class="wayra-rating-hit" data-half="end"><input class="wayra-rating-input" type="radio" name="half-demo" value="4" aria-label="4 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit" data-half="start"><input class="wayra-rating-input" type="radio" name="half-demo" value="4.5" aria-label="4.5 of 5" /></label><label class="wayra-rating-hit" data-half="end"><input class="wayra-rating-input" type="radio" name="half-demo" value="5" aria-label="5 of 5" /></label></span></div></fieldset><fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md"><legend class="wayra-rating-legend">Whole steps — 5</legend><div class="wayra-rating-items"><input class="wayra-rating-clear" type="radio" name="whole-demo" value="" aria-label="No rating" /><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="whole-demo" value="1" aria-label="1 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="whole-demo" value="2" aria-label="2 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="whole-demo" value="3" aria-label="3 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="whole-demo" value="4" aria-label="4 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="whole-demo" value="5" aria-label="5 of 5" /></label></span></div></fieldset>The half row paints exactly like the whole one at the same size — same icon, same
cell. The two halves split the 24px cell, so each is ~12px wide and sits under the WCAG
2.5.8 pointer floor on purpose: making the halves clear it means either a cell twice the
icon (dead space between stars wider than the icon itself) or a 48px icon (a half-star row a
different size from every control beside it), and both were tried and rejected. The star
stays a full-size target, and the precise path is the keyboard — the arrow keys step by
halves on it.
half is editable-only. A read-only average rounds through precision instead — the two
would otherwise be two ways to spell the same thing.
Colors
Section titled “Colors”Code
<Rating readOnly value={3.5} color="primary" /><Rating readOnly value={3.5} color="secondary" /><Rating readOnly value={3.5} color="accent" /><Rating readOnly value={3.5} color="neutral" /><Rating readOnly value={3.5} color="info" /><Rating readOnly value={3.5} color="success" /><Rating readOnly value={3.5} color="warning" /><Rating readOnly value={3.5} color="error" /><svg style="display:none" aria-hidden="true"><symbol id="hu-star" viewBox="0 0 24 24"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></symbol><symbol id="hu-heart" viewBox="0 0 24 24"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></symbol></svg><div class="wayra-rating" data-size="md" data-readonly="true" data-color="primary"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="secondary"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="accent"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="neutral"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="info"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="success"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="warning"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="error"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div>The default is warning, because a rating is warm. That is only possible because the
shipped warning is a bronze rather than an amber: every role has to clear 4.5:1 against
base-100 before it may carry a component, and an amber does not.
A rating reads exactly one token — the accent, on the filled layer. The empty layer is a fixed
mix of base-content, the same in every role, so nothing has to be measured twice. There is
no -content job here, because nothing sits on top of an icon.
The icon walks the --wayra-size-selector ladder on the same multipliers
Checkbox and Radio walk, so a star and a
checkbox at the same step are the same size side by side.
Code
<Rating readOnly value={4} size="xs" /><Rating readOnly value={4} size="sm" /><Rating readOnly value={4} size="md" /><Rating readOnly value={4} size="lg" /><Rating readOnly value={4} size="xl" /><svg style="display:none" aria-hidden="true"><symbol id="hu-star" viewBox="0 0 24 24"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></symbol><symbol id="hu-heart" viewBox="0 0 24 24"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></symbol></svg><div class="wayra-rating" data-color="warning" data-readonly="true" data-size="xs"><div class="wayra-rating-items" role="img" aria-label="4 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-readonly="true" data-size="sm"><div class="wayra-rating-items" role="img" aria-label="4 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-readonly="true" data-size="md"><div class="wayra-rating-items" role="img" aria-label="4 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-readonly="true" data-size="lg"><div class="wayra-rating-items" role="img" aria-label="4 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div><div class="wayra-rating" data-color="warning" data-readonly="true" data-size="xl"><div class="wayra-rating-items" role="img" aria-label="4 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><use href="#hu-star"/></svg></span></span></div></div>The cell around the icon does not follow it all the way down:
| Size | Icon | Editable cell | Half-mode hit (half the cell) |
|---|---|---|---|
xs |
16px | 24 × 24px | 12 × 24px |
sm |
18px | 24 × 24px | 12 × 24px |
md |
20px | 24 × 24px | 12 × 24px |
lg |
24px | 24 × 24px | 12 × 24px |
xl |
28px | 28 × 28px | 14 × 28px |
Every whole-step hit clears 24 × 24 CSS px; half mode trades that on the halves for
identical geometry (see Half steps). A read-only row has no targets, so its
cell is exactly the icon. The floor is a knob — --wayra-rating-target: 24px — if a dense
table really needs to go under it.
star and heart ship. Anything else is a ReactElement you hand over, and it is rendered
into both layers exactly as the built-ins are.
Code
<Rating readOnly value={3.5} /><Rating readOnly value={3.5} icon="heart" color="error" />
<RatingreadOnlyvalue={3.5}icon={<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">…</svg>}/><div class="wayra-rating" data-color="warning" data-size="md" data-readonly="true"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span></span></div></div><div class="wayra-rating" data-size="md" data-readonly="true" data-color="error"><div class="wayra-rating-items" role="img" aria-label="3.5 of 5"><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:50"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 20.8S3.4 15.5 3.4 9.8A4.6 4.6 0 0 1 12 7.3a4.6 4.6 0 0 1 8.6 2.5c0 5.7-8.6 11-8.6 11z"/></svg></span></span></div></div>name, disabled and ref are the native <fieldset> props, so a plain form works with no
adapter: FormData posts one entry under the shared name, carrying the chosen option’s
value.
react-hook-form is built in, the same way it is on Radio.
Inside Form, every option registers itself on name alone; react-hook-form de-duplicates by
name and collects the options’ refs, so one prop wires the whole group. An explicit disabled
wins over the freeze a submitting form applies, and noRhf turns all of it off.
const schema = z.object({ score: z.coerce.number().min(1, 'Pick a score.'),});
const form = useForm({ // A string, because a native radio's value is a string. defaultValues: { score: '' }, resolver: zodResolver(schema),});
<Form form={form} onSubmit={save}> <Rating name="score" label="How did we do?" /> <Button type="submit">Save</Button></Form>The value is a string
Section titled “The value is a string”This is the one thing to remember. The score comes off a native radio’s value attribute, so
what lands in the store is "4", "2.5", or "" for no rating — never a number.
- Coerce it in the schema.
z.coerce.number()with zod,Number(value)anywhere else. - Seed
defaultValueswith a string.{ score: '' }for none,{ score: '3' }to start at three. A numeric3matches no radio, so the row would render empty. onChangeis the exception and gives you a number —4,2.5,0— because it is the component’s own prop rather than the DOM’s.
The error speaks for the group
Section titled “The error speaks for the group”Rating owns its <fieldset>, which is the difference from Radio: a
radio has no group element of its own, so its form error can only mark the options and the
message has to be your markup. Here the message has exactly one place to go — the hint under
the row, role="alert", pointed at by aria-describedby on the fieldset — and it is
announced once for the whole group. aria-invalid sits on the fieldset too, which is why it
carries role="radiogroup": ARIA 1.2 supports the state on a radio group and deprecates it on
the plain group a fieldset would otherwise be.
Code
<Rating name="score" label="Rate the driver" error="Pick a score first." /><fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md" aria-invalid="true"><legend class="wayra-rating-legend">Rate the driver</legend><div class="wayra-rating-items"><input class="wayra-rating-clear" type="radio" name="error-demo" value="" aria-label="No rating" checked /><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="error-demo" value="1" aria-label="1 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="error-demo" value="2" aria-label="2 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="error-demo" value="3" aria-label="3 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="error-demo" value="4" aria-label="4 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="error-demo" value="5" aria-label="5 of 5" /></label></span></div><p class="wayra-rating-hint">Pick a score first.</p></fieldset>An explicit error prop still wins over the form’s, and an explicit aria-invalid — including
false — wins over both.
Controlled and uncontrolled
Section titled “Controlled and uncontrolled”Outside a Form the component works either way:
<Rating defaultValue={3} onChange={setScore} /> {/* it keeps the score */}<Rating value={score} onChange={setScore} /> {/* you keep it */}Choose name or value. Inside a Form, on a field with a name, the store is the
value: a value prop is ignored and the console says so once. It never paints the row and
never seeds the store, not even while the store is empty, so what the row shows is always what
the form posts. To control a form field, call form.setValue("score", …); outside a Form, or
with noRhf, value controls the row as usual. A read-only row registers nothing, so its
value always paints.
reset() and setValue() repaint the row, because the painted fill reads the store rather
than a mirror of it. With no defaultValues a bare reset() leaves the store empty, and the
row goes back to defaultValue (or to no rating): that is the score painted, the radio
checked, and what the next submit posts.
Disabled
Section titled “Disabled”Code
<Rating name="score" label="Rate this order" defaultValue={3} disabled /><fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md" disabled><legend class="wayra-rating-legend">Rate this order</legend><div class="wayra-rating-items"><input class="wayra-rating-clear" type="radio" name="disabled-demo" value="" aria-label="No rating" /><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="disabled-demo" value="1" aria-label="1 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="disabled-demo" value="2" aria-label="2 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:100"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="disabled-demo" value="3" aria-label="3 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="disabled-demo" value="4" aria-label="4 of 5" /></label></span><span class="wayra-rating-item" style="--wayra-rating-fill:0"><span class="wayra-rating-icon" data-role="base"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><span class="wayra-rating-icon" data-role="fill"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.86 6.05 6.54.92-4.74 4.72 1.13 6.6L12 17.83 6.21 20.9l1.13-6.6L2.6 9.57l6.54-.92z"/></svg></span><label class="wayra-rating-hit"><input class="wayra-rating-input" type="radio" name="disabled-demo" value="5" aria-label="5 of 5" /></label></span></div></fieldset>One attribute on the <fieldset> takes every option out at once, which is the practical
reason the group is a fieldset and not a <div> of radios: there is no list of inputs to keep
in sync. The row also stops answering the pointer, so no hover preview fires behind the fade.
What’s not included
Section titled “What’s not included”No variant
Section titled “No variant”There is one way to draw a rating in v1: a solid empty silhouette under a solid filled one.
The prototype this was ported from also had an outline variant — hollow empty icons with a
stroked edge — and it may come back as data-variant="outline" if there is a second consumer.
Until then, one axis fewer.
No value readout
Section titled “No value readout”4.3 (128) next to the icons is two spans and a number you already have. Building it in would
mean a number formatter, a locale, a review-count prop and a way to hide all three.
No RatingGroup, and no keyboard code
Section titled “No RatingGroup, and no keyboard code”For the same reason Radio has none: a shared
name inside a <fieldset> already is a group, and the arrow keys that come with it are the
ones a rating needs.
API reference
Section titled “API reference”RatingProps extends ComponentProps<"fieldset"> minus color, defaultValue, onChange
and ref.
| Prop | Type | Default | Description |
|---|---|---|---|
color |
WayraColor |
"warning" |
The filled layer’s color. |
size |
"xs" | "sm" | "md" | "lg" | "xl" |
"md" |
Off --wayra-size-selector, same steps as Checkbox. |
count |
number |
5 |
How many icons. Clamped to at least 1, whole icons only. |
half |
boolean |
false |
Two hits per icon; editable mode only. |
clearable |
boolean |
true |
The “No rating” option and click-to-clear. |
readOnly |
boolean |
false |
Show a score instead of asking for one. |
precision |
"exact" | "half" | "full" |
"exact" |
Read-only rounding. |
icon |
"star" | "heart" | ReactElement |
"star" |
Must paint with currentColor. |
value |
number |
— | Controlled score. Ignored inside a Form on a named field. |
defaultValue |
number |
— | Uncontrolled seed. |
onChange |
(value: number) => void |
— | The chosen score as a number, 0 once cleared. |
formatLabel |
(value: number, count: number) => string |
`${value} of ${count}` |
Names each option, and the read-only row. |
label |
ReactNode |
— | The <legend>, or a <span> while read-only. |
helper |
ReactNode |
— | Group hint under the row. |
error |
ReactNode |
— | Takes the hint slot, adds role="alert", sets aria-invalid. |
noRhf |
boolean |
false |
Opt out of the surrounding Form. |
unstyled |
boolean |
false |
Drops the classes, keeps the data attributes. |
name, disabled, id and ref are the native fieldset props, typed. ref lands on the
root, which is the fieldset while editable and a <div> once readOnly, so it is typed
Ref<HTMLElement>.
Styling contract
Section titled “Styling contract”The class and data-* pair is public API and works on hand-authored markup:
<fieldset role="radiogroup" class="wayra-rating" data-color="warning" data-size="md" data-half="true"> <legend class="wayra-rating-legend">Rate this order</legend> <div class="wayra-rating-items"> <input class="wayra-rating-clear" type="radio" name="score" value="" aria-label="No rating" checked /> <span class="wayra-rating-item" style="--wayra-rating-fill:100"> <span class="wayra-rating-icon" data-role="base"><svg …></svg></span> <span class="wayra-rating-icon" data-role="fill"><svg …></svg></span> <label class="wayra-rating-hit" data-half="start"> <input class="wayra-rating-input" type="radio" name="score" value="0.5" aria-label="0.5 of 5" /> </label> <label class="wayra-rating-hit" data-half="end">…</label> </span> … </div> <p class="wayra-rating-hint">Optional.</p></fieldset>The public knobs are --wayra-rating-size, --wayra-rating-target, --wayra-rating-gap,
--wayra-rating-tone, --wayra-rating-empty, --wayra-rating-duration and the per-item
--wayra-rating-fill.
Accessibility
Section titled “Accessibility”- Grouping is the
<fieldset>withrole="radiogroup", named by its<legend>— or by anaria-labelwhen the rating renders bare. Every option is named byformatLabel. - Focus uses
:focus-visibleand rings the icon the keyboard is on, not the row, because the arrow keys move the selection through the group. The hidden “No rating” option has no icon of its own, so the row rings for it — a focused control with no visible indicator is exactly what that rule exists to prevent. - Targets clear 24 × 24 CSS px in every size (WCAG 2.5.8). In half mode the two halves split that cell and go under the floor on purpose — the star stays a full-size target, and the arrow keys are the precise path (see Half steps).
- Windows high contrast repaints the filled layer as
Highlightand the empty one asCanvasText, or both asGrayTextwhen disabled, withforced-color-adjust: noneso the two stay apart. - Reduced motion zeroes the sweep, the fill transition and the commit pop. The rating still fills; it just arrives instantly. A press no longer gives: the pressed star keeps the size the pointer gave it.
- Right to left is handled down to the browser floor:
clip-path: inset()has no logical form, so adir="rtl"on the rating or on any ancestor makes the fill eat the other edge and grow from the right.:dir()then puts an LTR island inside RTL back where the engine has it (Chrome 120 / Safari 16.4); below that, such an island fills from the wrong side.
Do and don’t
Section titled “Do and don’t”Do
Use readOnly for an average, and put the number beside it. Five icons cannot say "4.3".
Don't
Show an average with a live radio group. It rounds the value to a whole score and invites a click that changes nothing.
Do
Ship the row empty and let the user say nothing. "No rating" is checked, so Tab has somewhere to land.
Don't
Pre-select a score. Every submission is then at least a 3, whether the user looked at the row or not.
Do
Keep count at five. It is the shape everyone reads without counting.
Don't
Ask for a ten-icon score. The user counts icons instead of judging, and the row stops fitting on a phone.