Open demo

CSS code

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false"><symbol id="dk-star" viewBox="0 0 20 20"><path d="M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z"/></symbol></svg>

<div><span class="lbl">Rate an episode</span>
<div class="dk dk-ep">
  <div class="dk-still" aria-hidden="true"></div>
  <fieldset>
    <legend><span class="dk-kicker">Season 2 · Episode 4</span>How was “The Quiet Tide”?</legend>
    <div class="dk-row">
      <input type="radio" name="episode" id="dk-ep-0" value="0" checked><label class="dk-clear" for="dk-ep-0">Clear<span class="sr-only"> rating</span></label>
      <input type="radio" name="episode" id="dk-ep-1" value="1"><label class="dk-star" for="dk-ep-1"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">1 star</span></label>
      <input type="radio" name="episode" id="dk-ep-2" value="2"><label class="dk-star" for="dk-ep-2"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">2 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-3" value="3"><label class="dk-star" for="dk-ep-3"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">3 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-4" value="4"><label class="dk-star" for="dk-ep-4"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">4 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-5" value="5"><label class="dk-star" for="dk-ep-5"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">5 stars</span></label>
      <p class="dk-out" aria-hidden="true"><span data-v="0">Your rating helps us pick what to show you next.</span><span data-v="1"><b>1 of 5</b> · Not for me</span><span data-v="2"><b>2 of 5</b> · Meh</span><span data-v="3"><b>3 of 5</b> · Decent</span><span data-v="4"><b>4 of 5</b> · Really good</span><span data-v="5"><b>5 of 5</b> · Loved it</span></p>
    </div>
  </fieldset>
</div></div>

<div><span class="lbl">Film list with scores</span>
<div class="dk">
  <h2>Trending this week</h2>
  <ul class="dk-list">
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#2d3a63,#0f1322)" aria-hidden="true"></span><div><a href="#salt-and-signal">Salt and Signal</a><p>2026 · Thriller · 1 h 52 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:4.4" aria-hidden="true"></span> <b><span class="sr-only">Rated </span>4.4<span class="sr-only"> out of 5</span></b></span><small>12.8k ratings</small></p></li>
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#6b3a2d,#1a0f0c)" aria-hidden="true"></span><div><a href="#the-glasshouse-summer">The Glasshouse Summer</a><p>2025 · Drama · 2 h 06 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:3.8" aria-hidden="true"></span> <b><span class="sr-only">Rated </span>3.8<span class="sr-only"> out of 5</span></b></span><small>6.1k ratings</small></p></li>
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#2d5b4f,#0c1814)" aria-hidden="true"></span><div><a href="#understory">Understory</a><p>2026 · Documentary · 1 h 31 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:4.7" aria-hidden="true"></span> <b><span class="sr-only">Rated </span>4.7<span class="sr-only"> out of 5</span></b></span><small>3.4k ratings</small></p></li>
  </ul>
</div></div>

<div><span class="lbl">Dark summary</span>
<section class="dk dk-sum" aria-labelledby="dk-sum-title">
  <div><h2 id="dk-sum-title" class="sr-only">Audience rating</h2>
    <p class="dk-big"><span class="sr-only">Rated </span>4.4<span class="sr-only"> out of 5</span></p>
    <span class="dk-stars" style="--r:4.4;--s:20px" aria-hidden="true"></span>
    <small>12,846 audience ratings</small>
    <a class="dk-btn" href="#rate">Rate this film</a></div>
  <ul class="dk-bars" aria-label="Rating breakdown">
    <li><span>5 stars</span><span class="dk-bar" aria-hidden="true"><i style="--w:62%"></i></span><span>62%<span class="sr-only"> of ratings</span></span></li>
    <li><span>4 stars</span><span class="dk-bar" aria-hidden="true"><i style="--w:24%"></i></span><span>24%<span class="sr-only"> of ratings</span></span></li>
    <li><span>3 stars</span><span class="dk-bar" aria-hidden="true"><i style="--w:8%"></i></span><span>8%<span class="sr-only"> of ratings</span></span></li>
    <li><span>2 stars</span><span class="dk-bar" aria-hidden="true"><i style="--w:4%"></i></span><span>4%<span class="sr-only"> of ratings</span></span></li>
    <li><span>1 star</span><span class="dk-bar" aria-hidden="true"><i style="--w:2%"></i></span><span>2%<span class="sr-only"> of ratings</span></span></li>
  </ul>
</section></div>
CSS
/* Dark star ratings, tuned for a near-black ground: brighter gold with a soft glow, empty stars drawn as outlines that
   still reach 3:1 against the card, and a light-blue focus ring. The input is a native radio group in natural order:
   "Clear" (value 0) starts checked, every star is gold and the stars after the checked radio turn empty. */
.dk{--dk-on:#fbbf24;--dk-on-edge:#fde68a;--dk-glow:rgba(251,191,36,.45);--dk-off:#1c2230;--dk-edge:#7d8799;--dk-card:#141922;--dk-line:#262d3a;
  --dk-ink:#e6e9ef;--dk-muted:#9aa3b5;--dk-focus:#7dd3fc;--dk-track:#262d3a;
  --dk-star:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z'/%3E%3C/svg%3E");
  padding:26px 28px;background:var(--dk-card);border:1px solid var(--dk-line);border-radius:22px;color:var(--dk-ink)}
.dk .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.dk-kicker{display:block;margin-bottom:6px;font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--dk-muted)}
.dk h2,.dk legend{margin:0;padding:0;font-size:1.2rem;font-weight:800}

/* 1 — rate an episode */
.dk-ep{display:grid;gap:22px;grid-template-columns:minmax(0,1fr)}
@media (min-width:600px){.dk-ep{grid-template-columns:150px minmax(0,1fr);align-items:center}}
.dk-still{aspect-ratio:16/10;border-radius:14px;background:radial-gradient(120% 90% at 20% 20%,#3b4a7a,transparent 60%),radial-gradient(90% 90% at 90% 90%,#7a3b5e,transparent 60%),#10131b}
.dk fieldset{border:0;margin:0;padding:0;min-width:0}
.dk-row{position:relative;display:flex;flex-wrap:wrap;align-items:center;margin-top:14px;pointer-events:none}
.dk-row label{pointer-events:auto;cursor:pointer}
.dk-row input{position:absolute;top:0;left:0;width:1px;height:1px;margin:0;opacity:0;pointer-events:none}
.dk-star{position:relative;display:block;padding:4px;border-radius:10px}
.dk-star svg{display:block;width:34px;height:34px;fill:var(--dk-on);stroke:var(--dk-on-edge);stroke-width:.8;stroke-linejoin:round;
  filter:drop-shadow(0 0 6px var(--dk-glow));transition:fill .15s,stroke .15s,filter .15s,transform .15s}
.dk-row input:checked+label~.dk-star svg{fill:var(--dk-off);stroke:var(--dk-edge);stroke-width:1.1;filter:none}
@media (hover:hover){
  .dk .dk-row:hover .dk-star svg{fill:var(--dk-on);stroke:var(--dk-on-edge);stroke-width:.8;filter:drop-shadow(0 0 6px var(--dk-glow))}
  .dk .dk-row:hover label:hover~.dk-star svg{fill:var(--dk-off);stroke:var(--dk-edge);stroke-width:1.1;filter:none}
  .dk .dk-star:hover svg{transform:scale(1.12)}
}
.dk-star::after{content:"";position:absolute;left:50%;bottom:-4px;width:14px;height:3px;margin-left:-7px;border-radius:2px}
.dk-row input:checked+.dk-star::after{background:var(--dk-ink)}
.dk input:focus-visible+label{outline:3px solid var(--dk-focus);outline-offset:1px}
.dk-clear{order:1;margin-left:10px;padding:6px 12px;border:1px solid var(--dk-line);border-radius:999px;font-size:.82rem;font-weight:600;color:var(--dk-muted);background:transparent}
.dk-clear:hover{border-color:var(--dk-edge);color:var(--dk-ink)}
.dk-row input:checked+.dk-clear{border-style:dashed}
.dk-out{order:2;flex-basis:100%;margin-top:12px;min-height:1.3em;font-size:.92rem;color:var(--dk-muted)}
.dk-out b{color:var(--dk-ink)}
.dk-out span{display:none}
.dk input[value="0"]:checked~.dk-out [data-v="0"],.dk input[value="1"]:checked~.dk-out [data-v="1"],.dk input[value="2"]:checked~.dk-out [data-v="2"],
.dk input[value="3"]:checked~.dk-out [data-v="3"],.dk input[value="4"]:checked~.dk-out [data-v="4"],.dk input[value="5"]:checked~.dk-out [data-v="5"]{display:inline}

/* read-only stars: a gradient filled to --r under a star mask */
.dk-stars{--r:0;--s:16px;--fill:calc(var(--r) * 20%);flex:none;display:inline-block;width:calc(var(--s) * 5);height:var(--s);
  background:linear-gradient(90deg,var(--dk-on) var(--fill),#3a4252 0);
  -webkit-mask:var(--dk-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--dk-star) 0 0/var(--s) var(--s) repeat-x}
@supports (width:round(down,4.3px,1px)) and (width:mod(4.3px,1px)){.dk-stars{--fill:calc((round(down,var(--r),1) + .12 + mod(var(--r),1) * .76) * 20%)}}

/* 2 — film list */
.dk-list{list-style:none;margin:0;padding:6px 0}
.dk-list li{display:flex;align-items:center;gap:16px;padding:12px 0}
.dk-list li+li{border-top:1px solid var(--dk-line)}
.dk-poster{flex:none;width:46px;height:64px;border-radius:8px;background:var(--p)}
.dk-list div{flex:1;min-width:0}
.dk-list a{color:var(--dk-ink);font-weight:700;text-decoration:none}
.dk-list a:hover{text-decoration:underline;text-underline-offset:3px}
.dk a:focus-visible,.dk-btn:focus-visible{outline:3px solid var(--dk-focus);outline-offset:3px;border-radius:4px}
.dk-list p{margin:3px 0 0;font-size:.84rem;color:var(--dk-muted)}
.dk-score{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:4px;font-size:.88rem;font-variant-numeric:tabular-nums}
.dk-score small{font-size:.8rem;color:var(--dk-muted)}
.dk-score b{color:#fde68a}

/* 3 — dark summary */
.dk-sum{display:grid;gap:24px;grid-template-columns:minmax(0,1fr)}
@media (min-width:600px){.dk-sum{grid-template-columns:200px minmax(0,1fr);align-items:center}}
.dk-big{margin:0 0 10px;font-size:3.4rem;font-weight:800;line-height:1;letter-spacing:-.04em}
.dk-sum small{display:block;margin-top:8px;font-size:.88rem;color:var(--dk-muted)}
.dk-bars{list-style:none;margin:0;padding:0;display:grid;gap:9px}
.dk-bars li{display:grid;grid-template-columns:3.6em minmax(0,1fr) 4ch;align-items:center;gap:12px;font-size:.88rem;font-variant-numeric:tabular-nums}
.dk-bars li>span:last-child{text-align:right;color:var(--dk-muted)}
.dk-bar{display:block;height:8px;border-radius:99px;background:var(--dk-track);overflow:hidden}
.dk-bar i{display:block;height:100%;width:var(--w);border-radius:inherit;background:linear-gradient(90deg,#f59e0b,#fbbf24)}
.dk-btn{display:inline-block;margin-top:16px;padding:10px 18px;border-radius:12px;background:var(--dk-on);color:#1a1405;font-weight:800;font-size:.92rem;text-decoration:none;transition:background .15s}
.dk-btn:hover{background:#fcd34d}
@media (max-width:480px){.dk{padding:22px 18px}.dk-star svg{width:30px;height:30px}}
@media (prefers-reduced-motion:reduce){.dk-star svg,.dk-btn{transition:none}.dk .dk-star:hover svg{transform:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false"><symbol id="dk-star" viewBox="0 0 20 20"><path d="M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z"/></symbol></svg>

<div><span class="lbl">Rate an episode</span>
<div class="dk dk-ep card" data-bs-theme="dark">
  <div class="dk-still" aria-hidden="true"></div>
  <fieldset>
    <legend><span class="dk-kicker">Season 2 · Episode 4</span>How was “The Quiet Tide”?</legend>
    <div class="dk-row">
      <input class="btn-check" type="radio" name="episode" id="dk-ep-0" value="0" checked><label class="dk-clear btn rounded-pill" for="dk-ep-0">Clear<span class="visually-hidden"> rating</span></label>
      <input class="btn-check" type="radio" name="episode" id="dk-ep-1" value="1"><label class="dk-star" for="dk-ep-1"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="visually-hidden">1 star</span></label>
      <input class="btn-check" type="radio" name="episode" id="dk-ep-2" value="2"><label class="dk-star" for="dk-ep-2"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="visually-hidden">2 stars</span></label>
      <input class="btn-check" type="radio" name="episode" id="dk-ep-3" value="3"><label class="dk-star" for="dk-ep-3"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="visually-hidden">3 stars</span></label>
      <input class="btn-check" type="radio" name="episode" id="dk-ep-4" value="4"><label class="dk-star" for="dk-ep-4"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="visually-hidden">4 stars</span></label>
      <input class="btn-check" type="radio" name="episode" id="dk-ep-5" value="5"><label class="dk-star" for="dk-ep-5"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="visually-hidden">5 stars</span></label>
      <p class="dk-out" aria-hidden="true"><span data-v="0">Your rating helps us pick what to show you next.</span><span data-v="1"><b>1 of 5</b> · Not for me</span><span data-v="2"><b>2 of 5</b> · Meh</span><span data-v="3"><b>3 of 5</b> · Decent</span><span data-v="4"><b>4 of 5</b> · Really good</span><span data-v="5"><b>5 of 5</b> · Loved it</span></p>
    </div>
  </fieldset>
</div></div>

<div><span class="lbl">Film list with scores</span>
<div class="dk card" data-bs-theme="dark">
  <h2>Trending this week</h2>
  <ul class="dk-list list-unstyled">
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#2d3a63,#0f1322)" aria-hidden="true"></span><div><a href="#salt-and-signal">Salt and Signal</a><p>2026 · Thriller · 1 h 52 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:4.4" aria-hidden="true"></span> <b><span class="visually-hidden">Rated </span>4.4<span class="visually-hidden"> out of 5</span></b></span><small>12.8k ratings</small></p></li>
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#6b3a2d,#1a0f0c)" aria-hidden="true"></span><div><a href="#the-glasshouse-summer">The Glasshouse Summer</a><p>2025 · Drama · 2 h 06 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:3.8" aria-hidden="true"></span> <b><span class="visually-hidden">Rated </span>3.8<span class="visually-hidden"> out of 5</span></b></span><small>6.1k ratings</small></p></li>
    <li><span class="dk-poster" style="--p:linear-gradient(160deg,#2d5b4f,#0c1814)" aria-hidden="true"></span><div><a href="#understory">Understory</a><p>2026 · Documentary · 1 h 31 min</p></div>
      <p class="dk-score"><span><span class="dk-stars" style="--r:4.7" aria-hidden="true"></span> <b><span class="visually-hidden">Rated </span>4.7<span class="visually-hidden"> out of 5</span></b></span><small>3.4k ratings</small></p></li>
  </ul>
</div></div>

<div><span class="lbl">Dark summary</span>
<section class="dk dk-sum card" data-bs-theme="dark" aria-labelledby="dk-sum-title">
  <div><h2 id="dk-sum-title" class="visually-hidden">Audience rating</h2>
    <p class="dk-big"><span class="visually-hidden">Rated </span>4.4<span class="visually-hidden"> out of 5</span></p>
    <span class="dk-stars" style="--r:4.4;--s:20px" aria-hidden="true"></span>
    <small>12,846 audience ratings</small>
    <a class="dk-btn btn" href="#rate">Rate this film</a></div>
  <ul class="dk-bars list-unstyled m-0" aria-label="Rating breakdown">
    <li><span>5 stars</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:62%"></span></span><span>62%<span class="visually-hidden"> of ratings</span></span></li>
    <li><span>4 stars</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:24%"></span></span><span>24%<span class="visually-hidden"> of ratings</span></span></li>
    <li><span>3 stars</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:8%"></span></span><span>8%<span class="visually-hidden"> of ratings</span></span></li>
    <li><span>2 stars</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:4%"></span></span><span>4%<span class="visually-hidden"> of ratings</span></span></li>
    <li><span>1 star</span><span class="progress" aria-hidden="true"><span class="progress-bar" style="width:2%"></span></span><span>2%<span class="visually-hidden"> of ratings</span></span></li>
  </ul>
</section></div>
CSS
/* Dark star ratings on Bootstrap in data-bs-theme="dark": .card, .btn-check radios, .btn and .progress.
   Dark star ratings, tuned for a near-black ground: brighter gold with a soft glow, empty stars drawn as outlines that
   still reach 3:1 against the card, and a light-blue focus ring. The input is a native radio group in natural order:
   "Clear" (value 0) starts checked, every star is gold and the stars after the checked radio turn empty. */
.dk{--dk-on:#fbbf24;--dk-on-edge:#fde68a;--dk-glow:rgba(251,191,36,.45);--dk-off:#1c2230;--dk-edge:#7d8799;--dk-card:#141922;--dk-line:#262d3a;
  --dk-ink:#e6e9ef;--dk-muted:#9aa3b5;--dk-focus:#7dd3fc;--dk-track:#262d3a;
  --dk-star:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z'/%3E%3C/svg%3E");
  --bs-card-bg:var(--dk-card);--bs-card-border-color:var(--dk-line);--bs-card-border-radius:22px;--bs-card-color:var(--dk-ink);--bs-body-color:var(--dk-ink);
  display:grid;padding:26px 28px;color:var(--dk-ink)}
.dk p{margin-bottom:0}
.dk-kicker{display:block;margin-bottom:6px;font-size:.75rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--dk-muted)}
.dk h2,.dk legend{float:none;margin:0;line-height:1.3;padding:0;font-size:1.2rem;font-weight:800}

/* 1 — rate an episode */
.dk-ep{display:grid;gap:22px;grid-template-columns:minmax(0,1fr)}
@media (min-width:600px){.dk-ep{grid-template-columns:150px minmax(0,1fr);align-items:center}}
.dk-still{aspect-ratio:16/10;border-radius:14px;background:radial-gradient(120% 90% at 20% 20%,#3b4a7a,transparent 60%),radial-gradient(90% 90% at 90% 90%,#7a3b5e,transparent 60%),#10131b}
.dk fieldset{border:0;margin:0;padding:0;min-width:0}
.dk-row{position:relative;display:flex;flex-wrap:wrap;align-items:center;margin-top:14px;pointer-events:none}
.dk-row label{pointer-events:auto;cursor:pointer}
.dk-row input{position:absolute;top:0;left:0;width:1px;height:1px;margin:0;opacity:0;pointer-events:none}
.dk-star{position:relative;display:block;padding:4px;border-radius:10px}
.dk-star svg{display:block;width:34px;height:34px;fill:var(--dk-on);stroke:var(--dk-on-edge);stroke-width:.8;stroke-linejoin:round;
  filter:drop-shadow(0 0 6px var(--dk-glow));transition:fill .15s,stroke .15s,filter .15s,transform .15s}
.dk-row input:checked+label~.dk-star svg{fill:var(--dk-off);stroke:var(--dk-edge);stroke-width:1.1;filter:none}
@media (hover:hover){
  .dk .dk-row:hover .dk-star svg{fill:var(--dk-on);stroke:var(--dk-on-edge);stroke-width:.8;filter:drop-shadow(0 0 6px var(--dk-glow))}
  .dk .dk-row:hover label:hover~.dk-star svg{fill:var(--dk-off);stroke:var(--dk-edge);stroke-width:1.1;filter:none}
  .dk .dk-star:hover svg{transform:scale(1.12)}
}
.dk-star::after{content:"";position:absolute;left:50%;bottom:-4px;width:14px;height:3px;margin-left:-7px;border-radius:2px}
.dk-row input:checked+.dk-star::after{background:var(--dk-ink)}
.dk input:focus-visible+label{outline:3px solid var(--dk-focus);outline-offset:1px}
.dk-clear{--bs-btn-padding-x:12px;--bs-btn-padding-y:6px;--bs-btn-font-size:.82rem;--bs-btn-font-weight:600;--bs-btn-line-height:normal;
  --bs-btn-color:var(--dk-muted);--bs-btn-bg:transparent;--bs-btn-border-color:var(--dk-line);--bs-btn-hover-color:var(--dk-ink);--bs-btn-hover-bg:transparent;--bs-btn-hover-border-color:var(--dk-edge);
  --bs-btn-active-color:var(--dk-muted);--bs-btn-active-bg:transparent;--bs-btn-active-border-color:var(--dk-line);order:1;margin-left:10px;transition:none}
.dk-row .btn-check:checked+.dk-clear{border-style:dashed}
.dk .btn-check:focus-visible+.dk-clear{outline:3px solid var(--dk-focus);outline-offset:1px;box-shadow:none}
.dk-out{order:2;flex-basis:100%;margin-top:12px;min-height:1.3em;font-size:.92rem;color:var(--dk-muted)}
.dk-out b{color:var(--dk-ink)}
.dk-out span{display:none}
.dk input[value="0"]:checked~.dk-out [data-v="0"],.dk input[value="1"]:checked~.dk-out [data-v="1"],.dk input[value="2"]:checked~.dk-out [data-v="2"],
.dk input[value="3"]:checked~.dk-out [data-v="3"],.dk input[value="4"]:checked~.dk-out [data-v="4"],.dk input[value="5"]:checked~.dk-out [data-v="5"]{display:inline}

/* read-only stars: a gradient filled to --r under a star mask */
.dk-stars{--r:0;--s:16px;--fill:calc(var(--r) * 20%);flex:none;display:inline-block;width:calc(var(--s) * 5);height:var(--s);
  background:linear-gradient(90deg,var(--dk-on) var(--fill),#3a4252 0);
  -webkit-mask:var(--dk-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--dk-star) 0 0/var(--s) var(--s) repeat-x}
@supports (width:round(down,4.3px,1px)) and (width:mod(4.3px,1px)){.dk-stars{--fill:calc((round(down,var(--r),1) + .12 + mod(var(--r),1) * .76) * 20%)}}

/* 2 — film list */
.dk-list{margin:0;padding:6px 0}
.dk-list li{display:flex;align-items:center;gap:16px;padding:12px 0}
.dk-list li+li{border-top:1px solid var(--dk-line)}
.dk-poster{flex:none;width:46px;height:64px;border-radius:8px;background:var(--p)}
.dk-list div{flex:1;min-width:0}
.dk-list a{color:var(--dk-ink);font-weight:700;text-decoration:none}
.dk-list a:hover{text-decoration:underline;text-underline-offset:3px}
.dk a:focus-visible,.dk .dk-btn:focus-visible{outline:3px solid var(--dk-focus);outline-offset:3px;border-radius:4px}
.dk-list p{margin:3px 0 0;font-size:.84rem;color:var(--dk-muted)}
.dk-score{flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:4px;font-size:.88rem;font-variant-numeric:tabular-nums}
.dk-score small{font-size:.8rem;color:var(--dk-muted)}
.dk-score b{color:#fde68a}

/* 3 — dark summary */
.dk-sum{display:grid;gap:24px;grid-template-columns:minmax(0,1fr)}
@media (min-width:600px){.dk-sum{grid-template-columns:200px minmax(0,1fr);align-items:center}}
.dk-big{margin:0 0 10px;font-size:3.4rem;font-weight:800;line-height:1;letter-spacing:-.04em}
.dk-sum small{display:block;margin-top:8px;font-size:.88rem;color:var(--dk-muted)}
.dk-bars{display:grid;gap:9px}
.dk-bars li{display:grid;grid-template-columns:3.6em minmax(0,1fr) 4ch;align-items:center;gap:12px;font-size:.88rem;font-variant-numeric:tabular-nums}
.dk-bars li>span:last-child{text-align:right;color:var(--dk-muted)}
.dk .progress{--bs-progress-height:8px;--bs-progress-bg:var(--dk-track);--bs-progress-border-radius:99px}
.dk .progress-bar{border-radius:inherit;background:linear-gradient(90deg,#f59e0b,#fbbf24)}
.dk-btn{--bs-btn-padding-x:18px;--bs-btn-padding-y:10px;--bs-btn-font-size:.92rem;--bs-btn-font-weight:800;--bs-btn-line-height:normal;--bs-btn-border-radius:12px;--bs-btn-border-width:0;
  --bs-btn-color:#1a1405;--bs-btn-bg:var(--dk-on);--bs-btn-hover-color:#1a1405;--bs-btn-hover-bg:#fcd34d;--bs-btn-active-color:#1a1405;--bs-btn-active-bg:#fcd34d;--bs-btn-focus-box-shadow:none;
  margin-top:16px;transition:background-color .15s}
@media (max-width:480px){.dk{padding:22px 18px}.dk-star svg{width:30px;height:30px}}
@media (prefers-reduced-motion:reduce){.dk-star svg,.dk .dk-btn{transition:none}.dk .dk-star:hover svg{transform:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<svg width="0" height="0" class="absolute" aria-hidden="true" focusable="false"><symbol id="dk-star" viewBox="0 0 20 20"><path d="M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z"/></symbol></svg>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-muted uppercase">Rate an episode</span>
<div class="rounded-[22px] border border-dk-line bg-dk-card px-[18px] py-[22px] min-[481px]:px-7 min-[481px]:py-[26px] text-dk-ink grid grid-cols-1 gap-[22px] min-[600px]:grid-cols-[150px_minmax(0,1fr)] min-[600px]:items-center">
  <div class="aspect-[16/10] rounded-[14px] [background:radial-gradient(120%_90%_at_20%_20%,#3b4a7a,transparent_60%),radial-gradient(90%_90%_at_90%_90%,#7a3b5e,transparent_60%),#10131b]" aria-hidden="true"></div>
  <fieldset class="m-0 min-w-0 border-0 p-0">
    <legend class="p-0 text-[1.2rem] font-extrabold"><span class="mb-1.5 block text-[.75rem] font-bold tracking-[.12em] text-dk-muted uppercase">Season 2 · Episode 4</span>How was “The Quiet Tide”?</legend>
    <div class="dark-rate relative mt-3.5 flex flex-wrap items-center [--si-size:30px] min-[481px]:[--si-size:34px]">
      <input type="radio" name="episode" id="dk-ep-0" value="0" checked><label data-clear class="order-1 ml-2.5 rounded-full border border-dk-line px-3 py-1.5 text-[.82rem] font-semibold text-dk-muted hover:border-dk-edge hover:text-dk-ink" for="dk-ep-0">Clear<span class="sr-only"> rating</span></label>
      <input type="radio" name="episode" id="dk-ep-1" value="1"><label data-star class="relative block rounded-[10px] p-1" for="dk-ep-1"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">1 star</span></label>
      <input type="radio" name="episode" id="dk-ep-2" value="2"><label data-star class="relative block rounded-[10px] p-1" for="dk-ep-2"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">2 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-3" value="3"><label data-star class="relative block rounded-[10px] p-1" for="dk-ep-3"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">3 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-4" value="4"><label data-star class="relative block rounded-[10px] p-1" for="dk-ep-4"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">4 stars</span></label>
      <input type="radio" name="episode" id="dk-ep-5" value="5"><label data-star class="relative block rounded-[10px] p-1" for="dk-ep-5"><svg aria-hidden="true"><use href="#dk-star"/></svg><span class="sr-only">5 stars</span></label>
      <p data-out class="order-2 mt-3 min-h-[1.3em] basis-full text-[.92rem] text-dk-muted [&_b]:text-dk-ink" aria-hidden="true"><span data-v="0">Your rating helps us pick what to show you next.</span><span data-v="1"><b>1 of 5</b> · Not for me</span><span data-v="2"><b>2 of 5</b> · Meh</span><span data-v="3"><b>3 of 5</b> · Decent</span><span data-v="4"><b>4 of 5</b> · Really good</span><span data-v="5"><b>5 of 5</b> · Loved it</span></p>
    </div>
  </fieldset>
</div></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-muted uppercase">Film list with scores</span>
<div class="rounded-[22px] border border-dk-line bg-dk-card px-[18px] py-[22px] min-[481px]:px-7 min-[481px]:py-[26px] text-dk-ink">
  <h2 class="text-[1.2rem] font-extrabold">Trending this week</h2>
  <ul class="divide-y divide-dk-line py-1.5">
    <li class="flex items-center gap-4 py-3"><span class="h-16 w-[46px] flex-none rounded-lg bg-[linear-gradient(160deg,#2d3a63,#0f1322)]" aria-hidden="true"></span><div class="min-w-0 flex-1"><a class="font-bold text-dk-ink no-underline hover:underline hover:underline-offset-3 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-dk-focus focus-visible:rounded" href="#salt-and-signal">Salt and Signal</a><p class="mt-[3px] text-[.84rem] text-dk-muted">2026 · Thriller · 1 h 52 min</p></div>
      <p class="flex flex-none flex-col items-end gap-1 text-[.88rem] tabular-nums"><span><span class="stars" style="--r:4.4" aria-hidden="true"></span> <b class="text-[#fde68a]"><span class="sr-only">Rated </span>4.4<span class="sr-only"> out of 5</span></b></span><small class="text-[.8rem] text-dk-muted">12.8k ratings</small></p></li>
    <li class="flex items-center gap-4 py-3"><span class="h-16 w-[46px] flex-none rounded-lg bg-[linear-gradient(160deg,#6b3a2d,#1a0f0c)]" aria-hidden="true"></span><div class="min-w-0 flex-1"><a class="font-bold text-dk-ink no-underline hover:underline hover:underline-offset-3 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-dk-focus focus-visible:rounded" href="#the-glasshouse-summer">The Glasshouse Summer</a><p class="mt-[3px] text-[.84rem] text-dk-muted">2025 · Drama · 2 h 06 min</p></div>
      <p class="flex flex-none flex-col items-end gap-1 text-[.88rem] tabular-nums"><span><span class="stars" style="--r:3.8" aria-hidden="true"></span> <b class="text-[#fde68a]"><span class="sr-only">Rated </span>3.8<span class="sr-only"> out of 5</span></b></span><small class="text-[.8rem] text-dk-muted">6.1k ratings</small></p></li>
    <li class="flex items-center gap-4 py-3"><span class="h-16 w-[46px] flex-none rounded-lg bg-[linear-gradient(160deg,#2d5b4f,#0c1814)]" aria-hidden="true"></span><div class="min-w-0 flex-1"><a class="font-bold text-dk-ink no-underline hover:underline hover:underline-offset-3 focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-dk-focus focus-visible:rounded" href="#understory">Understory</a><p class="mt-[3px] text-[.84rem] text-dk-muted">2026 · Documentary · 1 h 31 min</p></div>
      <p class="flex flex-none flex-col items-end gap-1 text-[.88rem] tabular-nums"><span><span class="stars" style="--r:4.7" aria-hidden="true"></span> <b class="text-[#fde68a]"><span class="sr-only">Rated </span>4.7<span class="sr-only"> out of 5</span></b></span><small class="text-[.8rem] text-dk-muted">3.4k ratings</small></p></li>
  </ul>
</div></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-dk-muted uppercase">Dark summary</span>
<section class="rounded-[22px] border border-dk-line bg-dk-card px-[18px] py-[22px] min-[481px]:px-7 min-[481px]:py-[26px] text-dk-ink grid grid-cols-1 gap-6 min-[600px]:grid-cols-[200px_minmax(0,1fr)] min-[600px]:items-center" aria-labelledby="dk-sum-title">
  <div><h2 id="dk-sum-title" class="sr-only">Audience rating</h2>
    <p class="mb-2.5 text-[3.4rem] leading-none font-extrabold tracking-[-.04em]"><span class="sr-only">Rated </span>4.4<span class="sr-only"> out of 5</span></p>
    <span class="stars" style="--r:4.4;--s:20px" aria-hidden="true"></span>
    <small class="mt-2 block text-[.88rem] text-dk-muted">12,846 audience ratings</small>
    <a class="mt-4 inline-block rounded-xl bg-dk-on px-[18px] py-2.5 text-[.92rem] font-extrabold text-[#1a1405] no-underline transition-colors hover:bg-[#fcd34d] motion-reduce:transition-none focus-visible:outline-3 focus-visible:outline-offset-3 focus-visible:outline-dk-focus" href="#rate">Rate this film</a></div>
  <ul class="grid gap-[9px]" aria-label="Rating breakdown">
    <li class="grid grid-cols-[3.6em_minmax(0,1fr)_4ch] items-center gap-3 text-[.88rem] tabular-nums"><span>5 stars</span><span class="block h-2 overflow-hidden rounded-full bg-dk-line" aria-hidden="true"><span class="block h-full rounded-full bg-linear-to-r from-[#f59e0b] to-[#fbbf24]" style="width:62%"></span></span><span class="text-right text-dk-muted">62%<span class="sr-only"> of ratings</span></span></li>
    <li class="grid grid-cols-[3.6em_minmax(0,1fr)_4ch] items-center gap-3 text-[.88rem] tabular-nums"><span>4 stars</span><span class="block h-2 overflow-hidden rounded-full bg-dk-line" aria-hidden="true"><span class="block h-full rounded-full bg-linear-to-r from-[#f59e0b] to-[#fbbf24]" style="width:24%"></span></span><span class="text-right text-dk-muted">24%<span class="sr-only"> of ratings</span></span></li>
    <li class="grid grid-cols-[3.6em_minmax(0,1fr)_4ch] items-center gap-3 text-[.88rem] tabular-nums"><span>3 stars</span><span class="block h-2 overflow-hidden rounded-full bg-dk-line" aria-hidden="true"><span class="block h-full rounded-full bg-linear-to-r from-[#f59e0b] to-[#fbbf24]" style="width:8%"></span></span><span class="text-right text-dk-muted">8%<span class="sr-only"> of ratings</span></span></li>
    <li class="grid grid-cols-[3.6em_minmax(0,1fr)_4ch] items-center gap-3 text-[.88rem] tabular-nums"><span>2 stars</span><span class="block h-2 overflow-hidden rounded-full bg-dk-line" aria-hidden="true"><span class="block h-full rounded-full bg-linear-to-r from-[#f59e0b] to-[#fbbf24]" style="width:4%"></span></span><span class="text-right text-dk-muted">4%<span class="sr-only"> of ratings</span></span></li>
    <li class="grid grid-cols-[3.6em_minmax(0,1fr)_4ch] items-center gap-3 text-[.88rem] tabular-nums"><span>1 star</span><span class="block h-2 overflow-hidden rounded-full bg-dk-line" aria-hidden="true"><span class="block h-full rounded-full bg-linear-to-r from-[#f59e0b] to-[#fbbf24]" style="width:2%"></span></span><span class="text-right text-dk-muted">2%<span class="sr-only"> of ratings</span></span></li>
  </ul>
</section></div>
CSS
@theme {
  --color-dk-on: #fbbf24;
  --color-dk-on-edge: #fde68a;
  --color-dk-off: #1c2230;
  --color-dk-edge: #7d8799;
  --color-dk-card: #141922;
  --color-dk-line: #262d3a;
  --color-dk-ink: #e6e9ef;
  --color-dk-muted: #9aa3b5;
  --color-dk-focus: #7dd3fc;
}
/* Read-only stars: a gradient filled to --r (0–5) under a repeating star mask. Set --s for the size. */
@utility stars {
  --r: 0;
  --s: 16px;
  --fill: calc(var(--r) * 20%);
  --star: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M9.05 2.93c.3-.92 1.6-.92 1.9 0l1.07 3.29a1 1 0 0 0 .95.69h3.46c.97 0 1.37 1.24.59 1.81l-2.8 2.03a1 1 0 0 0-.36 1.12l1.07 3.3c.3.91-.76 1.68-1.54 1.11l-2.8-2.03a1 1 0 0 0-1.18 0l-2.8 2.03c-.78.57-1.84-.2-1.54-1.11l1.07-3.3a1 1 0 0 0-.36-1.12L2.98 8.72c-.78-.57-.38-1.81.59-1.81h3.46a1 1 0 0 0 .95-.69z'/%3E%3C/svg%3E");
  flex: none;
  display: inline-block;
  width: calc(var(--s) * 5);
  height: var(--s);
  background: linear-gradient(90deg, var(--color-dk-on) var(--fill), #3a4252 0);
  -webkit-mask: var(--star) 0 0 / var(--s) var(--s) repeat-x;
  mask: var(--star) 0 0 / var(--s) var(--s) repeat-x;
  @supports (width: round(down, 4.3px, 1px)) and (width: mod(4.3px, 1px)) {
    --fill: calc((round(down, var(--r), 1) + .12 + mod(var(--r), 1) * .76) * 20%);
  }
}
/* Dark star input: a native radio group in natural 1→5 order. "Clear" (value 0) starts checked; every star is gold
   with a glow and the stars AFTER the checked radio turn into outlines. */
@utility dark-rate {
  pointer-events: none;
  & label { pointer-events: auto; cursor: pointer; }
  & input { position: absolute; top: 0; left: 0; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
  & label[data-star] svg { display: block; width: var(--si-size, 34px); height: var(--si-size, 34px); fill: var(--color-dk-on); stroke: var(--color-dk-on-edge); stroke-width: .8; stroke-linejoin: round;
    filter: drop-shadow(0 0 6px rgba(251,191,36,.45)); transition: fill .15s, stroke .15s, filter .15s, transform .15s; }
  & input:checked + label ~ label[data-star] svg { fill: var(--color-dk-off); stroke: var(--color-dk-edge); stroke-width: 1.1; filter: none; }
  @media (hover: hover) {
    &:hover label[data-star] svg { fill: var(--color-dk-on); stroke: var(--color-dk-on-edge); stroke-width: .8; filter: drop-shadow(0 0 6px rgba(251,191,36,.45)); }
    &:hover label:hover ~ label[data-star] svg { fill: var(--color-dk-off); stroke: var(--color-dk-edge); stroke-width: 1.1; filter: none; }
    & label[data-star]:hover svg { transform: scale(1.12); }
  }
  & label[data-star]::after { content: ""; position: absolute; left: 50%; bottom: -4px; width: 14px; height: 3px; margin-left: -7px; border-radius: 2px; }
  & input:checked + label[data-star]::after { background: var(--color-dk-ink); }
  & input:checked + label[data-clear] { border-style: dashed; }
  & input:focus-visible + label { outline: 3px solid var(--color-dk-focus); outline-offset: 1px; }
  & [data-out] span { display: none; }
  & input[value="0"]:checked ~ [data-out] [data-v="0"], & input[value="1"]:checked ~ [data-out] [data-v="1"], & input[value="2"]:checked ~ [data-out] [data-v="2"],
  & input[value="3"]:checked ~ [data-out] [data-v="3"], & input[value="4"]:checked ~ [data-out] [data-v="4"], & input[value="5"]:checked ~ [data-out] [data-v="5"] { display: inline; }
  @media (prefers-reduced-motion: reduce) { & label[data-star] svg { transition: none; } & label[data-star]:hover svg { transform: none; } }
}

About this set

Star ratings tuned for dark interfaces such as streaming apps and media libraries. Rate an episode is a star input beside an episode still, asking how an episode was, with brighter gold stars that glow softly, outline stars for the empty ones and a Clear button, built on the same natural-order radio group as the light input. Film list with scores lists three films with a poster, year, genre and length, and a fractional star rating with its number and rating count on the right. Dark summary shows an audience average of 4.4 from 12,846 ratings, its stars, a Rate this film button and five distribution bars with percentages. Colours are checked against the near-black card: the empty star outlines reach 3:1, all text reaches 4.5:1, and the focus ring is a light blue that stands out from the gold. The Bootstrap version runs inside data-bs-theme=”dark” and uses .card, .btn-check, .btn and .progress.

What’s included

  • Glowing star input for dark backgrounds
  • Empty stars drawn as outlines with 3:1 contrast
  • Film list with fractional ratings
  • Dark rating summary with distribution bars
  • Bootstrap version in data-bs-theme="dark"

Accessibility

The input is a fieldset with a legend and radios named 1 star to 5 stars, in natural order for the arrow keys, with a light-blue 3px focus outline and a bar under the chosen star. Read-only scores are text such as Rated 4.4 out of 5 with the stars aria-hidden. The glow, transitions and hover zoom stop under prefers-reduced-motion.

Customise it

Change --dk-on, --dk-glow, --dk-off, --dk-edge, --dk-card and --dk-focus on .dk. Bootstrap reads --bs-card-bg and the .progress variables from the same values; in Tailwind edit the dk colours in @theme.