Rating
Rating — stars, in two genuinely different components.
Examples
<div class="rating rating-display" role="img" aria-label="Rated 3.5 out of 5"
style="--rating-value: 3.5">
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
</div>A number someone else produced, so it is an image with one name. It takes fractional values; the half star is real, drawn from the same polygon as the whole ones rather than from a second glyph.
<fieldset class="rating rating-input">
<legend class="field-label">Rate your experience</legend>
<input class="rating-radio" type="radio" id="demo-rate-1" name="demo-rate" value="1" />
<label class="rating-star" for="demo-rate-1"><span class="sr-only">1 star</span></label>
<input class="rating-radio" type="radio" id="demo-rate-2" name="demo-rate" value="2" />
<label class="rating-star" for="demo-rate-2"><span class="sr-only">2 stars</span></label>
<input class="rating-radio" type="radio" id="demo-rate-3" name="demo-rate" value="3" />
<label class="rating-star" for="demo-rate-3"><span class="sr-only">3 stars</span></label>
<input class="rating-radio" type="radio" id="demo-rate-4" name="demo-rate" value="4" />
<label class="rating-star" for="demo-rate-4"><span class="sr-only">4 stars</span></label>
<input class="rating-radio" type="radio" id="demo-rate-5" name="demo-rate" value="5" />
<label class="rating-star" for="demo-rate-5"><span class="sr-only">5 stars</span></label>
</fieldset>A real radio group: arrow keys, submission and the “3 of 5” announcement are the platform’s.
The hover preview is :has(). It is shown at the default size, like the read-only one above — this example used to carry .rating-lg and the two previews read as one component drawn at two sizes for no reason anybody could see.
<div class="rating rating-display rating-sm" role="img" aria-label="Rated 4 out of 5"
style="--rating-value: 4">
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
</div>
<div class="rating rating-display" role="img" aria-label="Rated 4 out of 5"
style="--rating-value: 4">
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
</div>
<div class="rating rating-display rating-lg" role="img" aria-label="Rated 4 out of 5"
style="--rating-value: 4">
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
<span class="rating-star"></span>
</div>The size is --rating-size and the three classes only set it.
It applies to the read-only rating freely; on the interactive one, remember that each star is a target and .rating-sm takes it under the 24px floor, so use it only where the spacing exception applies. The star itself is one shape declared twice — a polygon() for the border-shape branch and the same polygon as a mask for the base one — because border-shape shapes the box and can therefore take a real border, and a mask cannot. Change one and change both; the file says so at the declaration.
Tokens 10
Level 2, declared on .rating itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --rating-fill | 0% |
| --rating-index | 0 |
| --rating-target-size | max(var(--rating-size), var(--target-size-min, 1.5rem)) |
| --rating-value | 0 |
| --rating-mask | url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20100%20100%22%3E%3Cpath%20d%3D%22M46.82%2C6.24Q50.00%2C0.00%2053.18%2C6.24L62.72%2C24.92Q64.99%2C29.37%2069.93%2C30.16L90.64%2C33.45Q97.55%2C34.55%2092.61%2C39.50L77.79%2C54.34Q74.25%2C57.88%2075.03%2C62.82L78.30%2C83.54Q79.39%2C90.45%2073.15%2C87.28L54.46%2C77.77Q50.00%2C75.50%2045.54%2C77.77L26.85%2C87.28Q20.61%2C90.45%2021.70%2C83.54L24.97%2C62.82Q25.75%2C57.88%2022.21%2C54.34L7.39%2C39.50Q2.45%2C34.55%209.36%2C33.45L30.07%2C30.16Q35.01%2C29.37%2037.28%2C24.92Z%22%2F%3E%3C%2Fsvg%3E") |
| --rating-shape | polygon( 50% 0%, 65% 29.4%, 97.6% 34.5%, 74.3% 57.9%, 79.4% 90.5%, 50% 75.5%, 20.6% 90.5%, 25.7% 57.9%, 2.4% 34.5%, 35% 29.4% ) |
| --rating-color | var(--color-accent) |
| --rating-color-empty | var(--color-border) |
| --rating-gap | var(--space-1) |
| --rating-size | 1.25rem |
Variants and states
Variants
.rating-display.rating-input.rating-sm.rating-lg
Inside it
.rating-star.rating-radio
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
:checked:disabled:focus:hoverdata-legacy
Before you ship it
What you have to do 2 requirements
.rating-displayneedsrole="img"and anaria-labelthat states the value and the maximum ("Rated 3.5 out of 5"). The stars are decoration and are hidden from assistive technology; without the label the rating is silent..rating-inputneeds a<legend>naming the group, and every radio needs its own label text —<span class="sr-only">3 stars</span>inside the label. A label with nothing but a star in it has no accessible name.
src/css/components/rating.css · npx mostlycss add rating