AI CSS Animations

Star Rating Radio Buttons

Five real radios in natural order: :has() lights the chosen star and every star before it in a popping wave, so arrow keys move the right way. Hover previews a rating first.

Also known as: css star rating, 5 star rating css, star rating without javascript.

HTML

<div class="rad-star-rating" role="radiogroup" aria-label="Rating">
  <label style="--i: 0"><input type="radio" name="rad-star-rating" value="1" aria-label="1 star"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9z"/></svg></label>
  <label style="--i: 1"><input type="radio" name="rad-star-rating" value="2" aria-label="2 stars"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9z"/></svg></label>
  <label style="--i: 2"><input type="radio" name="rad-star-rating" value="3" aria-label="3 stars" checked><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9z"/></svg></label>
  <label style="--i: 3"><input type="radio" name="rad-star-rating" value="4" aria-label="4 stars"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9z"/></svg></label>
  <label style="--i: 4"><input type="radio" name="rad-star-rating" value="5" aria-label="5 stars"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2.8l2.8 5.7 6.2.9-4.5 4.4 1.1 6.2L12 17.1 6.4 20l1.1-6.2L3 9.4l6.2-.9z"/></svg></label>
</div>

CSS

.rad-star-rating {
  --c: var(--rad-color, #3b82f6);
  font-size: var(--rad-size, 18px);
  display: inline-flex;
}
.rad-star-rating label {
  position: relative;
  display: block;
  padding: 0 0.12em;
  cursor: pointer;
}
.rad-star-rating input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
  z-index: 1;
}
.rad-star-rating svg {
  display: block;
  width: 2.2em;
  height: 2.2em;
  fill: currentColor;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linejoin: round;
  color: color-mix(in srgb, currentColor 16%, transparent);
  transition: color 0.25s ease, scale 0.25s ease;
}
.rad-star-rating label:has(:checked) svg,
.rad-star-rating label:has(~ label :checked) svg {
  color: var(--c);
  animation: rad-star-rating-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: calc(var(--i) * 70ms);
}
.rad-star-rating:hover label:nth-child(n) svg {
  color: color-mix(in srgb, currentColor 16%, transparent);
}
.rad-star-rating:hover label:hover svg,
.rad-star-rating:hover label:has(~ label:hover) svg {
  color: color-mix(in srgb, var(--c) 70%, transparent);
  scale: 1.08;
}
.rad-star-rating input:focus-visible + svg {
  outline: 0.1em solid var(--c);
  outline-offset: 0.05em;
  border-radius: 0.4em;
}
@keyframes rad-star-rating-pop {
  0% { transform: scale(1); }
  40% { transform: scale(1.3) rotate(-10deg); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .rad-star-rating, .rad-star-rating *, .rad-star-rating::before, .rad-star-rating::after, .rad-star-rating *::before, .rad-star-rating *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --rad-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --rad-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 18px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar radio buttons