Rating Stars Input — Free HTML CSS JS Star Rating Picker
Rating Stars Input · Forms · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="rs-card">
<p class="rs-q">How was your experience?</p>
<div class="rs-stars" id="rsStars" role="radiogroup" aria-label="Rating">
<button type="button" class="rs-star" role="radio" aria-label="1 star" data-v="1" aria-checked="false"><svg viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.5 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z"/></svg></button>
<button type="button" class="rs-star" role="radio" aria-label="2 stars" data-v="2" aria-checked="false"><svg viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.5 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z"/></svg></button>
<button type="button" class="rs-star" role="radio" aria-label="3 stars" data-v="3" aria-checked="false"><svg viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.5 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z"/></svg></button>
<button type="button" class="rs-star" role="radio" aria-label="4 stars" data-v="4" aria-checked="false"><svg viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.5 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z"/></svg></button>
<button type="button" class="rs-star" role="radio" aria-label="5 stars" data-v="5" aria-checked="false"><svg viewBox="0 0 24 24"><path d="M12 2l2.9 6.3 6.9.7-5.2 4.6 1.5 6.8L12 17.5 5.9 20.4l1.5-6.8L2.2 9l6.9-.7z"/></svg></button>
</div>
<p class="rs-out" id="rsOut">Tap a star to rate</p>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0d0f18;display:flex;justify-content:center;align-items:center;min-height:100vh;padding:24px}
.rs-card{background:#151926;border:1px solid #232838;border-radius:18px;padding:28px 30px;text-align:center;width:320px}
.rs-q{color:#e7eaf3;font-size:17px;font-weight:600;margin-bottom:18px}
.rs-stars{display:inline-flex;gap:6px}
.rs-star{background:none;border:0;padding:4px;cursor:pointer;line-height:0}
.rs-star svg{width:38px;height:38px;fill:#2a3145;stroke:#3a4258;stroke-width:1;transition:fill .12s,transform .12s}
.rs-star:hover{transform:scale(1.12)}
/* Fill this star and all before it when active or hovered. */
.rs-star.is-on svg{fill:#fbbf24;stroke:#f59e0b}
.rs-star.is-hover svg{fill:#fde68a;stroke:#fbbf24}
.rs-star:focus-visible{outline:2px solid #6366f1;outline-offset:3px;border-radius:8px}
.rs-out{color:#8b93a8;font-size:14px;margin-top:18px;min-height:20px}
.rs-out strong{color:#fbbf24}var wrap = document.getElementById('rsStars');
var out = document.getElementById('rsOut');
var stars = Array.prototype.slice.call(wrap.querySelectorAll('.rs-star'));
var LABELS = ['', 'Terrible', 'Poor', 'Okay', 'Good', 'Excellent'];
var rating = 0;
// Light up every star up to and including n, using the given class.
function paint(n, cls) {
stars.forEach(function (s, i) {
s.classList.toggle(cls, i < n);
});
}
function setRating(n) {
rating = n;
stars.forEach(function (s, i) {
var on = i < n;
s.classList.toggle('is-on', on);
s.setAttribute('aria-checked', i === n - 1 ? 'true' : 'false');
});
out.innerHTML = 'You rated: <strong>' + n + '/5 · ' + LABELS[n] + '</strong>';
}
stars.forEach(function (star, i) {
var n = i + 1;
star.addEventListener('mouseenter', function () { paint(n, 'is-hover'); });
star.addEventListener('click', function () { setRating(n); });
// Keyboard: arrows move, Enter/Space commit.
star.addEventListener('keydown', function (e) {
if (e.key === 'ArrowRight' || e.key === 'ArrowUp') { stars[Math.min(i + 1, 4)].focus(); e.preventDefault(); }
if (e.key === 'ArrowLeft' || e.key === 'ArrowDown') { stars[Math.max(i - 1, 0)].focus(); e.preventDefault(); }
if (e.key === 'Enter' || e.key === ' ') { setRating(n); e.preventDefault(); }
});
});
// Clear the hover preview, leaving the committed rating shown.
wrap.addEventListener('mouseleave', function () { paint(0, 'is-hover'); });Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Rating Stars Input — A Hoverable, Keyboard-Friendly Star Picker

The rating stars input is the five-star control people use to score a product, an order, or an experience — stars fill up to the one under your cursor as a preview, and clicking commits the score. This snippet builds an accessible version with SVG stars, CSS, and vanilla JavaScript, including a hover preview, keyboard support, and a live text label.
Two layers: hover preview and committed rating
The control tracks two separate states. Hovering a star paints a lighter "is-hover" fill on it and every star before it, previewing what you'd get if you clicked; clicking commits a brighter "is-on" fill that persists. Keeping preview and selection as different classes means moving the mouse away cleanly restores the committed rating instead of clearing it — the behaviour every star input should have but many get wrong.
Fill-up-to logic
Both states use the same idea: a paint(n, cls) helper toggles a class on stars whose index is less than n, so star three lights stars one through three. There's no per-star bookkeeping — the count drives which stars are lit. The stars themselves are inline SVGs whose fill and stroke transition, so the colour change is smooth and they scale slightly on hover for a tactile feel.
Accessible by design
The group is a role="radiogroup" and each star a role="radio" with an aria-label ("3 stars") and aria-checked that reflects the committed value, so screen readers announce it as a rating control rather than a row of buttons. A :focus-visible outline shows keyboard focus, and the live label text updates so the score is conveyed in plain text too.
Keyboard control
Arrow keys move focus between stars (Right/Up forward, Left/Down back, clamped at the ends) and Enter or Space commits the focused star — the standard radio-group keyboard model. This makes the input fully operable without a mouse, which is essential for a form field.
A descriptive label
Beyond the stars, a line below shows the numeric score and a word — "4/5 · Good" — pulled from a labels array. Pairing a number with a descriptor makes the rating clearer and gives immediate feedback that the click registered.
Customizing it
Change the star count, the colours, or the label words; pre-set a rating by calling setRating on load; or read rating on form submit. Swap the SVG path for hearts or thumbs for a different scale. Pair it with a star rating display, a review form, or a rating breakdown.
Step by step
How to Use
- 1Paste HTML, CSS, and JSFive empty stars render with a prompt.
- 2Hover the starsThey fill up to the one under the cursor.
- 3Click to commitThe score sticks and the label updates.
- 4Use arrow keysMove focus and press Enter to rate.
- 5Move the mouse awayThe preview clears, the committed score stays.
- 6Read the valueGet the rating variable on submit.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The control keeps hover preview and committed selection as separate classes. Hovering paints a lighter is-hover fill, while clicking sets a persistent is-on fill. On mouseleave only the hover class is cleared, so the committed rating stays shown — the behaviour a star input should have but many implementations get wrong.
A paint helper toggles a class on every star whose index is less than the target count, so hovering or selecting star three lights stars one through three. The count drives which stars are lit, with no per-star bookkeeping, and the same logic serves both the hover preview and the committed rating.
Yes. The group is a role=radiogroup and each star a role=radio with an aria-label like 3 stars and aria-checked reflecting the committed value, so screen readers announce a rating control. Arrow keys move focus, Enter or Space commit, a focus-visible outline shows keyboard focus, and a live text label conveys the score in words.
Call setRating with the initial value on load, which applies the is-on fill to that many stars, sets aria-checked on the correct star, and updates the label. This is useful for edit forms where a user is changing an existing score rather than rating for the first time.
Hold rating and a hover value in state. Render each star lit when its index is less than the hover value (if hovering) or the committed rating otherwise, set onMouseEnter to the hover value, onMouseLeave to clear it, and onClick to commit. Keep the radiogroup roles and aria-checked bound to state. The SVG and CSS port unchanged.