Open demo

CSS code

HTML
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false"><symbol id="sd-star-shape" 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 class="sd"><span class="lbl">Fractional stars with a CSS mask</span>
<ul class="sd-list" aria-label="Reader ratings">
  <li><div class="sd-book"><b>A Field Guide to Small Rivers</b><span>Maren Okafor</span></div>
    <p class="sd-score"><span class="sd-stars" style="--r:4.8" aria-hidden="true"></span><strong><span class="sr-only">Rated </span>4.8<span class="sr-only"> out of 5</span></strong><small>3,412 ratings</small></p></li>
  <li><div class="sd-book"><b>The Salt Orchard</b><span>Teodor Lindqvist</span></div>
    <p class="sd-score"><span class="sd-stars" style="--r:4.3" aria-hidden="true"></span><strong><span class="sr-only">Rated </span>4.3<span class="sr-only"> out of 5</span></strong><small>2,104 ratings</small></p></li>
  <li><div class="sd-book"><b>Harbour Lights at Dusk</b><span>Priya Raman</span></div>
    <p class="sd-score"><span class="sd-stars" style="--r:3.6" aria-hidden="true"></span><strong><span class="sr-only">Rated </span>3.6<span class="sr-only"> out of 5</span></strong><small>868 ratings</small></p></li>
  <li><div class="sd-book"><b>Northbound, Mostly</b><span>Callum Avery</span></div>
    <p class="sd-score"><span class="sd-stars" style="--r:2.5" aria-hidden="true"></span><strong><span class="sr-only">Rated </span>2.5<span class="sr-only"> out of 5</span></strong><small>157 ratings</small></p></li>
</ul></div>

<div class="sd"><span class="lbl">Inline SVG stars rounded to the half</span>
<div class="sd-hotels">
  <article class="sd-card"><h3>Juniper Lodge</h3><p>Cabins · Lake Varna</p>
    <div class="sd-svg"><span aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="half"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>4.5<span class="sr-only"> out of 5</span></b></div></article>
  <article class="sd-card"><h3>The Ferryman Inn</h3><p>Guesthouse · Old Quay</p>
    <div class="sd-svg"><span aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="half"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>3.5<span class="sr-only"> out of 5</span></b></div></article>
  <article class="sd-card"><h3>Cedar & Stone</h3><p>Boutique hotel · Market Row</p>
    <div class="sd-svg"><span aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>2<span class="sr-only"> out of 5</span></b></div></article>
</div></div>

<div class="sd"><span class="lbl">Outlined stars clipped to the score</span>
<div class="sd-hero">
  <p class="sd-big"><span class="sr-only">Rated </span>4.3<small aria-hidden="true"> / 5</small><span class="sr-only"> out of 5</span></p>
  <div><span class="sd-line" style="--r:4.3" aria-hidden="true"></span>
    <p class="sd-verdict"><b>Very good</b> · from 1,204 guest ratings</p></div>
</div></div>
CSS
/* Read-only star ratings. The value is always text ("Rated 4.3 out of 5"); the stars are aria-hidden graphics.
   .sd-stars: one element, a gradient filled to --r (0–5) under a repeating star mask, so 4.3 shows 4.3 stars.
   .sd-svg:   five inline SVG stars, each a grey star with a gold copy on top clipped to full, half or none.
   .sd-line:  an outline row with a solid row on top, clipped with clip-path: inset() to --r. */
.sd{--sd-on:#f59e0b;--sd-off:#ddd7ce;--sd-ink:#1f2328;--sd-muted:#5b616e;--sd-line:#e8e3da;--sd-edge:#b45309;
  --sd-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");
  --sd-outline:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.4' stroke-linejoin='round' 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");
  color:var(--sd-ink)}
.sd .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}

/* 1 — fractional stars with a CSS mask */
.sd-stars,.sd-line{--r:0;--fill:calc(var(--r) * 20%)}
/* Where round() and mod() exist, map the fraction onto the star's body so 4.3 fills 30% of the fifth star. */
@supports (width:round(down,4.3px,1px)) and (width:mod(4.3px,1px)){
  .sd-stars,.sd-line{--fill:calc((round(down,var(--r),1) + .12 + mod(var(--r),1) * .76) * 20%)}}
.sd-stars{--s:20px;flex:none;display:inline-block;width:calc(var(--s) * 5);height:var(--s);
  background:linear-gradient(90deg,var(--sd-on) var(--fill),var(--sd-off) 0);
  -webkit-mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x}
.sd-list{list-style:none;margin:0;padding:0;background:#fff;border:1px solid var(--sd-line);border-radius:18px}
.sd-list li{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 20px;padding:16px 22px}
.sd-list li+li{border-top:1px solid var(--sd-line)}
.sd-book{min-width:0}
.sd-book b{display:block;font-size:1rem;font-weight:700}
.sd-book span{display:block;margin-top:2px;font-size:.86rem;color:var(--sd-muted)}
.sd-score{display:flex;align-items:center;gap:10px;font-size:.92rem;font-variant-numeric:tabular-nums}
.sd-score strong{min-width:2.2ch;font-weight:800}
.sd-score small{font-size:.84rem;color:var(--sd-muted)}

/* 2 — inline SVG stars rounded to the nearest half */
.sd-hotels{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px}
.sd-card{padding:20px 22px;background:#fff;border:1px solid var(--sd-line);border-radius:18px}
.sd-card h3{margin:0;font-size:1rem;font-weight:700}
.sd-card p{margin:4px 0 14px;font-size:.86rem;color:var(--sd-muted)}
.sd-svg{display:flex;align-items:center;gap:10px;font-size:.92rem;font-variant-numeric:tabular-nums}
.sd-svg>span{display:inline-flex;gap:2px}
.sd-svg svg{width:22px;height:22px;display:block}
.sd-svg .bg{fill:var(--sd-off)}
.sd-svg .fg{fill:var(--sd-on)}
.sd-svg .half .fg{clip-path:inset(0 50% 0 0)}
.sd-svg .none .fg{display:none}

/* 3 — outlined stars clipped to the score */
.sd-hero{display:flex;flex-wrap:wrap;align-items:center;gap:18px 28px;padding:26px 28px;background:#fff;border:1px solid var(--sd-line);border-radius:22px}
.sd-big{font-size:3.6rem;font-weight:800;line-height:1;letter-spacing:-.04em}
.sd-big small{font-size:1.1rem;font-weight:600;letter-spacing:0;color:var(--sd-muted)}
.sd-line{--s:30px;position:relative;display:block;width:calc(var(--s) * 5);height:var(--s)}
.sd-line::before,.sd-line::after{content:"";position:absolute;inset:0;
  -webkit-mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;background:var(--sd-on);
  clip-path:inset(0 calc(100% - var(--fill)) 0 0)}
.sd-line::after{background:var(--sd-edge);-webkit-mask-image:var(--sd-outline);mask-image:var(--sd-outline);clip-path:none}
.sd-verdict{margin-top:10px;font-size:.95rem;color:var(--sd-muted)}
.sd-verdict b{color:var(--sd-ink)}

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="sd-star-shape" 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 class="sd"><span class="lbl">Fractional stars with a CSS mask</span>
<ul class="list-group" aria-label="Reader ratings">
  <li class="list-group-item d-flex flex-wrap align-items-center justify-content-between gap-2 column-gap-4"><div class="text-break"><b class="d-block">A Field Guide to Small Rivers</b><span class="d-block mt-1 small sd-muted">Maren Okafor</span></div>
    <p class="sd-score d-flex align-items-center gap-2 m-0"><span class="sd-stars" style="--r:4.8" aria-hidden="true"></span><strong><span class="visually-hidden">Rated </span>4.8<span class="visually-hidden"> out of 5</span></strong><small class="sd-muted">3,412 ratings</small></p></li>
  <li class="list-group-item d-flex flex-wrap align-items-center justify-content-between gap-2 column-gap-4"><div class="text-break"><b class="d-block">The Salt Orchard</b><span class="d-block mt-1 small sd-muted">Teodor Lindqvist</span></div>
    <p class="sd-score d-flex align-items-center gap-2 m-0"><span class="sd-stars" style="--r:4.3" aria-hidden="true"></span><strong><span class="visually-hidden">Rated </span>4.3<span class="visually-hidden"> out of 5</span></strong><small class="sd-muted">2,104 ratings</small></p></li>
  <li class="list-group-item d-flex flex-wrap align-items-center justify-content-between gap-2 column-gap-4"><div class="text-break"><b class="d-block">Harbour Lights at Dusk</b><span class="d-block mt-1 small sd-muted">Priya Raman</span></div>
    <p class="sd-score d-flex align-items-center gap-2 m-0"><span class="sd-stars" style="--r:3.6" aria-hidden="true"></span><strong><span class="visually-hidden">Rated </span>3.6<span class="visually-hidden"> out of 5</span></strong><small class="sd-muted">868 ratings</small></p></li>
  <li class="list-group-item d-flex flex-wrap align-items-center justify-content-between gap-2 column-gap-4"><div class="text-break"><b class="d-block">Northbound, Mostly</b><span class="d-block mt-1 small sd-muted">Callum Avery</span></div>
    <p class="sd-score d-flex align-items-center gap-2 m-0"><span class="sd-stars" style="--r:2.5" aria-hidden="true"></span><strong><span class="visually-hidden">Rated </span>2.5<span class="visually-hidden"> out of 5</span></strong><small class="sd-muted">157 ratings</small></p></li>
</ul></div>

<div class="sd"><span class="lbl">Inline SVG stars rounded to the half</span>
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
  <div class="col"><article class="card sd-card h-100"><div class="card-body"><h3 class="h6 fw-bold m-0">Juniper Lodge</h3><p class="small sd-muted mt-1 mb-3">Cabins · Lake Varna</p>
    <div class="sd-svg sd-score d-flex align-items-center gap-2"><span class="d-inline-flex gap-1" aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="half"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="visually-hidden">Rated </span>4.5<span class="visually-hidden"> out of 5</span></b></div></div></article></div>
  <div class="col"><article class="card sd-card h-100"><div class="card-body"><h3 class="h6 fw-bold m-0">The Ferryman Inn</h3><p class="small sd-muted mt-1 mb-3">Guesthouse · Old Quay</p>
    <div class="sd-svg sd-score d-flex align-items-center gap-2"><span class="d-inline-flex gap-1" aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="half"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="visually-hidden">Rated </span>3.5<span class="visually-hidden"> out of 5</span></b></div></div></article></div>
  <div class="col"><article class="card sd-card h-100"><div class="card-body"><h3 class="h6 fw-bold m-0">Cedar & Stone</h3><p class="small sd-muted mt-1 mb-3">Boutique hotel · Market Row</p>
    <div class="sd-svg sd-score d-flex align-items-center gap-2"><span class="d-inline-flex gap-1" aria-hidden="true">
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="none"><use class="bg" href="#sd-star-shape"/><use class="fg" href="#sd-star-shape"/></svg>
    </span><b><span class="visually-hidden">Rated </span>2<span class="visually-hidden"> out of 5</span></b></div></div></article></div>
</div></div>

<div class="sd"><span class="lbl">Outlined stars clipped to the score</span>
<div class="card sd-card sd-hero"><div class="card-body d-flex flex-wrap align-items-center row-gap-3 column-gap-4">
  <p class="sd-big m-0"><span class="visually-hidden">Rated </span>4.3<small aria-hidden="true"> / 5</small><span class="visually-hidden"> out of 5</span></p>
  <div><span class="sd-line" style="--r:4.3" aria-hidden="true"></span>
    <p class="mt-2 mb-0 sd-muted"><b>Very good</b> · from 1,204 guest ratings</p></div>
</div></div></div>
CSS
/* Read-only star ratings on Bootstrap: .list-group, .card and .row-cols-*, with the value as text and the stars aria-hidden.
   .sd-stars: a gradient filled to --r (0–5) under a repeating star mask. .sd-svg: inline SVG stars clipped to full, half or none.
   .sd-line: an outline row over a solid row clipped with clip-path: inset() to --r. */
.sd{--sd-on:#f59e0b;--sd-off:#ddd7ce;--sd-ink:#1f2328;--sd-muted:#5b616e;--sd-line:#e8e3da;--sd-edge:#b45309;
  --sd-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");
  --sd-outline:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.4' stroke-linejoin='round' 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-border-color:var(--sd-line);color:var(--sd-ink)}
.sd-stars,.sd-line{--r:0;--fill:calc(var(--r) * 20%)}
/* Where round() and mod() exist, map the fraction onto the star's body so 4.3 fills 30% of the fifth star. */
@supports (width:round(down,4.3px,1px)) and (width:mod(4.3px,1px)){
  .sd-stars,.sd-line{--fill:calc((round(down,var(--r),1) + .12 + mod(var(--r),1) * .76) * 20%)}}
.sd-stars{--s:20px;flex:none;display:inline-block;width:calc(var(--s) * 5);height:var(--s);
  background:linear-gradient(90deg,var(--sd-on) var(--fill),var(--sd-off) 0);
  -webkit-mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x}
.sd .list-group{--bs-list-group-border-color:var(--sd-line);--bs-list-group-border-radius:18px;--bs-list-group-item-padding-x:22px;--bs-list-group-item-padding-y:16px;--bs-list-group-color:var(--sd-ink)}
.sd-muted{color:var(--sd-muted)}
.sd-score{font-size:.92rem;font-variant-numeric:tabular-nums}
.sd-score strong{min-width:2.2ch;font-weight:800}
.sd-card{--bs-card-border-color:var(--sd-line);--bs-card-border-radius:18px;--bs-card-spacer-x:22px;--bs-card-spacer-y:20px;--bs-card-color:var(--sd-ink)}
.sd-svg svg{width:22px;height:22px;display:block}
.sd-svg .bg{fill:var(--sd-off)}
.sd-svg .fg{fill:var(--sd-on)}
.sd-svg .half .fg{clip-path:inset(0 50% 0 0)}
.sd-svg .none .fg{display:none}
.sd-hero{--bs-card-border-radius:22px;--bs-card-spacer-x:28px;--bs-card-spacer-y:26px}
.sd-big{font-size:3.6rem;font-weight:800;line-height:1;letter-spacing:-.04em}
.sd-hero b{color:var(--sd-ink)}
.sd-big small{font-size:1.1rem;font-weight:600;letter-spacing:0;color:var(--sd-muted)}
.sd-line{--s:30px;position:relative;display:block;width:calc(var(--s) * 5);height:var(--s)}
.sd-line::before,.sd-line::after{content:"";position:absolute;inset:0;
  -webkit-mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;mask:var(--sd-star) 0 0/var(--s) var(--s) repeat-x;background:var(--sd-on);
  clip-path:inset(0 calc(100% - var(--fill)) 0 0)}
.sd-line::after{background:var(--sd-edge);-webkit-mask-image:var(--sd-outline);mask-image:var(--sd-outline);clip-path:none}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<svg width="0" height="0" class="absolute" aria-hidden="true" focusable="false"><symbol id="sd-star-shape" 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-[#5d6573] uppercase">Fractional stars with a CSS mask</span>
<ul class="divide-y divide-sd-line rounded-[18px] border border-sd-line bg-white" aria-label="Reader ratings">
  <li class="flex flex-wrap items-center justify-between gap-x-5 gap-y-2 px-[22px] py-4"><div class="min-w-0"><b class="block">A Field Guide to Small Rivers</b><span class="mt-0.5 block text-[.86rem] text-sd-muted">Maren Okafor</span></div>
    <p class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="stars" style="--r:4.8" aria-hidden="true"></span><strong class="min-w-[2.2ch] font-extrabold"><span class="sr-only">Rated </span>4.8<span class="sr-only"> out of 5</span></strong><small class="text-[.84rem] text-sd-muted">3,412 ratings</small></p></li>
  <li class="flex flex-wrap items-center justify-between gap-x-5 gap-y-2 px-[22px] py-4"><div class="min-w-0"><b class="block">The Salt Orchard</b><span class="mt-0.5 block text-[.86rem] text-sd-muted">Teodor Lindqvist</span></div>
    <p class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="stars" style="--r:4.3" aria-hidden="true"></span><strong class="min-w-[2.2ch] font-extrabold"><span class="sr-only">Rated </span>4.3<span class="sr-only"> out of 5</span></strong><small class="text-[.84rem] text-sd-muted">2,104 ratings</small></p></li>
  <li class="flex flex-wrap items-center justify-between gap-x-5 gap-y-2 px-[22px] py-4"><div class="min-w-0"><b class="block">Harbour Lights at Dusk</b><span class="mt-0.5 block text-[.86rem] text-sd-muted">Priya Raman</span></div>
    <p class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="stars" style="--r:3.6" aria-hidden="true"></span><strong class="min-w-[2.2ch] font-extrabold"><span class="sr-only">Rated </span>3.6<span class="sr-only"> out of 5</span></strong><small class="text-[.84rem] text-sd-muted">868 ratings</small></p></li>
  <li class="flex flex-wrap items-center justify-between gap-x-5 gap-y-2 px-[22px] py-4"><div class="min-w-0"><b class="block">Northbound, Mostly</b><span class="mt-0.5 block text-[.86rem] text-sd-muted">Callum Avery</span></div>
    <p class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="stars" style="--r:2.5" aria-hidden="true"></span><strong class="min-w-[2.2ch] font-extrabold"><span class="sr-only">Rated </span>2.5<span class="sr-only"> out of 5</span></strong><small class="text-[.84rem] text-sd-muted">157 ratings</small></p></li>
</ul></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5d6573] uppercase">Inline SVG stars rounded to the half</span>
<div class="grid grid-cols-[repeat(auto-fit,minmax(210px,1fr))] gap-4">
  <article class="rounded-[18px] border border-sd-line bg-white px-[22px] py-5"><h3 class="font-bold">Juniper Lodge</h3><p class="mt-1 mb-3.5 text-[.86rem] text-sd-muted">Cabins · Lake Varna</p>
    <div class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="inline-flex gap-0.5" aria-hidden="true">
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on [clip-path:inset(0_50%_0_0)]" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>4.5<span class="sr-only"> out of 5</span></b></div></article>
  <article class="rounded-[18px] border border-sd-line bg-white px-[22px] py-5"><h3 class="font-bold">The Ferryman Inn</h3><p class="mt-1 mb-3.5 text-[.86rem] text-sd-muted">Guesthouse · Old Quay</p>
    <div class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="inline-flex gap-0.5" aria-hidden="true">
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on [clip-path:inset(0_50%_0_0)]" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>3.5<span class="sr-only"> out of 5</span></b></div></article>
  <article class="rounded-[18px] border border-sd-line bg-white px-[22px] py-5"><h3 class="font-bold">Cedar & Stone</h3><p class="mt-1 mb-3.5 text-[.86rem] text-sd-muted">Boutique hotel · Market Row</p>
    <div class="flex items-center gap-2.5 text-[.92rem] tabular-nums"><span class="inline-flex gap-0.5" aria-hidden="true">
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/><use class="fill-sd-on" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/></svg>
      <svg viewBox="0 0 20 20" class="size-[22px]"><use class="fill-sd-off" href="#sd-star-shape"/></svg>
    </span><b><span class="sr-only">Rated </span>2<span class="sr-only"> out of 5</span></b></div></article>
</div></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5d6573] uppercase">Outlined stars clipped to the score</span>
<div class="flex flex-wrap items-center gap-x-7 gap-y-[18px] rounded-[22px] border border-sd-line bg-white px-7 py-[26px]">
  <p class="text-[3.6rem] leading-none font-extrabold tracking-[-.04em]"><span class="sr-only">Rated </span>4.3<small class="text-[1.1rem] font-semibold tracking-normal text-sd-muted" aria-hidden="true"> / 5</small><span class="sr-only"> out of 5</span></p>
  <div><span class="stars-outline" style="--r:4.3" aria-hidden="true"></span>
    <p class="mt-2.5 text-[.95rem] text-sd-muted"><b class="text-sd-ink">Very good</b> · from 1,204 guest ratings</p></div>
</div></div>
CSS
@theme {
  --color-sd-on: #f59e0b;
  --color-sd-off: #ddd7ce;
  --color-sd-ink: #1f2328;
  --color-sd-muted: #5b616e;
  --color-sd-line: #e8e3da;
  --color-sd-edge: #b45309;
}
/* Read-only stars: a gradient filled to --r (0–5) under a repeating star mask. Set --s for the size.
   Where round() and mod() exist, the fraction is mapped onto the star's body so 4.3 fills 30% of the fifth star. */
@utility stars {
  --r: 0;
  --s: 20px;
  --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-sd-on) var(--fill), var(--color-sd-off) 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%);
  }
}
/* Outlined variant: the solid row is clipped to --r and an outline row sits on top. */
@utility stars-outline {
  --r: 0;
  --s: 30px;
  --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");
  --outline: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.4' stroke-linejoin='round' 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");
  position: relative;
  display: block;
  width: calc(var(--s) * 5);
  height: var(--s);
  @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%);
  }
  &::before, &::after {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--color-sd-on);
    -webkit-mask: var(--star) 0 0 / var(--s) var(--s) repeat-x;
    mask: var(--star) 0 0 / var(--s) var(--s) repeat-x;
    clip-path: inset(0 calc(100% - var(--fill)) 0 0);
  }
  &::after {
    background: var(--color-sd-edge);
    -webkit-mask-image: var(--outline);
    mask-image: var(--outline);
    clip-path: none;
  }
}

About this set

Three ways to show a score that people can read but not change, each with fractional or half stars. Fractional stars with a CSS mask is a list of books rated 4.8, 4.3, 3.6 and 2.5, where one element draws all five stars: a gradient filled to the –r value sits under a repeating star mask, and where the browser supports round() and mod() the fraction is mapped onto the star’s body, so 4.3 fills about a third of the fifth star. Inline SVG stars rounded to the half is a row of hotel cards that use five SVG stars from one symbol, each a grey star with a gold copy on top that is shown whole, clipped to its left half or hidden. Outlined stars clipped to the score is a large 4.3 out of 5 with outline stars and a solid row clipped with clip-path: inset() to the value. Use the mask version when the score comes from data, the SVG version when you round on the server, and the outline version for a headline score. In every variant the score is text and the stars are hidden from screen readers.

What’s included

  • Fractional stars from one masked element and a --r value
  • Half stars from inline SVG with clip-path
  • Outline stars with a solid row clipped to the score
  • Score always written as text, such as Rated 4.3 out of 5
  • No JavaScript and no icon font

Accessibility

Every score is real text that reads as Rated 4.3 out of 5, with the words Rated and out of 5 in visually hidden spans, so the visible number stays short. The star graphics are aria-hidden, so screen readers do not announce five images. Nothing in the set is interactive or animated.

Customise it

Change --sd-on, --sd-off and --sd-edge on .sd for the star colours and set --s on each element for the size. In Bootstrap the same variables apply; in Tailwind edit the sd colours in @theme and pass --r and --s in the style attribute.