Rating

v0.0.10

v0.0.10StableHeadlessStatelessAccessibleJSApache-2.0
Installation mix mishka.ui.gen.headless rating
Source View Source

Overview of the Phoenix and LiveView Headless Rating Component

The Mishka Chelekom Headless Rating is a Phoenix function component for a star rating that is a radio group underneath. It renders a div with role="radiogroup" and one button with role="radio" for each star, so the arrow keys move and select, Home and End jump to the ends, and the whole control is a single tab stop. A hidden input carries the value into a form and fires input, so a LiveView <.form phx-change> sees every change. The shared RadioGroup JS hook drives it, and the component ships no styling.

Each star is an item part styled through item_class, and the items are siblings. That lets a stylesheet fill every star up to the chosen one with :has(~ [aria-checked=true]) and no JavaScript. With precision={0.5} each star becomes two half-width items marked data-half="first" and data-half="second".

Base UI Rating example - five stars (Phoenix headless)

Base UI Rating example - five stars (Phoenix headless)

heex
<.rating
  id="baseui-rating-hero"
  value={3}
  class="flex gap-1"
  item_class="h-6 w-6 bg-neutral-300 dark:bg-neutral-700 data-[checked]:bg-amber-400 [&:has(~[aria-checked=true])]:bg-amber-400 [mask-repeat:no-repeat] [mask-position:center] [mask-size:contain] [mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20fill%3D%27black%27%20d%3D%27M12%202l3%206.5%207%20.9-5%204.8%201.3%207-6.3-3.4L5.7%2021%207%2014.2%202%209.4l7-.9z%27%2F%3E%3C%2Fsvg%3E)]"
/>

Base UI Rating example - half stars (Phoenix headless)

Base UI Rating example - half stars (Phoenix headless)

heex
<.rating
  id="baseui-rating-half"
  value={2.5}
  precision={0.5}
  class="flex"
  item_class="h-6 w-6 bg-neutral-300 dark:bg-neutral-700 data-[checked]:bg-amber-400 [&:has(~[aria-checked=true])]:bg-amber-400 [mask-repeat:no-repeat] [mask-position:center] [mask-size:contain] [mask-image:url(data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2024%2024%27%3E%3Cpath%20fill%3D%27black%27%20d%3D%27M12%202l3%206.5%207%20.9-5%204.8%201.3%207-6.3-3.4L5.7%2021%207%2014.2%202%209.4l7-.9z%27%2F%3E%3C%2Fsvg%3E)] data-[half=first]:w-3 data-[half=first]:[mask-size:200%] data-[half=first]:[mask-position:left] data-[half=second]:w-3 data-[half=second]:[mask-size:200%] data-[half=second]:[mask-position:right]"
/>

Attributes

id is required because it carries the hook. value is the current rating. count is the number of stars and defaults to 5. precision is 1.0 for whole stars or 0.5 for halves, and the rating is a float either way. label is the accessible name of the group and defaults to "Rating", so pass something specific such as "Product rating". name is the name of the hidden input that submits the value.

readonly shows a rating without letting it change, for example a review someone else left. It is not the same as disabled: focus still moves through a read-only rating, so a screen reader can read it. disabled turns the whole control off. clearable adds a first item with value 0 and aria-label="No rating", which is the only way back to no rating once one is chosen. That item is marked data-clear and takes item_class like the stars, so give it a style of its own, for example data-[clear]:w-0 to keep it focusable but invisible.

Events and forms

on_change names an event that receives %{"value" => value} whenever the rating changes. The value is a string such as "3.0" or "2.5", and "0" for the clear item. The hidden input holds the same string and fires a bubbling input event, so a surrounding <.form phx-change> receives it too.

heex
<.form for={@form} id="review-form" phx-change="validate" phx-submit="save">
  <.rating
    id="review-rating"
    name={@form[:stars].name}
    value={@form[:stars].value || 0}
    label="Your rating"
    clearable
    class="flex gap-1"
    item_class="h-6 w-6 bg-neutral-300 data-[checked]:bg-amber-400 data-[clear]:w-0"
  />
</.form>

Parts & data attributes

The root is marked data-part="root" and carries data-orientation, data-precision and data-disabled. Every item carries data-value, and data-checked or data-unchecked as its state. Half-star items add data-half, and a focused item gets data-highlighted. The structural classes are chelekom-rating and chelekom-rating__item.

Accessibility

The component follows the WAI-ARIA radio group pattern. Arrow Right and Arrow Down move to the next star, Arrow Left and Arrow Up to the previous one, and both wrap around. Home and End jump to the first and last. As in a native radio group, moving focus selects, and Space or Enter selects the focused star. The chosen star is the tab stop. When nothing is chosen yet, the first item is, so an empty rating can still be reached with the keyboard. Each star has an aria-label that names its value and the count, and a read-only rating sets aria-readonly.