Open demo

CSS code

HTML
<div><span class="lbl">Strike-through</span>
<section class="cl" aria-labelledby="bag-title">
  <h2 id="bag-title">Carry-on bag</h2>
  <p class="cl-sub">Flight to Lisbon, Thursday 07:40</p>
  <ul class="cl-list">
    <li><label class="cl-item"><input class="cl-box" type="checkbox" checked><span class="cl-text">Passport and boarding pass</span></label></li>
    <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Phone charger and travel adapter</span></label></li>
    <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Noise-cancelling headphones</span></label></li>
    <li><label class="cl-item"><input class="cl-box" type="checkbox" checked><span class="cl-text">Refillable water bottle, empty</span></label></li>
    <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">A paperback for the flight</span></label></li>
  </ul>
</section></div>

<div><span class="lbl">Progress bar</span>
<section class="cl" data-checklist aria-labelledby="week-title">
  <div class="cl-top"><div><h2 id="week-title">Your first week at Fieldnote</h2><p class="cl-sub">Tick things off as you go. Your manager sees the same list.</p></div><p class="cl-count" data-count>3 of 6 done</p></div>
  <div class="cl-bar" data-bar role="progressbar" aria-label="First week progress" aria-valuemin="0" aria-valuemax="6" aria-valuenow="3" aria-valuetext="3 of 6 done" style="--done:50%"><i></i></div>
  <ul class="cl-rows">
    <li><label class="cl-row"><input class="cl-box" type="checkbox" checked><span class="cl-copy"><span class="cl-name" data-name>Sign the employment contract</span><span class="cl-note">Sent to your personal email on Friday</span></span></label></li>
    <li><label class="cl-row"><input class="cl-box" type="checkbox" checked><span class="cl-copy"><span class="cl-name" data-name>Set up your laptop and password manager</span><span class="cl-note">IT brings it to your desk at 9:00</span></span></label></li>
    <li><label class="cl-row"><input class="cl-box" type="checkbox" checked><span class="cl-copy"><span class="cl-name" data-name>Meet your onboarding buddy, Priya</span><span class="cl-note">Coffee on Tuesday at 10:30</span></span></label></li>
    <li><label class="cl-row"><input class="cl-box" type="checkbox"><span class="cl-copy"><span class="cl-name" data-name>Read the product handbook</span><span class="cl-note">About 40 minutes, with a short quiz at the end</span></span></label></li>
    <li><label class="cl-row"><input class="cl-box" type="checkbox"><span class="cl-copy"><span class="cl-name" data-name>Ship a one-line fix to the docs</span><span class="cl-note">Your buddy will pair with you on it</span></span></label></li>
    <li><label class="cl-row"><input class="cl-box" type="checkbox"><span class="cl-copy"><span class="cl-name" data-name>Book a 1:1 with your manager</span><span class="cl-note">Any free slot on Friday afternoon</span></span></label></li>
  </ul>
  <p class="sr-only" role="status" data-live></p>
</section></div>

<div><span class="lbl">Grouped sections</span>
<section class="cl" data-checklist aria-labelledby="move-title">
  <div class="cl-top"><div><h2 id="move-title">Moving to 14 Elm Street</h2><p class="cl-sub">Keys handed over on 28 March</p></div><p class="cl-count" data-count>3 of 9 done</p></div>
  <div class="cl-bar slim" data-bar role="progressbar" aria-label="Moving checklist progress" aria-valuemin="0" aria-valuemax="9" aria-valuenow="3" aria-valuetext="3 of 9 done" style="--done:33.3333%"><i></i></div>
  <section class="cl-group" data-group aria-labelledby="move-before">
    <div class="cl-group-head"><h3 id="move-before">Two weeks before</h3><p class="cl-pill" data-count>2 of 3 done</p></div>
    <ul class="cl-list">
      <li><label class="cl-item"><input class="cl-box" type="checkbox" checked><span class="cl-text">Book the removal van</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox" checked><span class="cl-text">Give notice to the broadband provider</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Order boxes and parcel tape</span></label></li>
    </ul>
  </section>
  <section class="cl-group" data-group aria-labelledby="move-week">
    <div class="cl-group-head"><h3 id="move-week">Moving week</h3><p class="cl-pill" data-count>1 of 3 done</p></div>
    <ul class="cl-list">
      <li><label class="cl-item"><input class="cl-box" type="checkbox" checked><span class="cl-text">Defrost and clean the freezer</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Pack a first-night box</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Hand back the old keys</span></label></li>
    </ul>
  </section>
  <section class="cl-group" data-group aria-labelledby="move-after">
    <div class="cl-group-head"><h3 id="move-after">After the move</h3><p class="cl-pill" data-count>0 of 3 done</p></div>
    <ul class="cl-list">
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Register with a local GP</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Update the address on your driving licence</span></label></li>
      <li><label class="cl-item"><input class="cl-box" type="checkbox"><span class="cl-text">Redirect post for three months</span></label></li>
    </ul>
  </section>
  <p class="sr-only" role="status" data-live></p>
</section></div>
CSS
/* Checklists: real checkboxes inside their labels. The strike line is a background on the inline text that grows
   to 100% when the box is ticked; the progress bar and the "3 of 6 done" counts are kept up to date by the script. */
.cl{--cl-ink:#10261a;--cl-muted:#4b6155;--cl-line:#dbe7df;--cl-box:#6b8576;--cl-accent:#15803d;--cl-accent-dark:#166534;--cl-tint:#e3f1e7;--cl-track:#dfe9e2;
  background:#fff;color:var(--cl-ink);border:1px solid var(--cl-line);border-radius:18px;padding:24px 26px;box-shadow:0 18px 40px -28px rgba(16,38,26,.35)}
.cl :where(h2,h3,p,ul){margin:0;padding:0}
.cl ul{list-style:none}
.cl h2{font-size:1.2rem;font-weight:800;letter-spacing:-.015em;line-height:1.3}
.cl-sub{margin-top:4px!important;font-size:.88rem;color:var(--cl-muted)}
.cl-box{appearance:none;width:22px;height:22px;margin:0;border:2px solid var(--cl-box);border-radius:6px;background:#fff center/14px no-repeat;cursor:pointer;
  transition:background-color .15s,border-color .15s}
.cl-box:checked{background-color:var(--cl-accent);border-color:var(--cl-accent);background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E")}
.cl-box:focus-visible{outline:3px solid var(--cl-accent);outline-offset:2px}
/* strike-through list */
.cl-item{display:block;position:relative;padding:11px 0 11px 36px;font-size:1rem;line-height:1.45;cursor:pointer}
.cl-item .cl-box{position:absolute;left:0;top:12px}
.cl-text{background:linear-gradient(var(--cl-accent),var(--cl-accent)) no-repeat 0 60%/0 3px;transition:background-size .35s ease,color .2s}
.cl-box:checked+.cl-text{background-size:100% 3px;color:var(--cl-muted)}
.cl-list li+li{border-top:1px solid var(--cl-line)}
.cl-list{margin-top:10px!important}
/* progress bar */
.cl-top{display:flex;align-items:flex-end;justify-content:space-between;gap:6px 16px;flex-wrap:wrap}
.cl-count{font-size:.9rem;font-weight:700;color:var(--cl-accent-dark)}
.cl-bar{--done:0%;height:10px;margin-top:14px;border-radius:999px;background:var(--cl-track);overflow:hidden}
.cl-bar i{display:block;height:100%;width:var(--done);border-radius:inherit;background:var(--cl-accent);transition:width .4s ease}
.cl-bar.slim{height:6px;margin-top:12px}
.cl-rows{margin-top:12px!important}
.cl-row{display:flex;align-items:flex-start;gap:14px;padding:13px 12px;margin:0 -12px;border-radius:12px;cursor:pointer;transition:background-color .15s}
.cl-row:hover{background:#f4f9f5}
.cl-row .cl-box{flex:none;margin-top:1px}
.cl-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
.cl-name{font-size:1rem;font-weight:600;line-height:1.4;transition:color .2s}
.cl-note{font-size:.86rem;line-height:1.4;color:var(--cl-muted)}
.cl-box:checked+.cl-copy .cl-name{color:var(--cl-muted);text-decoration:line-through;text-decoration-thickness:1.5px}
/* grouped sections */
.cl-group{margin-top:22px}
.cl-group-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-bottom:6px;border-bottom:2px solid var(--cl-ink)}
.cl-group h3{font-size:.8rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase}
.cl-pill{font-size:.76rem;font-weight:700;padding:3px 9px;border-radius:999px;background:var(--cl-tint);color:var(--cl-accent-dark);white-space:nowrap}
.cl-pill[data-complete]{background:var(--cl-accent);color:#fff}
.cl-group .cl-list{margin-top:2px!important}
.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}
@media (max-width:480px){.cl{padding:20px 18px}}
@media (prefers-reduced-motion:reduce){.cl *{transition:none!important}}
JavaScript
/* Checklist progress: count the ticked boxes in each [data-checklist] and in each [data-group] inside it,
   write "3 of 6 done" into [data-count], fill the [data-bar] progress bar and announce the change politely. */
document.querySelectorAll('[data-checklist]').forEach((root) => {
  const live = root.querySelector('[data-live]');
  const tally = (scope) => {
    const boxes = [...scope.querySelectorAll('input[type=checkbox]')];
    return [boxes.filter((b) => b.checked).length, boxes.length];
  };
  const paint = () => {
    root.querySelectorAll('[data-count]').forEach((el) => {
      const [done, total] = tally(el.closest('[data-group]') || root);
      el.textContent = done === total ? 'All ' + total + ' done' : done + ' of ' + total + ' done';
      el.toggleAttribute('data-complete', done === total);
    });
    root.querySelectorAll('[data-bar]').forEach((bar) => {
      const [done, total] = tally(root);
      bar.setAttribute('aria-valuemax', total);
      bar.setAttribute('aria-valuenow', done);
      bar.setAttribute('aria-valuetext', done + ' of ' + total + ' done');
      bar.style.setProperty('--done', (done / total * 100) + '%');
    });
  };
  root.addEventListener('change', (e) => {
    if (e.target.type !== 'checkbox') return;
    paint();
    const [done, total] = tally(root);
    const name = e.target.closest('label').querySelector('[data-name]') || e.target.closest('label');
    live.textContent = '“' + name.textContent.trim() + '” ' + (e.target.checked ? 'ticked' : 'unticked') + '. ' + done + ' of ' + total + ' done.';
  });
  paint();
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Strike-through</span>
<section class="card cl" aria-labelledby="bag-title"><div class="card-body">
  <h2 id="bag-title">Carry-on bag</h2>
  <p class="cl-sub">Flight to Lisbon, Thursday 07:40</p>
  <ul class="list-group list-group-flush cl-list">
    <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox" checked><span class="cl-text">Passport and boarding pass</span></label></li>
    <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Phone charger and travel adapter</span></label></li>
    <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Noise-cancelling headphones</span></label></li>
    <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox" checked><span class="cl-text">Refillable water bottle, empty</span></label></li>
    <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">A paperback for the flight</span></label></li>
  </ul>
</div></section></div>

<div><span class="lbl">Progress bar</span>
<section class="card cl" data-checklist aria-labelledby="week-title"><div class="card-body">
  <div class="d-flex flex-wrap align-items-end justify-content-between column-gap-3 row-gap-1"><div><h2 id="week-title">Your first week at Fieldnote</h2><p class="cl-sub">Tick things off as you go. Your manager sees the same list.</p></div><p class="cl-count" data-count>3 of 6 done</p></div>
  <div class="progress" data-bar role="progressbar" aria-label="First week progress" aria-valuemin="0" aria-valuemax="6" aria-valuenow="3" aria-valuetext="3 of 6 done" style="--done:50%"><div class="progress-bar"></div></div>
  <ul class="cl-rows">
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox" checked><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Sign the employment contract</span><span class="cl-note">Sent to your personal email on Friday</span></span></label></li>
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox" checked><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Set up your laptop and password manager</span><span class="cl-note">IT brings it to your desk at 9:00</span></span></label></li>
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox" checked><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Meet your onboarding buddy, Priya</span><span class="cl-note">Coffee on Tuesday at 10:30</span></span></label></li>
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox"><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Read the product handbook</span><span class="cl-note">About 40 minutes, with a short quiz at the end</span></span></label></li>
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox"><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Ship a one-line fix to the docs</span><span class="cl-note">Your buddy will pair with you on it</span></span></label></li>
    <li><label class="cl-row d-flex align-items-start gap-3"><input class="form-check-input flex-shrink-0" type="checkbox"><span class="cl-copy d-flex flex-column"><span class="cl-name" data-name>Book a 1:1 with your manager</span><span class="cl-note">Any free slot on Friday afternoon</span></span></label></li>
  </ul>
  <p class="visually-hidden" role="status" data-live></p>
</div></section></div>

<div><span class="lbl">Grouped sections</span>
<section class="card cl" data-checklist aria-labelledby="move-title"><div class="card-body">
  <div class="d-flex flex-wrap align-items-end justify-content-between column-gap-3 row-gap-1"><div><h2 id="move-title">Moving to 14 Elm Street</h2><p class="cl-sub">Keys handed over on 28 March</p></div><p class="cl-count" data-count>3 of 9 done</p></div>
  <div class="progress slim" data-bar role="progressbar" aria-label="Moving checklist progress" aria-valuemin="0" aria-valuemax="9" aria-valuenow="3" aria-valuetext="3 of 9 done" style="--done:33.3333%"><div class="progress-bar"></div></div>
  <section class="cl-group" data-group aria-labelledby="move-before">
    <div class="cl-group-head d-flex align-items-center justify-content-between gap-2"><h3 class="mb-0" id="move-before">Two weeks before</h3><p class="badge rounded-pill cl-pill mb-0" data-count>2 of 3 done</p></div>
    <ul class="list-group list-group-flush cl-list">
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox" checked><span class="cl-text">Book the removal van</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox" checked><span class="cl-text">Give notice to the broadband provider</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Order boxes and parcel tape</span></label></li>
    </ul>
  </section>
  <section class="cl-group" data-group aria-labelledby="move-week">
    <div class="cl-group-head d-flex align-items-center justify-content-between gap-2"><h3 class="mb-0" id="move-week">Moving week</h3><p class="badge rounded-pill cl-pill mb-0" data-count>1 of 3 done</p></div>
    <ul class="list-group list-group-flush cl-list">
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox" checked><span class="cl-text">Defrost and clean the freezer</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Pack a first-night box</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Hand back the old keys</span></label></li>
    </ul>
  </section>
  <section class="cl-group" data-group aria-labelledby="move-after">
    <div class="cl-group-head d-flex align-items-center justify-content-between gap-2"><h3 class="mb-0" id="move-after">After the move</h3><p class="badge rounded-pill cl-pill mb-0" data-count>0 of 3 done</p></div>
    <ul class="list-group list-group-flush cl-list">
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Register with a local GP</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Update the address on your driving licence</span></label></li>
      <li class="list-group-item"><label class="form-check cl-item mb-0"><input class="form-check-input" type="checkbox"><span class="cl-text">Redirect post for three months</span></label></li>
    </ul>
  </section>
  <p class="visually-hidden" role="status" data-live></p>
</div></section></div>
CSS
/* Checklists on Bootstrap: .card, .list-group, .form-check, .form-check-input, .progress and .badge, restyled through
   Bootstrap's CSS variables. The strike line is a background on the inline text; the script keeps counts and the bar current. */
.cl{--cl-ink:#10261a;--cl-muted:#4b6155;--cl-line:#dbe7df;--cl-box:#6b8576;--cl-accent:#15803d;--cl-accent-dark:#166534;--cl-tint:#e3f1e7;--cl-track:#dfe9e2;
  --bs-card-border-color:var(--cl-line);--bs-card-border-radius:18px;--bs-card-spacer-y:24px;--bs-card-spacer-x:26px;--bs-card-color:var(--cl-ink);
  box-shadow:0 18px 40px -28px rgba(16,38,26,.35)}
.cl h2{font-size:1.2rem;font-weight:800;letter-spacing:-.015em;line-height:1.3;margin:0}
.cl-sub{margin:4px 0 0;font-size:.88rem;color:var(--cl-muted)}
.cl .form-check-input{width:22px;height:22px;margin:0;border:2px solid var(--cl-box);border-radius:6px;background-size:14px;cursor:pointer;transition:background-color .15s,border-color .15s}
.cl .form-check-input:checked{background-color:var(--cl-accent);border-color:var(--cl-accent)}
.cl .form-check-input:focus{box-shadow:none;border-color:var(--cl-box)}
.cl .form-check-input:checked:focus{border-color:var(--cl-accent)}
.cl .form-check-input:focus-visible{outline:3px solid var(--cl-accent);outline-offset:2px}
.cl .list-group{--bs-list-group-border-color:var(--cl-line);--bs-list-group-item-padding-x:0;--bs-list-group-item-padding-y:0;--bs-list-group-color:var(--cl-ink)}
.cl .list-group-flush>.list-group-item:last-child{border-bottom-width:0}
/* strike-through list: .form-check already floats the box into a left gutter */
.cl-item{display:block;padding:11px 0 11px 36px;font-size:1rem;line-height:1.45;cursor:pointer;min-height:0}
.cl-item .form-check-input{float:left;margin:1px 0 0 -36px}
.cl-text{background:linear-gradient(var(--cl-accent),var(--cl-accent)) no-repeat 0 60%/0 3px;transition:background-size .35s ease,color .2s}
.cl .form-check-input:checked+.cl-text{background-size:100% 3px;color:var(--cl-muted)}
.cl-list{margin-top:10px}
/* progress bar */
.cl-count{margin:0;font-size:.9rem;font-weight:700;color:var(--cl-accent-dark)}
.cl .progress{--bs-progress-height:10px;--bs-progress-bg:var(--cl-track);--bs-progress-bar-bg:var(--cl-accent);--bs-progress-border-radius:999px;--done:0%;margin-top:14px}
.cl .progress.slim{--bs-progress-height:6px;margin-top:12px}
.cl .progress-bar{width:var(--done);border-radius:inherit;transition:width .4s ease}
.cl-rows{margin:12px 0 0;padding:0;list-style:none}
.cl-row{padding:13px 12px;margin:0 -12px;border-radius:12px;cursor:pointer;transition:background-color .15s}
.cl-row:hover{background:#f4f9f5}
.cl-row .form-check-input{margin-top:1px}
.cl-copy{gap:3px;min-width:0}
.cl-name{font-size:1rem;font-weight:600;line-height:1.4;transition:color .2s}
.cl-note{font-size:.86rem;line-height:1.4;color:var(--cl-muted)}
.cl .form-check-input:checked+.cl-copy .cl-name{color:var(--cl-muted);text-decoration:line-through;text-decoration-thickness:1.5px}
/* grouped sections */
.cl-group{margin-top:22px}
.cl-group-head{padding-bottom:6px;border-bottom:2px solid var(--cl-ink)}
.cl-group h3{font-size:.8rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;line-height:normal}
.cl-pill{--bs-badge-font-size:.76rem;--bs-badge-font-weight:700;--bs-badge-padding-x:9px;--bs-badge-padding-y:3px;--bs-badge-color:var(--cl-accent-dark);background:var(--cl-tint);line-height:normal}
.cl-pill[data-complete]{--bs-badge-color:#fff;background:var(--cl-accent)}
.cl-group .cl-list{margin-top:2px}
@media (max-width:480px){.cl{--bs-card-spacer-y:20px;--bs-card-spacer-x:18px}}
@media (prefers-reduced-motion:reduce){.cl *{transition:none!important}}
JavaScript
/* Checklist progress: count the ticked boxes in each [data-checklist] and in each [data-group] inside it,
   write "3 of 6 done" into [data-count], fill the [data-bar] progress bar and announce the change politely. */
document.querySelectorAll('[data-checklist]').forEach((root) => {
  const live = root.querySelector('[data-live]');
  const tally = (scope) => {
    const boxes = [...scope.querySelectorAll('input[type=checkbox]')];
    return [boxes.filter((b) => b.checked).length, boxes.length];
  };
  const paint = () => {
    root.querySelectorAll('[data-count]').forEach((el) => {
      const [done, total] = tally(el.closest('[data-group]') || root);
      el.textContent = done === total ? 'All ' + total + ' done' : done + ' of ' + total + ' done';
      el.toggleAttribute('data-complete', done === total);
    });
    root.querySelectorAll('[data-bar]').forEach((bar) => {
      const [done, total] = tally(root);
      bar.setAttribute('aria-valuemax', total);
      bar.setAttribute('aria-valuenow', done);
      bar.setAttribute('aria-valuetext', done + ' of ' + total + ' done');
      bar.style.setProperty('--done', (done / total * 100) + '%');
    });
  };
  root.addEventListener('change', (e) => {
    if (e.target.type !== 'checkbox') return;
    paint();
    const [done, total] = tally(root);
    const name = e.target.closest('label').querySelector('[data-name]') || e.target.closest('label');
    live.textContent = '“' + name.textContent.trim() + '” ' + (e.target.checked ? 'ticked' : 'unticked') + '. ' + done + ' of ' + total + ' done.';
  });
  paint();
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#4b6155] uppercase">Strike-through</span>
<section class="rounded-[18px] border border-[#dbe7df] bg-white px-[26px] py-6 text-[#10261a] shadow-[0_18px_40px_-28px_rgba(16,38,26,.35)] max-[480px]:px-[18px] max-[480px]:py-5" aria-labelledby="bag-title">
  <h2 class="text-[1.2rem] leading-[1.3] font-extrabold tracking-[-.015em]" id="bag-title">Carry-on bag</h2>
  <p class="mt-1 text-[.88rem] text-[#4b6155]">Flight to Lisbon, Thursday 07:40</p>
  <ul class="mt-2.5">
    <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Passport and boarding pass</span></label></li>
    <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Phone charger and travel adapter</span></label></li>
    <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Noise-cancelling headphones</span></label></li>
    <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Refillable water bottle, empty</span></label></li>
    <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">A paperback for the flight</span></label></li>
  </ul>
</section></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#4b6155] uppercase">Progress bar</span>
<section class="rounded-[18px] border border-[#dbe7df] bg-white px-[26px] py-6 text-[#10261a] shadow-[0_18px_40px_-28px_rgba(16,38,26,.35)] max-[480px]:px-[18px] max-[480px]:py-5" data-checklist aria-labelledby="week-title">
  <div class="flex flex-wrap items-end justify-between gap-x-4 gap-y-1.5"><div><h2 class="text-[1.2rem] leading-[1.3] font-extrabold tracking-[-.015em]" id="week-title">Your first week at Fieldnote</h2><p class="mt-1 text-[.88rem] text-[#4b6155]">Tick things off as you go. Your manager sees the same list.</p></div><p class="text-[.9rem] font-bold text-[#166534]" data-count>3 of 6 done</p></div>
  <div class="mt-3.5 h-2.5 overflow-hidden rounded-full bg-[#dfe9e2]" data-bar role="progressbar" aria-label="First week progress" aria-valuemin="0" aria-valuemax="6" aria-valuenow="3" aria-valuetext="3 of 6 done" style="--done:50%"><i class="block h-full w-(--done) rounded-[inherit] bg-[#15803d] transition-[width] duration-400 ease-out motion-reduce:transition-none"></i></div>
  <ul class="mt-3">
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Sign the employment contract</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">Sent to your personal email on Friday</span></span></label></li>
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Set up your laptop and password manager</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">IT brings it to your desk at 9:00</span></span></label></li>
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Meet your onboarding buddy, Priya</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">Coffee on Tuesday at 10:30</span></span></label></li>
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Read the product handbook</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">About 40 minutes, with a short quiz at the end</span></span></label></li>
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Ship a one-line fix to the docs</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">Your buddy will pair with you on it</span></span></label></li>
    <li><label class="group/row -mx-3 flex cursor-pointer items-start gap-3.5 rounded-xl px-3 py-[13px] transition-colors duration-150 hover:bg-[#f4f9f5] motion-reduce:transition-none"><input class="peer mt-px flex-none size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="flex min-w-0 flex-col gap-[3px]"><span class="text-base leading-[1.4] font-semibold transition-colors duration-200 group-has-checked/row:text-[#4b6155] group-has-checked/row:line-through group-has-checked/row:decoration-[1.5px] motion-reduce:transition-none" data-name>Book a 1:1 with your manager</span><span class="text-[.86rem] leading-[1.4] text-[#4b6155]">Any free slot on Friday afternoon</span></span></label></li>
  </ul>
  <p class="sr-only" role="status" data-live></p>
</section></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#4b6155] uppercase">Grouped sections</span>
<section class="rounded-[18px] border border-[#dbe7df] bg-white px-[26px] py-6 text-[#10261a] shadow-[0_18px_40px_-28px_rgba(16,38,26,.35)] max-[480px]:px-[18px] max-[480px]:py-5" data-checklist aria-labelledby="move-title">
  <div class="flex flex-wrap items-end justify-between gap-x-4 gap-y-1.5"><div><h2 class="text-[1.2rem] leading-[1.3] font-extrabold tracking-[-.015em]" id="move-title">Moving to 14 Elm Street</h2><p class="mt-1 text-[.88rem] text-[#4b6155]">Keys handed over on 28 March</p></div><p class="text-[.9rem] font-bold text-[#166534]" data-count>3 of 9 done</p></div>
  <div class="mt-3 h-1.5 overflow-hidden rounded-full bg-[#dfe9e2]" data-bar role="progressbar" aria-label="Moving checklist progress" aria-valuemin="0" aria-valuemax="9" aria-valuenow="3" aria-valuetext="3 of 9 done" style="--done:33.3333%"><i class="block h-full w-(--done) rounded-[inherit] bg-[#15803d] transition-[width] duration-400 ease-out motion-reduce:transition-none"></i></div>
  <section class="mt-[22px]" data-group aria-labelledby="move-before">
    <div class="flex items-center justify-between gap-2.5 border-b-2 border-[#10261a] pb-1.5"><h3 class="text-[.8rem] font-extrabold tracking-[.1em] uppercase" id="move-before">Two weeks before</h3><p class="rounded-full bg-[#e3f1e7] px-[9px] py-[3px] text-[.76rem] font-bold whitespace-nowrap text-[#166534] data-complete:bg-[#15803d] data-complete:text-white" data-count>2 of 3 done</p></div>
    <ul class="mt-0.5">
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Book the removal van</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Give notice to the broadband provider</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Order boxes and parcel tape</span></label></li>
    </ul>
  </section>
  <section class="mt-[22px]" data-group aria-labelledby="move-week">
    <div class="flex items-center justify-between gap-2.5 border-b-2 border-[#10261a] pb-1.5"><h3 class="text-[.8rem] font-extrabold tracking-[.1em] uppercase" id="move-week">Moving week</h3><p class="rounded-full bg-[#e3f1e7] px-[9px] py-[3px] text-[.76rem] font-bold whitespace-nowrap text-[#166534] data-complete:bg-[#15803d] data-complete:text-white" data-count>1 of 3 done</p></div>
    <ul class="mt-0.5">
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox" checked><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Defrost and clean the freezer</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Pack a first-night box</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Hand back the old keys</span></label></li>
    </ul>
  </section>
  <section class="mt-[22px]" data-group aria-labelledby="move-after">
    <div class="flex items-center justify-between gap-2.5 border-b-2 border-[#10261a] pb-1.5"><h3 class="text-[.8rem] font-extrabold tracking-[.1em] uppercase" id="move-after">After the move</h3><p class="rounded-full bg-[#e3f1e7] px-[9px] py-[3px] text-[.76rem] font-bold whitespace-nowrap text-[#166534] data-complete:bg-[#15803d] data-complete:text-white" data-count>0 of 3 done</p></div>
    <ul class="mt-0.5">
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Register with a local GP</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Update the address on your driving licence</span></label></li>
      <li class="border-[#dbe7df] [&+li]:border-t"><label class="relative block cursor-pointer py-[11px] pl-9 text-base leading-[1.45]"><input class="peer absolute top-3 left-0 size-[22px] cursor-pointer appearance-none rounded-md border-2 border-[#6b8576] bg-white bg-[length:14px] bg-center bg-no-repeat transition-colors duration-150 checked:border-[#15803d] checked:bg-[#15803d] checked:bg-tick focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-[#15803d] motion-reduce:transition-none" type="checkbox"><span class="bg-[linear-gradient(#15803d,#15803d)] bg-[length:0_3px] bg-[position:0_60%] bg-no-repeat transition-[background-size,color] duration-[350ms] ease-out peer-checked:bg-[length:100%_3px] peer-checked:text-[#4b6155] motion-reduce:transition-none">Redirect post for three months</span></label></li>
    </ul>
  </section>
  <p class="sr-only" role="status" data-live></p>
</section></div>
CSS
/* the white tick drawn inside a checked box */
@utility bg-tick {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
}
JavaScript
/* Checklist progress: count the ticked boxes in each [data-checklist] and in each [data-group] inside it,
   write "3 of 6 done" into [data-count], fill the [data-bar] progress bar and announce the change politely. */
document.querySelectorAll('[data-checklist]').forEach((root) => {
  const live = root.querySelector('[data-live]');
  const tally = (scope) => {
    const boxes = [...scope.querySelectorAll('input[type=checkbox]')];
    return [boxes.filter((b) => b.checked).length, boxes.length];
  };
  const paint = () => {
    root.querySelectorAll('[data-count]').forEach((el) => {
      const [done, total] = tally(el.closest('[data-group]') || root);
      el.textContent = done === total ? 'All ' + total + ' done' : done + ' of ' + total + ' done';
      el.toggleAttribute('data-complete', done === total);
    });
    root.querySelectorAll('[data-bar]').forEach((bar) => {
      const [done, total] = tally(root);
      bar.setAttribute('aria-valuemax', total);
      bar.setAttribute('aria-valuenow', done);
      bar.setAttribute('aria-valuetext', done + ' of ' + total + ' done');
      bar.style.setProperty('--done', (done / total * 100) + '%');
    });
  };
  root.addEventListener('change', (e) => {
    if (e.target.type !== 'checkbox') return;
    paint();
    const [done, total] = tally(root);
    const name = e.target.closest('label').querySelector('[data-name]') || e.target.closest('label');
    live.textContent = '“' + name.textContent.trim() + '” ' + (e.target.checked ? 'ticked' : 'unticked') + '. ' + done + ' of ' + total + ' done.';
  });
  paint();
});

About this set

Three checklist styles for lists that are worked through rather than added to. Strike-through is a carry-on packing list where ticking an item draws a thick accent line across its text: the line is a background gradient on the inline text that grows from zero to full width, so it follows the words across a line break, and it needs no script. Progress bar is a new starter’s first week, six tasks with a short note under each, a progress bar and a 3 of 6 done count above them. Grouped sections is a house move split into Two weeks before, Moving week and After the move, each with its own count that turns into a solid All 3 done pill when the section is finished, under a slim bar for the whole move. A short vanilla script counts the ticked boxes, writes the counts, sets the bar’s aria-valuenow and aria-valuetext, and announces the new total. The counts and bars are written into the markup too, so the lists read correctly before the script runs. The Bootstrap version uses .progress, .list-group, .form-check and .badge.

What’s included

  • Animated strike-through drawn with a background gradient, no JavaScript
  • Progress bar with role="progressbar" and a written 3 of 6 done count
  • Grouped sections with a count per section and a total bar
  • Section counts switch to All 3 done when a section is finished
  • Bootstrap version built on .progress, .list-group, .form-check and .badge

Accessibility

Every item is a real checkbox inside its label. The progress bars are named and expose aria-valuenow, aria-valuemax and aria-valuetext such as 4 of 9 done, and a polite status region announces the ticked item and the new total. Checkboxes show a visible focus outline, and the strike-through and bar animations are switched off under prefers-reduced-motion.

Customise it

Change --cl-accent, --cl-accent-dark, --cl-track and --cl-box on .cl for colours. In Bootstrap the same variables feed --bs-progress-bar-bg and --bs-progress-bg; in Tailwind swap the #15803d and #166534 colour utilities.