Rating
v0.0.10
mix mishka.ui.gen.headless rating
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)
<.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)
<.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.
<.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.
Attributes
| Name | Type | Default | Values | Required | Description |
|---|---|---|---|---|---|
id
|
string
| — | — | Yes | Unique id (carries the RadioGroup hook) |
name
|
string
| — | — | No | Name for the hidden input that carries the value |
value
|
any
|
0
| — | No | The current rating; a float when `precision` is 0.5 |
count
|
integer
|
5
| — | No | How many stars |
precision
|
float
|
1.0
|
1.00.5
| No | 1.0 for whole stars, 0.5 to pick halves |
label
|
string
|
Rating
| — | No | Accessible name for the group |
clearable
|
boolean
|
false
| — | No | Add a zero-width control for going back to no rating |
readonly
|
boolean
|
false
| — | No | Show the rating without letting it change; focus still moves through it |
disabled
|
boolean
|
false
| — | No | Disable the whole control |
on_change
|
any
| — | — | No | LiveView event name pushed as `%{value}` whenever the rating changes |
class
|
any
| — | — | No | Extra classes for the root |
item_class
|
any
| — | — | No | — |
rest
|
global
| — | — | No | — |
Options
Each option takes one or more of its values, separated by commas.
| Option | Values |
|---|---|
--type
|
rating
|
--module
| A module name of your own |
What it generates
mix mishka.ui.gen.headless rating