Open demo

CSS code

HTML
<div><span class="lbl">Answer voting with an accepted answer</span>
<section class="vt" data-vt aria-labelledby="vt-a-h">
  <div class="vt-head"><h2 class="vt-h" id="vt-a-h">Why does my sourdough starter smell like nail polish remover?</h2><p class="vt-sub">3 answers · sorted by votes</p></div>
  <article class="vt-ans ok" aria-label="Answer by Rhea Kapoor">
    <div class="vt-votes" data-votes data-base="128">
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="vt-sr">Upvote answer by Rhea Kapoor</span></button>
      <p class="vt-score"><span data-score>128</span><span class="vt-sr"> votes</span></p>
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="vt-sr">Downvote answer by Rhea Kapoor</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-ok"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 5 5 9-10"/></svg>Accepted answer</p>
      <p class="vt-text">That sharp acetone smell means the yeast has run out of food. Feed it more often, or at a higher ratio such as 1:5:5, and it should smell sweet and tangy again within two or three days.</p>
      <p class="vt-by"><span class="vt-av c1" aria-hidden="true">RK</span><span><b>Rhea Kapoor</b> · answered <time datetime="2026-10-12T08:30">last week</time></span></p>
    </div>
  </article>
  <article class="vt-ans" aria-label="Answer by Owen Tran">
    <div class="vt-votes" data-votes data-base="37">
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="vt-sr">Upvote answer by Owen Tran</span></button>
      <p class="vt-score"><span data-score>37</span><span class="vt-sr"> votes</span></p>
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="vt-sr">Downvote answer by Owen Tran</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-text">Also check the temperature. Above 27 °C my starter peaks in four hours and turns sour long before the next feed.</p>
      <p class="vt-by"><span class="vt-av c2" aria-hidden="true">OT</span><span><b>Owen Tran</b> · answered <time datetime="2026-10-14T19:05">5 days ago</time></span></p>
    </div>
  </article>
  <article class="vt-ans" aria-label="Answer by Bea Lund">
    <div class="vt-votes" data-votes data-base="-2">
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="vt-sr">Upvote answer by Bea Lund</span></button>
      <p class="vt-score"><span data-score>-2</span><span class="vt-sr"> votes</span></p>
      <button class="vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="vt-sr">Downvote answer by Bea Lund</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-text">Mine did this and I threw it out. Starting over was quicker.</p>
      <p class="vt-by"><span class="vt-av c4" aria-hidden="true">BL</span><span><b>Bea Lund</b> · answered <time datetime="2026-10-18T10:40">yesterday</time></span></p>
    </div>
  </article>
  <p class="vt-sr" role="status" data-announce></p>
</section></div>

<div><span class="lbl">Emoji reactions with a picker</span>
<section class="vt" data-vt aria-label="Comments with reactions">
  <ol class="vt-list">
    <li class="vt-item"><span class="vt-av lg c5" aria-hidden="true">NA</span><div class="vt-main">
        <p class="vt-who"><b>Noor Aziz</b> · <time datetime="2026-10-19T09:14">3 hours ago</time></p>
        <p class="vt-text">The new dark map style is so much easier on the eyes at night. Any chance it can follow the system setting?</p>
        <div class="vt-reacts" data-reactions>
          <button class="vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="vt-emo" aria-hidden="true">👍</span><span class="vt-sr">Thumbs up</span><span data-n>12</span></button><button class="vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="Heart" data-emoji="❤️"><span class="vt-emo" aria-hidden="true">❤️</span><span class="vt-sr">Heart</span><span data-n>4</span></button><button class="vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Eyes" data-emoji="👀"><span class="vt-emo" aria-hidden="true">👀</span><span class="vt-sr">Eyes</span><span data-n>2</span></button><button class="vt-add" type="button" aria-expanded="false" aria-controls="vt-pick-1" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="vt-sr">Add reaction</span></button>
          <div class="vt-picker" id="vt-pick-1" role="group" aria-label="Reactions" hidden data-picker><button class="vt-pick" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="vt-sr">Thumbs up</span></button><button class="vt-pick" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="vt-sr">Heart</span></button><button class="vt-pick" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="vt-sr">Laughing</span></button><button class="vt-pick" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="vt-sr">Party</span></button><button class="vt-pick" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="vt-sr">Thinking</span></button><button class="vt-pick" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="vt-sr">Eyes</span></button></div>
          <template><button class="vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="vt-emo" aria-hidden="true"></span><span class="vt-sr"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
    <li class="vt-item"><span class="vt-av lg c3" aria-hidden="true">JM</span><div class="vt-main">
        <p class="vt-who"><b>Jakob Meyer</b> · <time datetime="2026-10-19T10:02">2 hours ago</time></p>
        <p class="vt-text">It already does on Android 14. iOS support is in the next update.</p>
        <div class="vt-reacts" data-reactions>
          <button class="vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Party" data-emoji="🎉"><span class="vt-emo" aria-hidden="true">🎉</span><span class="vt-sr">Party</span><span data-n>7</span></button><button class="vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="vt-emo" aria-hidden="true">👍</span><span class="vt-sr">Thumbs up</span><span data-n>3</span></button><button class="vt-add" type="button" aria-expanded="false" aria-controls="vt-pick-2" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="vt-sr">Add reaction</span></button>
          <div class="vt-picker" id="vt-pick-2" role="group" aria-label="Reactions" hidden data-picker><button class="vt-pick" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="vt-sr">Thumbs up</span></button><button class="vt-pick" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="vt-sr">Heart</span></button><button class="vt-pick" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="vt-sr">Laughing</span></button><button class="vt-pick" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="vt-sr">Party</span></button><button class="vt-pick" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="vt-sr">Thinking</span></button><button class="vt-pick" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="vt-sr">Eyes</span></button></div>
          <template><button class="vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="vt-emo" aria-hidden="true"></span><span class="vt-sr"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
  </ol>
  <p class="vt-sr" role="status" data-announce></p>
</section></div>

<div><span class="lbl">Compact like and dislike row</span>
<section class="vt" data-vt aria-label="Video comments">
  <ol class="vt-list">
    <li class="vt-item"><span class="vt-av lg c2" aria-hidden="true">SO</span><div class="vt-main">
        <p class="vt-who"><b>Sam Osei</b> · <time datetime="2026-10-18T21:40">14 hours ago</time></p>
        <p class="vt-text">The bit at 4:12 where you plane the edge by hand finally made it click for me.</p>
        <div class="vt-row"><button class="vt-like" type="button" aria-pressed="false" data-count-toggle data-name="Like"><svg class="vt-heart" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="vt-sr">Like</span><span data-n>86</span></button><button class="vt-txtbtn" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
    <li class="vt-item"><span class="vt-av lg c1" aria-hidden="true">LK</span><div class="vt-main">
        <p class="vt-who"><b>Lucía Keller</b> · <time datetime="2026-10-19T06:15">6 hours ago</time></p>
        <p class="vt-text">Which plane is that? Mine chatters on oak no matter how sharp the iron is.</p>
        <div class="vt-row"><button class="vt-like" type="button" aria-pressed="true" data-count-toggle data-name="Like"><svg class="vt-heart" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="vt-sr">Like</span><span data-n>9</span></button><button class="vt-txtbtn" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
  </ol>
  <p class="vt-sr" role="status" data-announce></p>
</section></div>
CSS
/* Comment voting and reactions: up/down vote buttons with aria-pressed and a score, emoji reaction toggles with counts and a picker,
   and a compact like row. Every change is announced through a polite status region. */
.vt{--vt-up:#c2410c;--vt-down:#4f46e5;--vt-heart:#be123c;--vt-ink:#1a1d29;--vt-muted:#565b6e;--vt-line:#e3e5ee;--vt-field:#8c91a3;border:1px solid var(--vt-line);border-radius:18px;background:#fff;color:var(--vt-ink);font-size:.94rem;line-height:1.55;box-shadow:0 18px 40px -30px rgba(26,29,41,.4)}
.vt :where(ol){list-style:none}
.vt-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
:where(.vt) button{font:inherit;color:inherit;cursor:pointer}
.vt :where(h2,p,ol){margin:0}
.vt [hidden]{display:none!important}
.vt-head{padding:18px 24px;border-bottom:1px solid var(--vt-line)}
.vt-h{font-size:1.08rem;font-weight:700;line-height:1.35}
.vt-sub{margin-top:2px;color:var(--vt-muted);font-size:.84rem}
.vt-ans{display:flex;gap:18px;padding:20px 24px;border-bottom:1px solid var(--vt-line)}
.vt-ans:last-of-type{border-bottom:0}
.vt-ans.ok{background:linear-gradient(90deg,#f0fdf4,#fff 40%);box-shadow:inset 4px 0 0 #15803d}
.vt-votes{flex:none;display:flex;flex-direction:column;align-items:center;gap:4px}
.vt-score{font-size:1.1rem;font-weight:800;line-height:1.2;font-variant-numeric:tabular-nums}
.vt-main{flex:1;min-width:0}
.vt-ok{display:inline-flex;align-items:center;gap:6px;margin-bottom:6px;color:#15803d;font-size:.8rem;font-weight:700}
.vt-text{text-wrap:pretty}
.vt-by{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px;color:var(--vt-muted);font-size:.84rem}
.vt-by b{color:var(--vt-ink)}
.vt-av{flex:none;display:grid;place-items:center;width:30px;height:30px;border-radius:50%;font-size:.68rem;font-weight:700}
.vt-av.lg{width:40px;height:40px;font-size:.78rem}
.vt .c1{background:#fecdd3;color:#881337}
.vt .c2{background:#bae6fd;color:#0c4a6e}
.vt .c3{background:#d9f99d;color:#365314}
.vt .c4{background:#fde68a;color:#713f12}
.vt .c5{background:#ddd6fe;color:#4c1d95}
.vt-list{display:flex;flex-direction:column;margin:0;padding:0}
.vt-item{display:flex;gap:14px;padding:20px 24px;border-bottom:1px solid var(--vt-line)}
.vt-item:last-child{border-bottom:0}
.vt-who{font-size:.86rem}
.vt-who b{font-weight:700}
.vt-who time{color:var(--vt-muted)}
.vt-reacts{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px}
.vt-emo{font-size:1rem;line-height:1}
.vt-picker{position:absolute;top:calc(100% + 6px);left:0;z-index:10;display:flex;gap:2px;padding:4px;border:1px solid var(--vt-line);border-radius:14px;background:#fff;box-shadow:0 14px 30px -12px rgba(26,29,41,.35)}
.vt-row{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-top:6px}
.vt-heart{fill:none}
[aria-pressed=true] .vt-heart{fill:currentColor}
@keyframes vt-pop{40%{transform:scale(1.3)}}
@media (prefers-reduced-motion:no-preference){[aria-pressed=true] .vt-heart{animation:vt-pop .3s ease-out}}
@media (max-width:640px){.vt-head,.vt-ans,.vt-item{padding-left:16px;padding-right:16px}.vt-ans{gap:12px}}
.vt-vote{display:grid;place-items:center;width:36px;height:36px;padding:0;border:1.5px solid var(--vt-field);border-radius:50%;background:#fff;color:var(--vt-muted);transition:background-color .15s,border-color .15s,color .15s}
.vt-vote:hover{border-color:var(--vt-ink);color:var(--vt-ink)}
.vt-vote[data-vote=up][aria-pressed=true]{border-color:var(--vt-up);background:var(--vt-up);color:#fff}
.vt-vote[data-vote=down][aria-pressed=true]{border-color:var(--vt-down);background:var(--vt-down);color:#fff}
.vt-chip{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 10px;border:1.5px solid var(--vt-line);border-radius:999px;background:#f7f8fb;color:var(--vt-muted);font-size:.84rem;font-weight:700;font-variant-numeric:tabular-nums;transition:background-color .15s,border-color .15s,color .15s}
.vt-chip:hover{border-color:var(--vt-field)}
.vt-chip[aria-pressed=true]{border-color:var(--vt-down);background:#eef0ff;color:var(--vt-down)}
.vt-add{display:grid;place-items:center;width:32px;height:32px;padding:0;border:1.5px dashed var(--vt-field);border-radius:50%;background:none;color:var(--vt-muted)}
.vt-add:hover,.vt-add[aria-expanded=true]{border-style:solid;border-color:var(--vt-ink);color:var(--vt-ink)}
.vt-pick{display:grid;place-items:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:none;font-size:1.2rem}
.vt-pick:hover,.vt-like:hover,.vt-txtbtn:hover{background:#f1f2f7}
.vt-like,.vt-txtbtn{display:inline-flex;align-items:center;gap:6px;height:32px;padding:0 10px;border:0;border-radius:999px;background:none;color:var(--vt-muted);font-size:.84rem;font-weight:700;font-variant-numeric:tabular-nums}
.vt-like[aria-pressed=true]{color:var(--vt-heart)}
.vt-txtbtn[aria-pressed=true]{background:#e7e9f1;color:var(--vt-ink)}
.vt button:focus-visible{outline:2px solid var(--vt-ink);outline-offset:2px}
@media (prefers-reduced-motion:reduce){.vt *{transition:none}}
JavaScript
/* Voting and reactions: up/down votes (one at a time, click again to undo), count toggles for reactions and likes,
   and an emoji picker. Each change is announced politely. Frontend only: send the new state to your backend where marked. */
document.querySelectorAll('[data-vt]').forEach(function (root) {
  var live = root.querySelector('[data-announce]');
  var say = function (t) { live.textContent = ''; setTimeout(function () { live.textContent = t; }, 50); };
  root.querySelectorAll('[data-votes]').forEach(function (box) {
    var score = box.querySelector('[data-score]'), base = +box.dataset.base;
    box.addEventListener('click', function (e) {
      var b = e.target.closest('[data-vote]');
      if (!b) return;
      var on = b.getAttribute('aria-pressed') !== 'true';
      box.querySelectorAll('[data-vote]').forEach(function (x) { x.setAttribute('aria-pressed', x === b && on); });
      var n = base + (on ? (b.dataset.vote === 'up' ? 1 : -1) : 0);
      score.textContent = n; // send the vote here
      say((b.dataset.vote === 'up' ? 'Upvote ' : 'Downvote ') + (on ? 'added' : 'removed') + '. Score ' + n + '.');
    });
  });
  function toggle(b) {
    var on = b.getAttribute('aria-pressed') !== 'true', n = b.querySelector('[data-n]'), count = +n.textContent + (on ? 1 : -1);
    b.setAttribute('aria-pressed', on); n.textContent = count;
    say(b.dataset.name + (on ? ' added' : ' removed') + (n.hidden ? '.' : ', ' + count + ' in total.'));
    var bar = b.closest('[data-reactions]');
    if (!count && bar) { b.remove(); bar.querySelector('[data-picker-btn]').focus(); }
  }
  root.addEventListener('click', function (e) {
    var b = e.target.closest('[data-count-toggle]');
    if (b) toggle(b);
    var open = e.target.closest('[data-picker-btn]');
    if (open) setPicker(open, open.getAttribute('aria-expanded') !== 'true');
    var pick = e.target.closest('[data-picker] [data-emoji]');
    if (pick) {
      var bar = pick.closest('[data-reactions]'), btn = bar.querySelector('[data-picker-btn]');
      var chip = bar.querySelector('[data-count-toggle][data-emoji="' + pick.dataset.emoji + '"]');
      if (!chip) {
        chip = bar.querySelector('template').content.firstElementChild.cloneNode(true);
        chip.dataset.emoji = pick.dataset.emoji; chip.dataset.name = pick.dataset.name;
        chip.querySelector('[aria-hidden]').textContent = pick.dataset.emoji;
        chip.querySelector('[data-n]').previousElementSibling.textContent = pick.dataset.name;
        chip.querySelector('[data-n]').textContent = '0'; chip.setAttribute('aria-pressed', 'false');
        bar.insertBefore(chip, btn);
      }
      if (chip.getAttribute('aria-pressed') !== 'true') toggle(chip);
      setPicker(btn, false); btn.focus();
    }
  });
  function setPicker(btn, open) {
    btn.setAttribute('aria-expanded', open);
    var p = document.getElementById(btn.getAttribute('aria-controls'));
    p.hidden = !open;
    if (open) p.querySelector('button').focus();
  }
  root.addEventListener('keydown', function (e) {
    var p = e.target.closest('[data-picker]');
    if (e.key === 'Escape' && p) { var btn = p.parentElement.querySelector('[data-picker-btn]'); setPicker(btn, false); btn.focus(); }
  });
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Answer voting with an accepted answer</span>
<section class="card vt" data-vt aria-labelledby="vt-a-h">
  <div class="vt-head"><h2 class="vt-h" id="vt-a-h">Why does my sourdough starter smell like nail polish remover?</h2><p class="vt-sub">3 answers · sorted by votes</p></div>
  <article class="vt-ans ok" aria-label="Answer by Rhea Kapoor">
    <div class="vt-votes" data-votes data-base="128">
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="visually-hidden">Upvote answer by Rhea Kapoor</span></button>
      <p class="vt-score"><span data-score>128</span><span class="visually-hidden"> votes</span></p>
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="visually-hidden">Downvote answer by Rhea Kapoor</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-ok"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 5 5 9-10"/></svg>Accepted answer</p>
      <p class="vt-text">That sharp acetone smell means the yeast has run out of food. Feed it more often, or at a higher ratio such as 1:5:5, and it should smell sweet and tangy again within two or three days.</p>
      <p class="vt-by"><span class="vt-av c1" aria-hidden="true">RK</span><span><b>Rhea Kapoor</b> · answered <time datetime="2026-10-12T08:30">last week</time></span></p>
    </div>
  </article>
  <article class="vt-ans" aria-label="Answer by Owen Tran">
    <div class="vt-votes" data-votes data-base="37">
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="visually-hidden">Upvote answer by Owen Tran</span></button>
      <p class="vt-score"><span data-score>37</span><span class="visually-hidden"> votes</span></p>
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="visually-hidden">Downvote answer by Owen Tran</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-text">Also check the temperature. Above 27 °C my starter peaks in four hours and turns sour long before the next feed.</p>
      <p class="vt-by"><span class="vt-av c2" aria-hidden="true">OT</span><span><b>Owen Tran</b> · answered <time datetime="2026-10-14T19:05">5 days ago</time></span></p>
    </div>
  </article>
  <article class="vt-ans" aria-label="Answer by Bea Lund">
    <div class="vt-votes" data-votes data-base="-2">
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="visually-hidden">Upvote answer by Bea Lund</span></button>
      <p class="vt-score"><span data-score>-2</span><span class="visually-hidden"> votes</span></p>
      <button class="btn vt-vote" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="visually-hidden">Downvote answer by Bea Lund</span></button>
    </div>
    <div class="vt-main">
      <p class="vt-text">Mine did this and I threw it out. Starting over was quicker.</p>
      <p class="vt-by"><span class="vt-av c4" aria-hidden="true">BL</span><span><b>Bea Lund</b> · answered <time datetime="2026-10-18T10:40">yesterday</time></span></p>
    </div>
  </article>
  <p class="visually-hidden" role="status" data-announce></p>
</section></div>

<div><span class="lbl">Emoji reactions with a picker</span>
<section class="card vt" data-vt aria-label="Comments with reactions">
  <ol class="list-unstyled vt-list">
    <li class="vt-item"><span class="vt-av lg c5" aria-hidden="true">NA</span><div class="vt-main">
        <p class="vt-who"><b>Noor Aziz</b> · <time datetime="2026-10-19T09:14">3 hours ago</time></p>
        <p class="vt-text">The new dark map style is so much easier on the eyes at night. Any chance it can follow the system setting?</p>
        <div class="vt-reacts" data-reactions>
          <button class="btn vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="vt-emo" aria-hidden="true">👍</span><span class="visually-hidden">Thumbs up</span><span data-n>12</span></button><button class="btn vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="Heart" data-emoji="❤️"><span class="vt-emo" aria-hidden="true">❤️</span><span class="visually-hidden">Heart</span><span data-n>4</span></button><button class="btn vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Eyes" data-emoji="👀"><span class="vt-emo" aria-hidden="true">👀</span><span class="visually-hidden">Eyes</span><span data-n>2</span></button><button class="btn vt-add" type="button" aria-expanded="false" aria-controls="vt-pick-1" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="visually-hidden">Add reaction</span></button>
          <div class="vt-picker" id="vt-pick-1" role="group" aria-label="Reactions" hidden data-picker><button class="btn vt-pick" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="visually-hidden">Thumbs up</span></button><button class="btn vt-pick" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="visually-hidden">Heart</span></button><button class="btn vt-pick" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="visually-hidden">Laughing</span></button><button class="btn vt-pick" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="visually-hidden">Party</span></button><button class="btn vt-pick" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="visually-hidden">Thinking</span></button><button class="btn vt-pick" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="visually-hidden">Eyes</span></button></div>
          <template><button class="btn vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="vt-emo" aria-hidden="true"></span><span class="visually-hidden"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
    <li class="vt-item"><span class="vt-av lg c3" aria-hidden="true">JM</span><div class="vt-main">
        <p class="vt-who"><b>Jakob Meyer</b> · <time datetime="2026-10-19T10:02">2 hours ago</time></p>
        <p class="vt-text">It already does on Android 14. iOS support is in the next update.</p>
        <div class="vt-reacts" data-reactions>
          <button class="btn vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Party" data-emoji="🎉"><span class="vt-emo" aria-hidden="true">🎉</span><span class="visually-hidden">Party</span><span data-n>7</span></button><button class="btn vt-chip" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="vt-emo" aria-hidden="true">👍</span><span class="visually-hidden">Thumbs up</span><span data-n>3</span></button><button class="btn vt-add" type="button" aria-expanded="false" aria-controls="vt-pick-2" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="visually-hidden">Add reaction</span></button>
          <div class="vt-picker" id="vt-pick-2" role="group" aria-label="Reactions" hidden data-picker><button class="btn vt-pick" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="visually-hidden">Thumbs up</span></button><button class="btn vt-pick" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="visually-hidden">Heart</span></button><button class="btn vt-pick" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="visually-hidden">Laughing</span></button><button class="btn vt-pick" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="visually-hidden">Party</span></button><button class="btn vt-pick" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="visually-hidden">Thinking</span></button><button class="btn vt-pick" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="visually-hidden">Eyes</span></button></div>
          <template><button class="btn vt-chip" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="vt-emo" aria-hidden="true"></span><span class="visually-hidden"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
  </ol>
  <p class="visually-hidden" role="status" data-announce></p>
</section></div>

<div><span class="lbl">Compact like and dislike row</span>
<section class="card vt" data-vt aria-label="Video comments">
  <ol class="list-unstyled vt-list">
    <li class="vt-item"><span class="vt-av lg c2" aria-hidden="true">SO</span><div class="vt-main">
        <p class="vt-who"><b>Sam Osei</b> · <time datetime="2026-10-18T21:40">14 hours ago</time></p>
        <p class="vt-text">The bit at 4:12 where you plane the edge by hand finally made it click for me.</p>
        <div class="vt-row"><button class="btn vt-like" type="button" aria-pressed="false" data-count-toggle data-name="Like"><svg class="vt-heart" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="visually-hidden">Like</span><span data-n>86</span></button><button class="btn vt-txtbtn" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
    <li class="vt-item"><span class="vt-av lg c1" aria-hidden="true">LK</span><div class="vt-main">
        <p class="vt-who"><b>Lucía Keller</b> · <time datetime="2026-10-19T06:15">6 hours ago</time></p>
        <p class="vt-text">Which plane is that? Mine chatters on oak no matter how sharp the iron is.</p>
        <div class="vt-row"><button class="btn vt-like" type="button" aria-pressed="true" data-count-toggle data-name="Like"><svg class="vt-heart" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="visually-hidden">Like</span><span data-n>9</span></button><button class="btn vt-txtbtn" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
  </ol>
  <p class="visually-hidden" role="status" data-announce></p>
</section></div>
CSS
/* Comment voting and reactions on Bootstrap: .card panels and .btn controls restyled through Bootstrap variables.
   Votes and reactions are aria-pressed toggle buttons; every change is announced through a polite status region. */
.vt{--vt-up:#c2410c;--vt-down:#4f46e5;--vt-heart:#be123c;--vt-ink:#1a1d29;--vt-muted:#565b6e;--vt-line:#e3e5ee;--vt-field:#8c91a3;--bs-card-border-color:var(--vt-line);--bs-card-border-radius:18px;--bs-card-color:var(--vt-ink);font-size:.94rem;line-height:1.55;box-shadow:0 18px 40px -30px rgba(26,29,41,.4)}
.vt :where(h2,p,ol){margin:0}
.vt [hidden]{display:none!important}
.vt-head{padding:18px 24px;border-bottom:1px solid var(--vt-line)}
.vt-h{font-size:1.08rem;font-weight:700;line-height:1.35}
.vt-sub{margin-top:2px;color:var(--vt-muted);font-size:.84rem}
.vt-ans{display:flex;gap:18px;padding:20px 24px;border-bottom:1px solid var(--vt-line)}
.vt-ans:last-of-type{border-bottom:0}
.vt-ans.ok{background:linear-gradient(90deg,#f0fdf4,#fff 40%);box-shadow:inset 4px 0 0 #15803d}
.vt-votes{flex:none;display:flex;flex-direction:column;align-items:center;gap:4px}
.vt-score{font-size:1.1rem;font-weight:800;line-height:1.2;font-variant-numeric:tabular-nums}
.vt-main{flex:1;min-width:0}
.vt-ok{display:inline-flex;align-items:center;gap:6px;margin-bottom:6px;color:#15803d;font-size:.8rem;font-weight:700}
.vt-text{text-wrap:pretty}
.vt-by{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:10px;color:var(--vt-muted);font-size:.84rem}
.vt-by b{color:var(--vt-ink)}
.vt-av{flex:none;display:grid;place-items:center;width:30px;height:30px;border-radius:50%;font-size:.68rem;font-weight:700}
.vt-av.lg{width:40px;height:40px;font-size:.78rem}
.vt .c1{background:#fecdd3;color:#881337}
.vt .c2{background:#bae6fd;color:#0c4a6e}
.vt .c3{background:#d9f99d;color:#365314}
.vt .c4{background:#fde68a;color:#713f12}
.vt .c5{background:#ddd6fe;color:#4c1d95}
.vt-list{display:flex;flex-direction:column;margin:0;padding:0}
.vt-item{display:flex;gap:14px;padding:20px 24px;border-bottom:1px solid var(--vt-line)}
.vt-item:last-child{border-bottom:0}
.vt-who{font-size:.86rem}
.vt-who b{font-weight:700}
.vt-who time{color:var(--vt-muted)}
.vt-reacts{position:relative;display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:10px}
.vt-emo{font-size:1rem;line-height:1}
.vt-picker{position:absolute;top:calc(100% + 6px);left:0;z-index:10;display:flex;gap:2px;padding:4px;border:1px solid var(--vt-line);border-radius:14px;background:#fff;box-shadow:0 14px 30px -12px rgba(26,29,41,.35)}
.vt-row{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-top:6px}
.vt-heart{fill:none}
[aria-pressed=true] .vt-heart{fill:currentColor}
@keyframes vt-pop{40%{transform:scale(1.3)}}
@media (prefers-reduced-motion:no-preference){[aria-pressed=true] .vt-heart{animation:vt-pop .3s ease-out}}
@media (max-width:640px){.vt-head,.vt-ans,.vt-item{padding-left:16px;padding-right:16px}.vt-ans{gap:12px}}
.vt .btn{--bs-btn-focus-box-shadow:none;--bs-btn-padding-y:0;--bs-btn-font-weight:700;--bs-btn-font-size:.84rem;--bs-btn-line-height:1;--bs-btn-active-border-color:transparent}
.vt .btn:focus-visible{outline:2px solid var(--vt-ink);outline-offset:2px}
.vt-vote{--bs-btn-padding-x:0;--bs-btn-color:var(--vt-muted);--bs-btn-bg:#fff;--bs-btn-border-color:var(--vt-field);--bs-btn-border-width:1.5px;--bs-btn-border-radius:50%;--bs-btn-hover-color:var(--vt-ink);--bs-btn-hover-bg:#fff;--bs-btn-hover-border-color:var(--vt-ink);--bs-btn-active-color:var(--vt-ink);--bs-btn-active-bg:#fff;display:grid;place-items:center;width:36px;height:36px}
.vt-vote[data-vote=up][aria-pressed=true]{--bs-btn-color:#fff;--bs-btn-bg:var(--vt-up);--bs-btn-border-color:var(--vt-up);--bs-btn-hover-color:#fff;--bs-btn-hover-bg:var(--vt-up);--bs-btn-hover-border-color:var(--vt-up)}
.vt-vote[data-vote=down][aria-pressed=true]{--bs-btn-color:#fff;--bs-btn-bg:var(--vt-down);--bs-btn-border-color:var(--vt-down);--bs-btn-hover-color:#fff;--bs-btn-hover-bg:var(--vt-down);--bs-btn-hover-border-color:var(--vt-down)}
.vt-chip{--bs-btn-padding-x:10px;--bs-btn-color:var(--vt-muted);--bs-btn-bg:#f7f8fb;--bs-btn-border-color:var(--vt-line);--bs-btn-border-width:1.5px;--bs-btn-border-radius:999px;--bs-btn-hover-color:var(--vt-muted);--bs-btn-hover-bg:#f7f8fb;--bs-btn-hover-border-color:var(--vt-field);--bs-btn-active-color:var(--vt-muted);--bs-btn-active-bg:#f7f8fb;display:inline-flex;align-items:center;gap:6px;height:32px;font-variant-numeric:tabular-nums}
.vt-chip[aria-pressed=true]{--bs-btn-color:var(--vt-down);--bs-btn-bg:#eef0ff;--bs-btn-border-color:var(--vt-down);--bs-btn-hover-color:var(--vt-down);--bs-btn-hover-bg:#eef0ff;--bs-btn-hover-border-color:var(--vt-down)}
.vt-add{--bs-btn-padding-x:0;--bs-btn-color:var(--vt-muted);--bs-btn-border-color:var(--vt-field);--bs-btn-border-width:1.5px;--bs-btn-border-radius:50%;--bs-btn-hover-color:var(--vt-ink);--bs-btn-hover-border-color:var(--vt-ink);--bs-btn-active-color:var(--vt-ink);display:grid;place-items:center;width:32px;height:32px;border-style:dashed}
.vt-add:hover,.vt-add[aria-expanded=true]{border-style:solid;border-color:var(--vt-ink);color:var(--vt-ink)}
.vt-pick{--bs-btn-padding-x:0;--bs-btn-border-width:0;--bs-btn-border-radius:10px;--bs-btn-hover-bg:#f1f2f7;--bs-btn-active-bg:#f1f2f7;--bs-btn-font-size:1.2rem;display:grid;place-items:center;width:36px;height:36px}
.vt-like,.vt-txtbtn{--bs-btn-padding-x:10px;--bs-btn-color:var(--vt-muted);--bs-btn-border-width:0;--bs-btn-border-radius:999px;--bs-btn-hover-color:var(--vt-muted);--bs-btn-hover-bg:#f1f2f7;--bs-btn-active-color:var(--vt-muted);--bs-btn-active-bg:#f1f2f7;display:inline-flex;align-items:center;gap:6px;height:32px;font-variant-numeric:tabular-nums}
.vt-txtbtn[aria-pressed=true]{--bs-btn-bg:#e7e9f1;--bs-btn-color:var(--vt-ink);--bs-btn-hover-bg:#e7e9f1;--bs-btn-hover-color:var(--vt-ink)}
.vt-like[aria-pressed=true]{--bs-btn-color:var(--vt-heart);--bs-btn-hover-color:var(--vt-heart);--bs-btn-active-color:var(--vt-heart)}
@media (prefers-reduced-motion:reduce){.vt .btn{transition:none}}
JavaScript
/* Voting and reactions: up/down votes (one at a time, click again to undo), count toggles for reactions and likes,
   and an emoji picker. Each change is announced politely. Frontend only: send the new state to your backend where marked. */
document.querySelectorAll('[data-vt]').forEach(function (root) {
  var live = root.querySelector('[data-announce]');
  var say = function (t) { live.textContent = ''; setTimeout(function () { live.textContent = t; }, 50); };
  root.querySelectorAll('[data-votes]').forEach(function (box) {
    var score = box.querySelector('[data-score]'), base = +box.dataset.base;
    box.addEventListener('click', function (e) {
      var b = e.target.closest('[data-vote]');
      if (!b) return;
      var on = b.getAttribute('aria-pressed') !== 'true';
      box.querySelectorAll('[data-vote]').forEach(function (x) { x.setAttribute('aria-pressed', x === b && on); });
      var n = base + (on ? (b.dataset.vote === 'up' ? 1 : -1) : 0);
      score.textContent = n; // send the vote here
      say((b.dataset.vote === 'up' ? 'Upvote ' : 'Downvote ') + (on ? 'added' : 'removed') + '. Score ' + n + '.');
    });
  });
  function toggle(b) {
    var on = b.getAttribute('aria-pressed') !== 'true', n = b.querySelector('[data-n]'), count = +n.textContent + (on ? 1 : -1);
    b.setAttribute('aria-pressed', on); n.textContent = count;
    say(b.dataset.name + (on ? ' added' : ' removed') + (n.hidden ? '.' : ', ' + count + ' in total.'));
    var bar = b.closest('[data-reactions]');
    if (!count && bar) { b.remove(); bar.querySelector('[data-picker-btn]').focus(); }
  }
  root.addEventListener('click', function (e) {
    var b = e.target.closest('[data-count-toggle]');
    if (b) toggle(b);
    var open = e.target.closest('[data-picker-btn]');
    if (open) setPicker(open, open.getAttribute('aria-expanded') !== 'true');
    var pick = e.target.closest('[data-picker] [data-emoji]');
    if (pick) {
      var bar = pick.closest('[data-reactions]'), btn = bar.querySelector('[data-picker-btn]');
      var chip = bar.querySelector('[data-count-toggle][data-emoji="' + pick.dataset.emoji + '"]');
      if (!chip) {
        chip = bar.querySelector('template').content.firstElementChild.cloneNode(true);
        chip.dataset.emoji = pick.dataset.emoji; chip.dataset.name = pick.dataset.name;
        chip.querySelector('[aria-hidden]').textContent = pick.dataset.emoji;
        chip.querySelector('[data-n]').previousElementSibling.textContent = pick.dataset.name;
        chip.querySelector('[data-n]').textContent = '0'; chip.setAttribute('aria-pressed', 'false');
        bar.insertBefore(chip, btn);
      }
      if (chip.getAttribute('aria-pressed') !== 'true') toggle(chip);
      setPicker(btn, false); btn.focus();
    }
  });
  function setPicker(btn, open) {
    btn.setAttribute('aria-expanded', open);
    var p = document.getElementById(btn.getAttribute('aria-controls'));
    p.hidden = !open;
    if (open) p.querySelector('button').focus();
  }
  root.addEventListener('keydown', function (e) {
    var p = e.target.closest('[data-picker]');
    if (e.key === 'Escape' && p) { var btn = p.parentElement.querySelector('[data-picker-btn]'); setPicker(btn, false); btn.focus(); }
  });
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#565b6e] uppercase">Answer voting with an accepted answer</span>
<section class="rounded-[18px] border border-vt-line bg-white text-[.94rem] leading-[1.55] text-vt-ink shadow-[0_18px_40px_-30px_rgba(26,29,41,.4)]" data-vt aria-labelledby="vt-a-h">
  <div class="border-b border-vt-line px-6 py-[18px] max-sm:px-4"><h2 class="text-[1.08rem] leading-[1.35] font-bold" id="vt-a-h">Why does my sourdough starter smell like nail polish remover?</h2><p class="mt-0.5 text-[.84rem] text-vt-muted">3 answers · sorted by votes</p></div>
  <article class="flex gap-[18px] border-b border-vt-line bg-[linear-gradient(90deg,#f0fdf4,#fff_40%)] px-6 py-5 shadow-[inset_4px_0_0_#15803d] max-sm:gap-3 max-sm:px-4" aria-label="Answer by Rhea Kapoor">
    <div class="flex flex-none flex-col items-center gap-1" data-votes data-base="128">
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-up aria-pressed:bg-vt-up aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="sr-only">Upvote answer by Rhea Kapoor</span></button>
      <p class="text-[1.1rem] leading-[1.2] font-extrabold tabular-nums"><span data-score>128</span><span class="sr-only"> votes</span></p>
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-down aria-pressed:bg-vt-down aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="sr-only">Downvote answer by Rhea Kapoor</span></button>
    </div>
    <div class="min-w-0 flex-1">
      <p class="mb-1.5 inline-flex items-center gap-1.5 text-[.8rem] font-bold text-[#15803d]"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 5 5 9-10"/></svg>Accepted answer</p>
      <p class="text-pretty">That sharp acetone smell means the yeast has run out of food. Feed it more often, or at a higher ratio such as 1:5:5, and it should smell sweet and tangy again within two or three days.</p>
      <p class="mt-2.5 flex flex-wrap items-center gap-2 text-[.84rem] text-vt-muted [&_b]:text-vt-ink"><span class="grid size-[30px] flex-none place-items-center rounded-full text-[.68rem] font-bold bg-[#fecdd3] text-[#881337]" aria-hidden="true">RK</span><span><b>Rhea Kapoor</b> · answered <time datetime="2026-10-12T08:30">last week</time></span></p>
    </div>
  </article>
  <article class="flex gap-[18px] border-b border-vt-line px-6 py-5 last-of-type:border-b-0 max-sm:gap-3 max-sm:px-4" aria-label="Answer by Owen Tran">
    <div class="flex flex-none flex-col items-center gap-1" data-votes data-base="37">
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-up aria-pressed:bg-vt-up aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="sr-only">Upvote answer by Owen Tran</span></button>
      <p class="text-[1.1rem] leading-[1.2] font-extrabold tabular-nums"><span data-score>37</span><span class="sr-only"> votes</span></p>
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-down aria-pressed:bg-vt-down aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="sr-only">Downvote answer by Owen Tran</span></button>
    </div>
    <div class="min-w-0 flex-1">
      <p class="text-pretty">Also check the temperature. Above 27 °C my starter peaks in four hours and turns sour long before the next feed.</p>
      <p class="mt-2.5 flex flex-wrap items-center gap-2 text-[.84rem] text-vt-muted [&_b]:text-vt-ink"><span class="grid size-[30px] flex-none place-items-center rounded-full text-[.68rem] font-bold bg-[#bae6fd] text-[#0c4a6e]" aria-hidden="true">OT</span><span><b>Owen Tran</b> · answered <time datetime="2026-10-14T19:05">5 days ago</time></span></p>
    </div>
  </article>
  <article class="flex gap-[18px] border-b border-vt-line px-6 py-5 last-of-type:border-b-0 max-sm:gap-3 max-sm:px-4" aria-label="Answer by Bea Lund">
    <div class="flex flex-none flex-col items-center gap-1" data-votes data-base="-2">
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-up aria-pressed:bg-vt-up aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="up"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 19V5M5 12l7-7 7 7"/></svg><span class="sr-only">Upvote answer by Bea Lund</span></button>
      <p class="text-[1.1rem] leading-[1.2] font-extrabold tabular-nums"><span data-score>-2</span><span class="sr-only"> votes</span></p>
      <button class="grid size-9 cursor-pointer place-items-center rounded-full border-[1.5px] border-[#8c91a3] text-vt-muted transition-colors duration-150 hover:border-vt-ink hover:text-vt-ink aria-pressed:border-vt-down aria-pressed:bg-vt-down aria-pressed:text-white motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-vote="down"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5v14M5 12l7 7 7-7"/></svg><span class="sr-only">Downvote answer by Bea Lund</span></button>
    </div>
    <div class="min-w-0 flex-1">
      <p class="text-pretty">Mine did this and I threw it out. Starting over was quicker.</p>
      <p class="mt-2.5 flex flex-wrap items-center gap-2 text-[.84rem] text-vt-muted [&_b]:text-vt-ink"><span class="grid size-[30px] flex-none place-items-center rounded-full text-[.68rem] font-bold bg-[#fde68a] text-[#713f12]" aria-hidden="true">BL</span><span><b>Bea Lund</b> · answered <time datetime="2026-10-18T10:40">yesterday</time></span></p>
    </div>
  </article>
  <p class="sr-only" role="status" data-announce></p>
</section></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#565b6e] uppercase">Emoji reactions with a picker</span>
<section class="rounded-[18px] border border-vt-line bg-white text-[.94rem] leading-[1.55] text-vt-ink shadow-[0_18px_40px_-30px_rgba(26,29,41,.4)]" data-vt aria-label="Comments with reactions">
  <ol class="flex flex-col">
    <li class="flex gap-3.5 border-b border-vt-line px-6 py-5 last:border-b-0 max-sm:px-4"><span class="grid size-10 flex-none place-items-center rounded-full text-[.78rem] font-bold bg-[#ddd6fe] text-[#4c1d95]" aria-hidden="true">NA</span><div class="min-w-0 flex-1">
        <p class="text-[.86rem] [&_b]:font-bold [&_time]:text-vt-muted"><b>Noor Aziz</b> · <time datetime="2026-10-19T09:14">3 hours ago</time></p>
        <p class="text-pretty">The new dark map style is so much easier on the eyes at night. Any chance it can follow the system setting?</p>
        <div class="relative mt-2.5 flex flex-wrap items-center gap-1.5" data-reactions>
          <button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="text-base leading-none" aria-hidden="true">👍</span><span class="sr-only">Thumbs up</span><span data-n>12</span></button><button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="true" data-count-toggle data-name="Heart" data-emoji="❤️"><span class="text-base leading-none" aria-hidden="true">❤️</span><span class="sr-only">Heart</span><span data-n>4</span></button><button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Eyes" data-emoji="👀"><span class="text-base leading-none" aria-hidden="true">👀</span><span class="sr-only">Eyes</span><span data-n>2</span></button><button class="grid size-8 cursor-pointer place-items-center rounded-full border-[1.5px] border-dashed border-[#8c91a3] text-vt-muted hover:border-vt-ink hover:text-vt-ink aria-expanded:border-solid aria-expanded:border-vt-ink aria-expanded:text-vt-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-expanded="false" aria-controls="vt-pick-1" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="sr-only">Add reaction</span></button>
          <div class="absolute top-[calc(100%+6px)] left-0 z-10 flex gap-0.5 rounded-[14px] border border-vt-line bg-white p-1 shadow-[0_14px_30px_-12px_rgba(26,29,41,.35)]" id="vt-pick-1" role="group" aria-label="Reactions" hidden data-picker><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="sr-only">Thumbs up</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="sr-only">Heart</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="sr-only">Laughing</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="sr-only">Party</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="sr-only">Thinking</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="sr-only">Eyes</span></button></div>
          <template><button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="text-base leading-none" aria-hidden="true"></span><span class="sr-only"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
    <li class="flex gap-3.5 border-b border-vt-line px-6 py-5 last:border-b-0 max-sm:px-4"><span class="grid size-10 flex-none place-items-center rounded-full text-[.78rem] font-bold bg-[#d9f99d] text-[#365314]" aria-hidden="true">JM</span><div class="min-w-0 flex-1">
        <p class="text-[.86rem] [&_b]:font-bold [&_time]:text-vt-muted"><b>Jakob Meyer</b> · <time datetime="2026-10-19T10:02">2 hours ago</time></p>
        <p class="text-pretty">It already does on Android 14. iOS support is in the next update.</p>
        <div class="relative mt-2.5 flex flex-wrap items-center gap-1.5" data-reactions>
          <button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Party" data-emoji="🎉"><span class="text-base leading-none" aria-hidden="true">🎉</span><span class="sr-only">Party</span><span data-n>7</span></button><button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Thumbs up" data-emoji="👍"><span class="text-base leading-none" aria-hidden="true">👍</span><span class="sr-only">Thumbs up</span><span data-n>3</span></button><button class="grid size-8 cursor-pointer place-items-center rounded-full border-[1.5px] border-dashed border-[#8c91a3] text-vt-muted hover:border-vt-ink hover:text-vt-ink aria-expanded:border-solid aria-expanded:border-vt-ink aria-expanded:text-vt-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-expanded="false" aria-controls="vt-pick-2" data-picker-btn><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="13" r="8"/><path d="M8 15a4 4 0 0 0 6 0M8.5 11h.01M13.5 11h.01M19 2v6M16 5h6"/></svg><span class="sr-only">Add reaction</span></button>
          <div class="absolute top-[calc(100%+6px)] left-0 z-10 flex gap-0.5 rounded-[14px] border border-vt-line bg-white p-1 shadow-[0_14px_30px_-12px_rgba(26,29,41,.35)]" id="vt-pick-2" role="group" aria-label="Reactions" hidden data-picker><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="👍" data-name="Thumbs up"><span aria-hidden="true">👍</span><span class="sr-only">Thumbs up</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="❤️" data-name="Heart"><span aria-hidden="true">❤️</span><span class="sr-only">Heart</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="😂" data-name="Laughing"><span aria-hidden="true">😂</span><span class="sr-only">Laughing</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="🎉" data-name="Party"><span aria-hidden="true">🎉</span><span class="sr-only">Party</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="🤔" data-name="Thinking"><span aria-hidden="true">🤔</span><span class="sr-only">Thinking</span></button><button class="grid size-9 cursor-pointer place-items-center rounded-[10px] text-[1.2rem] hover:bg-[#f1f2f7] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" data-emoji="👀" data-name="Eyes"><span aria-hidden="true">👀</span><span class="sr-only">Eyes</span></button></div>
          <template><button class="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full border-[1.5px] border-vt-line bg-[#f7f8fb] px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums transition-colors duration-150 hover:border-[#8c91a3] aria-pressed:border-vt-down aria-pressed:bg-[#eef0ff] aria-pressed:text-vt-down motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="true" data-count-toggle data-name="" data-emoji=""><span class="text-base leading-none" aria-hidden="true"></span><span class="sr-only"></span><span data-n>1</span></button></template>
        </div>
    </div></li>
  </ol>
  <p class="sr-only" role="status" data-announce></p>
</section></div>

<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#565b6e] uppercase">Compact like and dislike row</span>
<section class="rounded-[18px] border border-vt-line bg-white text-[.94rem] leading-[1.55] text-vt-ink shadow-[0_18px_40px_-30px_rgba(26,29,41,.4)]" data-vt aria-label="Video comments">
  <ol class="flex flex-col">
    <li class="flex gap-3.5 border-b border-vt-line px-6 py-5 last:border-b-0 max-sm:px-4"><span class="grid size-10 flex-none place-items-center rounded-full text-[.78rem] font-bold bg-[#bae6fd] text-[#0c4a6e]" aria-hidden="true">SO</span><div class="min-w-0 flex-1">
        <p class="text-[.86rem] [&_b]:font-bold [&_time]:text-vt-muted"><b>Sam Osei</b> · <time datetime="2026-10-18T21:40">14 hours ago</time></p>
        <p class="text-pretty">The bit at 4:12 where you plane the edge by hand finally made it click for me.</p>
        <div class="mt-1.5 flex flex-wrap items-center gap-1"><button class="group/l inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums hover:bg-[#f1f2f7] aria-pressed:text-vt-heart focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Like"><svg class="fill-none group-aria-pressed/l:fill-current motion-safe:group-aria-pressed/l:animate-[vt-pop_.3s_ease-out]" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="sr-only">Like</span><span data-n>86</span></button><button class="inline-flex h-8 cursor-pointer items-center rounded-full px-2.5 text-[.84rem] font-bold text-vt-muted hover:bg-[#f1f2f7] aria-pressed:bg-[#e7e9f1] aria-pressed:text-vt-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
    <li class="flex gap-3.5 border-b border-vt-line px-6 py-5 last:border-b-0 max-sm:px-4"><span class="grid size-10 flex-none place-items-center rounded-full text-[.78rem] font-bold bg-[#fecdd3] text-[#881337]" aria-hidden="true">LK</span><div class="min-w-0 flex-1">
        <p class="text-[.86rem] [&_b]:font-bold [&_time]:text-vt-muted"><b>Lucía Keller</b> · <time datetime="2026-10-19T06:15">6 hours ago</time></p>
        <p class="text-pretty">Which plane is that? Mine chatters on oak no matter how sharp the iron is.</p>
        <div class="mt-1.5 flex flex-wrap items-center gap-1"><button class="group/l inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full px-2.5 text-[.84rem] font-bold text-vt-muted tabular-nums hover:bg-[#f1f2f7] aria-pressed:text-vt-heart focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="true" data-count-toggle data-name="Like"><svg class="fill-none group-aria-pressed/l:fill-current motion-safe:group-aria-pressed/l:animate-[vt-pop_.3s_ease-out]" width="18" height="18" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7-4.4-9.2-8.6C1.3 8.4 3.2 5 6.6 5c2.1 0 3.6 1.2 5.4 3 1.8-1.8 3.3-3 5.4-3 3.4 0 5.3 3.4 3.8 6.4C19 15.6 12 20 12 20Z"/></svg><span class="sr-only">Like</span><span data-n>9</span></button><button class="inline-flex h-8 cursor-pointer items-center rounded-full px-2.5 text-[.84rem] font-bold text-vt-muted hover:bg-[#f1f2f7] aria-pressed:bg-[#e7e9f1] aria-pressed:text-vt-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-vt-ink" type="button" aria-pressed="false" data-count-toggle data-name="Dislike">Dislike<span hidden data-n>0</span></button></div>
    </div></li>
  </ol>
  <p class="sr-only" role="status" data-announce></p>
</section></div>
CSS
/* Palette as theme colours. Pressed states use aria-pressed: and group-aria-pressed/l:, the picker uses aria-expanded:,
   and the heart pop runs only under motion-safe:. */
@theme {
  --color-vt-up:#c2410c; --color-vt-down:#4f46e5; --color-vt-heart:#be123c; --color-vt-ink:#1a1d29;
  --color-vt-muted:#565b6e; --color-vt-line:#e3e5ee;
}
@keyframes vt-pop{40%{transform:scale(1.3)}}
JavaScript
/* Voting and reactions: up/down votes (one at a time, click again to undo), count toggles for reactions and likes,
   and an emoji picker. Each change is announced politely. Frontend only: send the new state to your backend where marked. */
document.querySelectorAll('[data-vt]').forEach(function (root) {
  var live = root.querySelector('[data-announce]');
  var say = function (t) { live.textContent = ''; setTimeout(function () { live.textContent = t; }, 50); };
  root.querySelectorAll('[data-votes]').forEach(function (box) {
    var score = box.querySelector('[data-score]'), base = +box.dataset.base;
    box.addEventListener('click', function (e) {
      var b = e.target.closest('[data-vote]');
      if (!b) return;
      var on = b.getAttribute('aria-pressed') !== 'true';
      box.querySelectorAll('[data-vote]').forEach(function (x) { x.setAttribute('aria-pressed', x === b && on); });
      var n = base + (on ? (b.dataset.vote === 'up' ? 1 : -1) : 0);
      score.textContent = n; // send the vote here
      say((b.dataset.vote === 'up' ? 'Upvote ' : 'Downvote ') + (on ? 'added' : 'removed') + '. Score ' + n + '.');
    });
  });
  function toggle(b) {
    var on = b.getAttribute('aria-pressed') !== 'true', n = b.querySelector('[data-n]'), count = +n.textContent + (on ? 1 : -1);
    b.setAttribute('aria-pressed', on); n.textContent = count;
    say(b.dataset.name + (on ? ' added' : ' removed') + (n.hidden ? '.' : ', ' + count + ' in total.'));
    var bar = b.closest('[data-reactions]');
    if (!count && bar) { b.remove(); bar.querySelector('[data-picker-btn]').focus(); }
  }
  root.addEventListener('click', function (e) {
    var b = e.target.closest('[data-count-toggle]');
    if (b) toggle(b);
    var open = e.target.closest('[data-picker-btn]');
    if (open) setPicker(open, open.getAttribute('aria-expanded') !== 'true');
    var pick = e.target.closest('[data-picker] [data-emoji]');
    if (pick) {
      var bar = pick.closest('[data-reactions]'), btn = bar.querySelector('[data-picker-btn]');
      var chip = bar.querySelector('[data-count-toggle][data-emoji="' + pick.dataset.emoji + '"]');
      if (!chip) {
        chip = bar.querySelector('template').content.firstElementChild.cloneNode(true);
        chip.dataset.emoji = pick.dataset.emoji; chip.dataset.name = pick.dataset.name;
        chip.querySelector('[aria-hidden]').textContent = pick.dataset.emoji;
        chip.querySelector('[data-n]').previousElementSibling.textContent = pick.dataset.name;
        chip.querySelector('[data-n]').textContent = '0'; chip.setAttribute('aria-pressed', 'false');
        bar.insertBefore(chip, btn);
      }
      if (chip.getAttribute('aria-pressed') !== 'true') toggle(chip);
      setPicker(btn, false); btn.focus();
    }
  });
  function setPicker(btn, open) {
    btn.setAttribute('aria-expanded', open);
    var p = document.getElementById(btn.getAttribute('aria-controls'));
    p.hidden = !open;
    if (open) p.querySelector('button').focus();
  }
  root.addEventListener('keydown', function (e) {
    var p = e.target.closest('[data-picker]');
    if (e.key === 'Escape' && p) { var btn = p.parentElement.querySelector('[data-picker-btn]'); setPicker(btn, false); btn.focus(); }
  });
});

About this set

Three ways to let readers rate comments without writing one. Answer voting is the Q&A pattern: each answer has an up arrow, a score and a down arrow, the accepted answer gets a green rail and a written Accepted answer label, and negative scores are shown as they are. Only one vote per answer can be pressed; pressing it again takes the vote back. Emoji reactions put counted chips such as thumbs up 12 under each comment, with a dashed Add reaction button that opens a row of six emoji; picking one presses the matching chip or creates a new one, and a chip that drops to zero disappears. The compact like row is the video-site style: a heart with a count and a Dislike button without one. Every control is a button with aria-pressed, so its state is announced, and each change is also read out through a polite status region, for example Upvote added. Score 129. Use voting for questions and answers, reactions for team or community threads, and the like row for long public comment lists.

What’s included

  • Up and down votes with aria-pressed and a live score
  • Accepted answer marked with a written label, not colour alone
  • Emoji reaction chips with counts and an Add reaction picker
  • Heart like and Dislike toggles for compact comment rows
  • Every change announced through a polite status region

Accessibility

Votes, reactions and likes are buttons with aria-pressed and names that include what they do and whose comment they apply to, such as Upvote answer by Rhea Kapoor. Emoji are hidden from screen readers and replaced by a word, counts are part of each button's name, and a polite status region announces each change. The picker opens with aria-expanded, moves focus to the first emoji, and Esc closes it and returns focus. The heart pop runs only when motion is allowed.

Customise it

Change --vt-up, --vt-down and --vt-heart on .vt for the pressed colours. Bootstrap sets the same states through --bs-btn-* variables; in Tailwind edit the --color-vt-* theme colours.