Skip to content

Rating

hc-rating renders a star rating as a radio group: each star is a real <input type="radio"> with a <label> glyph. The value serializes with the form natively, arrow-key group navigation comes from the platform, and the fill-up-to-checked rendering is pure CSS (:has(), Baseline 2023) — no JavaScript.

PrimitiveStatus
:has() (fill-up-to-checked, hover preview)Baseline 2023

Without :has() only the visual run-up degrades — the adjacent-sibling rule still fills the checked (or hovered) star itself, the radios remain a fully functional, serializing form control, and the read-only display (data-filled spans) doesn’t use :has() at all.

One input + label pair per star, in ascending DOM order; name the group with a visually-hidden <legend>.

Rate this article

The glyph is the label’s content and inherits currentColor — swap the for any character or an inline SVG without touching the CSS.

For showing an existing rating (a review list, a product card), skip the form entirely: spans with data-filled, one accessible name on the container.

data-size accepts sm, md (default), and lg.

The rating is a normal radio group — submit it like any form control, or save on change:

<form data-hx-post="/reviews/42/rate" data-hx-trigger="change"
data-hx-target="#rate-status" data-hx-swap="innerHTML">
<fieldset class="hc-rating">
<legend class="hc-sr-only">Rate this article</legend>
<!-- … the radios … -->
</fieldset>
</form>
<p id="rate-status" role="status"></p>

The server receives rate=4 in the standard form encoding — no custom serialization, no hidden inputs.

  • Name the group: a <legend> (visually hidden is fine) on the interactive form, role="img" + aria-label on the read-only form.
  • Give each star’s label an aria-label (“1 of 5” …) — the visible glyph would otherwise be announced as “black star” five times.
  • The radios are visually hidden but stay in the tree: Tab reaches the group, arrow keys move within it, and the focused star shows a ring.
  • A disabled group (<fieldset disabled>) mutes the stars and blocks input natively.
  • Half-star values can’t be expressed by a radio group — for display, round to whole stars and put the exact value in the aria-label (and visible text): aria-label="4.5 of 5 stars".

Component tokens (in component.tokens.json):

Token pathPurpose
rating.gapGap between stars.
rating.sizeGlyph size (default).
rating.colorEmpty-star color.
rating.filled-colorFilled-star color (warning/amber).
rating.transition-durationFill color transition.
rating.sm.size / lg.sizeSize variants.
Show the generated CSS variables
--hc-rating-gap | -size | -color | -filled-color
--hc-rating-transition-duration
--hc-rating-sm-size | -lg-size
--hc-color-focus-ring (inherited from data-color)
  • Radio — the underlying control, in its plain form.
  • Badge — pairs with a read-only rating in review lists.