Rating

How would you rate this component?
@use 'sprucecss/scss/spruce' as *;

.rating {
  @include layout-stack('xs');

  &__legend {
    font-size: font-size('h5');
  }

  &__stars {
    display: inline-flex;
    gap: spacer('xxs');
  }

  &__control {
    @include visually-hidden;
  }

  &__star {
    color: color('border-strong');
    cursor: pointer;
    display: inline-flex;
    @include transition($property: 'color, scale');

    svg {
      block-size: 2rem;
      fill: currentcolor;
      inline-size: 2rem;
    }
  }

  // Color every star up to the checked one, and up to the hovered one.
  &__stars:has(&__control:checked) &__star {
    color: color('warning', 'alert');
  }

  &__control:checked + &__star ~ &__star {
    color: color('border-strong');
  }

  &__stars:hover &__star {
    color: color('warning', 'alert');
  }

  &__star:hover ~ &__star {
    color: color('border-strong');
  }

  &__star:hover {
    scale: 1.1;
  }

  &__control:focus-visible + &__star {
    border-radius: radius('sm');
    outline: 2px solid color('focus-ring');
    outline-offset: 2px;
  }
}
<form onsubmit="return false;">
    <fieldset class="rating">
        <legend class="rating__legend">How would you rate this component?</legend>
        <div class="rating__stars">
        <input class="rating__control" type="radio" name="rating" id="rating-1" value="1">
        <label class="rating__star" for="rating-1"><span class="sr-only">1 star</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>
        </label>
        <input class="rating__control" type="radio" name="rating" id="rating-2" value="2">
        <label class="rating__star" for="rating-2"><span class="sr-only">2 stars</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>
        </label>
        <input class="rating__control" type="radio" name="rating" id="rating-3" value="3">
        <label class="rating__star" for="rating-3"><span class="sr-only">3 stars</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>
        </label>
        <input class="rating__control" type="radio" name="rating" id="rating-4" value="4" checked>
        <label class="rating__star" for="rating-4"><span class="sr-only">4 stars</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>
        </label>
        <input class="rating__control" type="radio" name="rating" id="rating-5" value="5">
        <label class="rating__star" for="rating-5"><span class="sr-only">5 stars</span>
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m12 2 3.1 6.3 6.9 1-5 4.9 1.2 6.8L12 17.8 5.8 21l1.2-6.8-5-4.9 6.9-1z"/></svg>
        </label>
        </div>
    </fieldset>
</form>

Rating

On this page

A star rating input made of native radio buttons, with no JavaScript.

Technical Details

  • Under the stars there are five radio buttons in a fieldset with a legend, so it works with the keyboard (arrow keys) and screen readers ("4 stars, selected").
  • The radios are visually hidden, not removed; the focus ring is drawn on the star of the focused radio.
  • The fill effect uses :has() and the general sibling combinator: every star up to the checked (or hovered) one is filled.
  • The checked state also needs the radio to be before its star in the DOM, so keep the input + label order.

Documentation

Learn about Spruce CSS through our extensive documentation.

Components

Explore our UI library of accessible components built with Spruce CSS.

Blocks and Pages

Full-width page blocks and complete page templates, ready to copy.

Find us on GitHub

Did you find a bug? Have an idea or a question? Please open an issue to help us develop the project.