Pure CSS Star Rating
A copy-paste css-only interactions component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.
CSS-Only InteractionsHTMLCSSany framework
Copy into your project
HTML
<!-- Pure CSS Star Rating — radios, reverse order, zero JS.
row-reverse lets ~ fill all stars before the hovered one. -->
<div class="nuda-css-rating" role="radiogroup" aria-label="Rating">
<input class="nuda-css-rating__input" type="radio" name="nuda-css-rating" id="nuda-css-rt-5" />
<label class="nuda-css-rating__star" for="nuda-css-rt-5" aria-label="5 stars">★</label>
<input class="nuda-css-rating__input" type="radio" name="nuda-css-rating" id="nuda-css-rt-4" checked />
<label class="nuda-css-rating__star" for="nuda-css-rt-4" aria-label="4 stars">★</label>
<input class="nuda-css-rating__input" type="radio" name="nuda-css-rating" id="nuda-css-rt-3" />
<label class="nuda-css-rating__star" for="nuda-css-rt-3" aria-label="3 stars">★</label>
<input class="nuda-css-rating__input" type="radio" name="nuda-css-rating" id="nuda-css-rt-2" />
<label class="nuda-css-rating__star" for="nuda-css-rt-2" aria-label="2 stars">★</label>
<input class="nuda-css-rating__input" type="radio" name="nuda-css-rating" id="nuda-css-rt-1" />
<label class="nuda-css-rating__star" for="nuda-css-rt-1" aria-label="1 star">★</label>
</div>CSS
/* Pure CSS Star Rating
Stars are laid out in reverse with flex-direction:row-reverse,
so the general-sibling (~) selector can fill every star up to
the hovered/checked one. No JS.
Customize: --nuda-css-rt-accent */
.nuda-css-rating {
--nuda-css-rt-accent: #e4ff54;
display: inline-flex;
flex-direction: row-reverse;
justify-content: flex-end;
}
.nuda-css-rating__input {
position: absolute;
width: 1px; height: 1px;
margin: -1px; overflow: hidden;
clip: rect(0, 0, 0, 0); border: 0;
}
.nuda-css-rating__star {
font-size: 2rem;
line-height: 1;
color: rgba(255, 255, 255, 0.18);
cursor: pointer;
padding: 0 2px;
transition: color 0.15s ease, transform 0.15s ease;
}
/* Hover: fill the hovered star and every one after it */
.nuda-css-rating__star:hover,
.nuda-css-rating__star:hover ~ .nuda-css-rating__star {
color: var(--nuda-css-rt-accent);
transform: scale(1.12);
}
/* Selected: fill the checked star and the ones after it */
.nuda-css-rating__input:checked ~ .nuda-css-rating__star {
color: var(--nuda-css-rt-accent);
}
.nuda-css-rating__input:focus-visible + .nuda-css-rating__star {
outline: 2px solid var(--nuda-css-rt-accent);
outline-offset: 2px;
border-radius: 4px;
}
@media (prefers-reduced-motion: reduce) {
.nuda-css-rating__star { transition: none; }
.nuda-css-rating__star:hover,
.nuda-css-rating__star:hover ~ .nuda-css-rating__star { transform: none; }
}How to use Pure CSS Star Rating
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.