Rating Stars
1 to 5 star rating chosen with the mouse, a finger or the arrow keys: a real radio group, golden fill on hover, a cascade when the rating is set.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →
3 usage examples



How it works
Each star is a <label> holding a hidden radio button; the five are grouped in a <fieldset>. Picking a star means checking its button: by click, by touch, or with the keyboard using Tab then the arrows. Inside a <form>, the rating is sent with the form under the name note (value 1 to 5), and the form's reset button clears the stars too.
Each star is a grey ★ covered by a golden copy (#fbbf24), cut by clip-path: inset(). On mouse hover, the stars fill up to the one being pointed at; when leaving, they go back to the chosen rating. When a rating is chosen, the filled stars grow one after the other (rtsPop, 0.3 s, 50 ms apart) and the text becomes the word from data-rts-word followed by the rating: "Very good (4/5)".
Empty stars are #70708c, 3.6:1 on the #1a1a2e background. A rating already checked in the HTML shows on load. Each .fx-0741-root is its own group: outside a form, if two groups share the same name, the script renames one so they do not mix. Listen to the native change event on the root to send the rating.
Accessibility
- Real radio group: the screen reader announces "Your rating, group" then "3 stars out of 5, radio button, 3 of 5". The legend and names are visually hidden text, to translate in the HTML.
- Keyboard: Tab enters the group, the arrows change the rating, Tab leaves; lavender ring around the focused star. In Safari on Mac, Tab only enters the group with the system keyboard option (otherwise Option + Tab).
- Rating announced: the "Very good (4/5)" text is in an
aria-live="polite"area, read after each choice. - Contrast: empty star at 3.6:1 on the default background, full star at 10:1. On a white background, use a deeper gold (
--rts-fill: #d97706) and a darker grey: the#fbbf24gold is only 1.7:1 on white. - Reduced motion: no cascade and no growth on hover; the stars fill in place.
Browser compatibility
Native HTML form, CSS and 2.9 KB of commented, dependency-free JavaScript. Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): code pasted twice (each group keeps its rating), click, touch at 390 px, arrow keys, reduced motion, form submitted then reset, 0 errors, no name added to window.
Without JavaScript, the radio buttons work and the rating is sent with the form, but the stars do not colour. The inset property that lays the button over the star dates from Chrome 87, Firefox 66 and Safari 14.1; the :focus-visible focus ring from Chrome 86, Firefox 85, Safari 15.4. Hover uses Pointer Events (Chrome 55, Firefox 59, Safari 13).
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0741-root">
<div class="rts-demo">
<fieldset class="rts-stars">
<legend class="rts-vh">Your rating</legend>
<label class="rts-star"><input type="radio" name="note" value="1" data-rts-word="Bad"><span class="rts-glyph" aria-hidden="true">★</span><span class="rts-vh">1 star out of 5</span></label>
<label class="rts-star"><input type="radio" name="note" value="2" data-rts-word="Fair"><span class="rts-glyph" aria-hidden="true">★</span><span class="rts-vh">2 stars out of 5</span></label>
<label class="rts-star"><input type="radio" name="note" value="3" data-rts-word="Good"><span class="rts-glyph" aria-hidden="true">★</span><span class="rts-vh">3 stars out of 5</span></label>
<label class="rts-star"><input type="radio" name="note" value="4" data-rts-word="Very good"><span class="rts-glyph" aria-hidden="true">★</span><span class="rts-vh">4 stars out of 5</span></label>
<label class="rts-star"><input type="radio" name="note" value="5" data-rts-word="Excellent"><span class="rts-glyph" aria-hidden="true">★</span><span class="rts-vh">5 stars out of 5</span></label>
</fieldset>
<span class="rts-text" aria-live="polite">Click to rate</span>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Rating words (HTML — data-rts-word on each radio button) |
Bad · Fair · Good · Very good · Excellent | Word shown and announced before "(n/5)". |
Group name (HTML — the radio buttons' name) |
note |
Name sent with the form; give each group on a page a different name. |
Starting rating (HTML — checked on a radio button) |
none | Shows a rating already given, read back from your database for instance. |
Size and gap (CSS — --rts-size, --rts-gap) |
2.5rem · 8 px | Giant stars for a kiosk, small ones for a list. |
Colours (CSS — --rts-fill, --rts-empty) |
#fbbf24 · #70708c |
Keep at least 3:1 between the empty star and your background. |
Text (CSS — --rts-text-color, --rts-text-size, --rts-text-gap) |
white at 60 % · .9rem · 15 px | Darken it on a light background. |
Frame (CSS — --rts-height, --rts-page, --rts-panel, --rts-pad) |
300 px · #0a0a0f · #1a1a2e · 20 px |
0 and transparent to fit it into your card. |
FAQ
note=4). In JavaScript: root.addEventListener('change', e => send(e.target.value)), or read root.querySelector('input:checked').value.type="reset" button: stars and text go back to the start.