<aside>
  <p>🚨 Sorry, your browser doesn't support <code>:has()</code> - view this demo in <a href="https://caniuse.com/css-has" target="_blank">a supported browser</a>.</p>
</aside>

<div class="star-rating">
  <fieldset>
    <legend>Rate this demo</legend>
    <div class="stars">
      <label class="star"><input type="radio" name="rating" value="1"> <span>1</span></label>
      <label class="star"><input type="radio" name="rating" value="2"> <span>2</span></label>
      <label class="star"><input type="radio" name="rating" value="3"> <span>3</span></label>
      <label class="star"><input type="radio" name="rating" value="4"> <span>4</span></label>
      <label class="star"><input type="radio" name="rating" value="5"> <span>5</span></label>
    </div>
  </fieldset>
</div>
/*
-- Select the range of stars -- 
between the first star and the star being hovered 
OR the first star and the star with a checked radio
*/

.star:hover,
/* Previous siblings of hovered star */
.star:has(~ .star:hover),
/* Star has a checked radio */
.star:has(:checked),
/* Previous siblings of a checked star */
.star:has(~ .star :checked) {
  --star-bg: Highlight;
  --star-rating-bg: dodgerblue;
}

/*
-- Select the range of stars -- 
between the star being hovered and a checked star
*/

/* Siblings between a hovered star and a checked star */
.star:hover ~ .star:has(~ .star :checked),
/* Checked star following a hovered star */
.star:hover ~ .star:has(:checked) {
  --star-bg: Canvas;
  --star-rating-bg: lightblue;
}

/* Increase star size when the child radio is checked */
.star:has(:checked) {
  transform: scale(1.25);
}

.star-rating {
  --star-rating-bg: white;

  padding: 1rem;
  font-size: 1.75rem;
  background-color: var(--star-rating-bg);
  border-radius: 1rem;
  width: fit-content;
  margin-inline: auto;
}

.star-rating fieldset {
  border: none;
  padding: 0;
}

.star-rating legend {
  font-weight: 500;
  font-size: 1.1rem;
}

.stars {
  margin-top: 0.75em;
  display: grid;
  grid-auto-flow: column;
}

.star {
  display: grid;
  place-items: center;
  grid-template-areas: "star" "label";
  padding: 0 0.25em;
  transition: transform ease-in-out 130ms;
}

.star span {
  grid-area: label;
  font-size: 0.65em;
}

.star :checked + span {
  font-style: italic;
  font-weight: bold;
}

.star input,
.star::before,
.star::after {
  grid-area: star;
  width: 1.25em;
  height: 1.25em;
}

.star [type="radio"] {
  appearance: none;
  font: inherit;
}

.star::before,
.star::after {
  content: "";
  clip-path: polygon(
    50% 0%,
    61% 35%,
    98% 35%,
    68% 57%,
    79% 91%,
    50% 70%,
    21% 91%,
    32% 57%,
    2% 35%,
    39% 35%
  );
}

.star::before {
  width: 100%;
  height: 100%;
  box-shadow: inset 1em 1em var(--star-outline, black);
}

.star::after {
  transition: all ease-in-out 130ms;
  width: calc(100% - 0.25em);
  height: calc(100% - 0.25em);
  background-color: var(--star-bg, ButtonFace);
  box-shadow: inset 1em 1em var(--star-color, var(--star-rating-bg));
}

@media (forced-colors: active) {
  .star::before {
    --star-outline: CanvasText;
    forced-color-adjust: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .star,
  .star::after {
    transition-duration: 0.01ms;
  }
}

/* :has() support alert */
aside {
  max-width: 30ch;
  padding: 1rem;
  background-color: palegoldenrod;
  border-radius: 0.5rem;
  margin-block-end: 1rem;
  line-height: 1.3;

  code {
    font-size: 1.25em;
    color: firebrick;
  }
}

@supports selector(:has(+ *)) {
  aside {
    display: none;
  }
}
View Compiled

External CSS

  1. https://codepen.io/5t3ph/pen/LYBGjpM/e1cf47eda72f658ccbf73f9056f6a53d.css

External JavaScript

This Pen doesn't use any external JavaScript resources.