Open demo

CSS code

HTML
<div class="demo-row">
<div><span class="lbl">Dark cart</span>
<section class="dk" data-cart data-free-over="300" data-ship="12.00" aria-labelledby="dk-title">
  <div class="dk-head"><h2 class="dk-title" id="dk-title">Your cart</h2><span class="dk-count" data-count>2 items</span></div>
  <div data-filled>
    <div class="dk-free"><p id="dk-free" data-free-msg>You have unlocked free shipping.</p><div class="dk-track" role="progressbar" aria-labelledby="dk-free" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"><span class="dk-fill" data-free-bar style="width:100%"></span></div></div>
    <ul class="dk-list">
      <li class="dk-line" data-line data-name="Studio monitor headphones" data-price="189.00">
        <img class="dk-img" src="https://images.unsplash.com/photo-1583394838336-acd977736f90?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="dk-info"><h3 class="dk-name">Studio monitor headphones</h3><p class="dk-meta">Graphite · Wired and wireless · $189.00</p></div>
        <div class="dk-qty"><label for="dk-q1">Qty<span class="sr-only"> of Studio monitor headphones</span></label><div class="dk-step"><button type="button" data-dec aria-label="Decrease quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input id="dk-q1" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button type="button" data-inc aria-label="Increase quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="dk-total" data-line-total>$189.00</p>
        <button class="dk-remove" type="button" data-remove>Remove<span class="sr-only"> Studio monitor headphones</span></button>
      </li>
      <li class="dk-line" data-line data-name="Field watch" data-price="149.00">
        <img class="dk-img" src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="dk-info"><h3 class="dk-name">Field watch</h3><p class="dk-meta">White · 38 mm · $149.00</p></div>
        <div class="dk-qty"><label for="dk-q2">Qty<span class="sr-only"> of Field watch</span></label><div class="dk-step"><button type="button" data-dec aria-label="Decrease quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input id="dk-q2" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button type="button" data-inc aria-label="Increase quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="dk-total" data-line-total>$149.00</p>
        <button class="dk-remove" type="button" data-remove>Remove<span class="sr-only"> Field watch</span></button>
      </li>
    </ul>
    <dl class="dk-rows">
      <div class="dk-row"><dt>Subtotal</dt><dd data-subtotal>$338.00</dd></div>
      <div class="dk-row"><dt>Shipping</dt><dd data-shipping>Free</dd></div>
    </dl>
    <dl class="dk-sum"><dt>Total</dt><dd data-total>$338.00</dd></dl>
    <a class="dk-btn" href="#"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Checkout</a>
  </div>
  <div class="dk-empty" data-empty hidden><h3 class="dk-empty-title" tabindex="-1">Your cart is empty</h3><p>Nothing here yet. The new monitor range is in stock.</p><a class="dk-btn" href="#">Browse headphones</a></div>
  <p class="sr-only" role="status" data-announce></p>
</section>
</div>
<div><span class="lbl">Dark express checkout</span>
<form class="dk" action="#" method="post" data-checkout data-subtotal="129.00" aria-labelledby="dk-x-title">
  <h2 class="dk-title" id="dk-x-title">Express checkout</h2>
  <div class="dk-product"><img src="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=112&h=112&fit=crop&auto=format&q=70" alt="" width="56" height="56"><div><p class="dk-name">Travel headphones</p><p class="dk-meta">Matte black · Qty 1</p></div><p class="dk-price" data-subtotal>$129.00</p></div>
  <div class="dk-field"><label class="dk-label" for="dk-email">Email</label><input class="dk-input" id="dk-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required aria-describedby="dk-email-err" data-missing="Enter your email address." data-bad="Enter an email address like [email protected]."><p class="dk-err" id="dk-email-err"></p></div>
  <fieldset class="dk-set"><legend>Delivery</legend><div class="dk-seg"><label class="dk-opt"><input type="radio" name="delivery" value="std" data-ship-option data-label="Standard" data-price="9.00" checked><strong>Standard · $9.00</strong><span>4 to 6 days</span></label><label class="dk-opt"><input type="radio" name="delivery" value="exp" data-ship-option data-label="Express" data-price="19.00"><strong>Express · $19.00</strong><span>1 to 2 days</span></label></div></fieldset>
  <div class="dk-demo"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 3v6c0 4.5-3.4 8-8 9-4.6-1-8-4.5-8-9V6z"/><path d="M12 8v4M12 16h.01"/></svg><p>Demo only. These card fields are not connected to a payment provider and nothing is charged.</p></div>
  <div class="dk-field"><label class="dk-label" for="dk-card">Card number</label><input class="dk-input" id="dk-card" type="text" autocomplete="cc-number" inputmode="numeric" pattern="[0-9 ]{13,23}" placeholder="1234 1234 1234 1234" required aria-describedby="dk-card-err" data-missing="Enter a card number." data-bad="Enter the 13 to 19 digits on the front of the card."><p class="dk-err" id="dk-card-err"></p></div>
  <div class="dk-pair"><div class=""><label class="dk-label" for="dk-exp">Expiry date</label><input class="dk-input" id="dk-exp" type="text" autocomplete="cc-exp" inputmode="numeric" pattern="(0[1-9]|1[0-2]) ?/ ?[0-9]{2}" placeholder="MM / YY" required aria-describedby="dk-exp-err" data-missing="Enter the expiry date." data-bad="Use the format MM / YY."><p class="dk-err" id="dk-exp-err"></p></div><div class=""><label class="dk-label" for="dk-cvc">Security code</label><input class="dk-input" id="dk-cvc" type="text" autocomplete="cc-csc" inputmode="numeric" pattern="[0-9]{3,4}" required aria-describedby="dk-cvc-hint dk-cvc-err" data-missing="Enter the security code." data-bad="Enter the 3 or 4 digit code."><p class="dk-hint" id="dk-cvc-hint">3 or 4 digits on the back.</p><p class="dk-err" id="dk-cvc-err"></p></div></div>
  <button class="dk-btn" type="submit"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Place demo order · <span data-total>$138.00</span></button>
  <p class="dk-fine">Includes <span data-shipping>$9.00</span> delivery. No payment is taken.</p>
  <p class="dk-status" role="status" data-status></p>
</form>
</div>
</div>
CSS
/* Dark cart and express checkout. Borders and grey text are tuned to keep 3:1 and 4.5:1 on the dark cards,
   and the single lime accent carries dark text. The card fields are a front-end mock with no name attribute. */
.dk{--dk-bg:#121821;--dk-field:#0b1019;--dk-ink:#e8edf5;--dk-muted:#9aa4b5;--dk-line:#263040;--dk-edge:#6b7588;--dk-accent:#a3e635;--dk-accent-dark:#bef264;
  --dk-on-accent:#0b1019;--dk-ring:rgba(163,230,53,.3);--dk-error:#f87171;--dk-ok:#4ade80;color-scheme:dark;color:var(--dk-ink);font-size:.93rem;line-height:1.45;
  background:var(--dk-bg);border:1px solid var(--dk-line);border-radius:20px;padding:24px;box-shadow:0 30px 60px -30px rgba(0,0,0,.8)}
.dk [hidden]{display:none!important}
.dk .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.dk :where(button,input){font:inherit;color:inherit}
.dk :is(a,button):focus-visible,.dk-empty-title:focus-visible{outline:2px solid var(--dk-accent);outline-offset:2px}
.dk-empty-title:focus:not(:focus-visible){outline:none}
.dk-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.dk-title{font-size:1.25rem;font-weight:700;letter-spacing:-.01em}
.dk-count{color:var(--dk-muted);font-size:.86rem}
.dk-free{margin:16px 0 4px;padding:12px 14px;border-radius:12px;background:#18202c;font-size:.85rem;font-weight:600}
.dk-free p{display:flex;gap:8px;align-items:center}
.dk-free p::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--dk-accent);box-shadow:0 0 10px var(--dk-accent)}
.dk-track{height:6px;border-radius:99px;background:#2a3444;margin-top:8px;overflow:hidden}
.dk-fill{display:block;height:100%;border-radius:inherit;background:var(--dk-accent);transition:width .3s}
.dk-list{list-style:none}
.dk-line{display:grid;grid-template-columns:64px minmax(0,1fr) auto auto;grid-template-areas:"img info qty total" "img info qty rm";gap:4px 16px;align-items:center;padding:16px 0;border-bottom:1px solid var(--dk-line)}
.dk-img{grid-area:img;width:64px;height:64px;border-radius:12px;object-fit:cover;background:#1b2330}
.dk-info{grid-area:info;min-width:0}
.dk-name{font-weight:600;line-height:1.3}
.dk-meta{font-size:.82rem;color:var(--dk-muted)}
.dk-qty{grid-area:qty}
.dk-qty label{display:block;font-size:.72rem;font-weight:600;color:var(--dk-muted);margin-bottom:3px}
.dk-step{display:inline-flex;align-items:center;border:1px solid var(--dk-edge);border-radius:10px;background:var(--dk-field)}
.dk-step:focus-within{border-color:var(--dk-accent);box-shadow:0 0 0 3px var(--dk-ring)}
.dk-step button{width:32px;height:32px;display:grid;place-items:center;border:0;background:none;border-radius:9px;cursor:pointer}
.dk-step button:hover{background:#1e2733}
.dk-step button[aria-disabled="true"]{color:#5d6778;cursor:not-allowed;background:none}
.dk-step input{width:34px;height:32px;border:0;background:none;text-align:center;font-weight:600;-moz-appearance:textfield;appearance:textfield}
.dk-step input::-webkit-inner-spin-button,.dk-step input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.dk-step input:focus{outline:none}
.dk-total{grid-area:total;text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.dk-price{text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.dk-remove{grid-area:rm;justify-self:end;border:0;background:none;padding:2px 0;color:var(--dk-muted);font-size:.8rem;text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.dk-remove:hover{color:var(--dk-ink)}
.dk-rows{display:grid;gap:8px;padding:16px 0;border-bottom:1px solid var(--dk-line)}
.dk-row{display:flex;justify-content:space-between;gap:12px}
.dk-row dt{color:var(--dk-muted)}
.dk-row dd{font-weight:600;font-variant-numeric:tabular-nums}
.dk-sum{display:flex;justify-content:space-between;align-items:baseline;padding-top:14px;font-weight:700}
.dk-sum dd{font-size:1.4rem;font-weight:800;font-variant-numeric:tabular-nums}
.dk-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:16px;padding:13px 18px;border:0;border-radius:12px;background:var(--dk-accent);color:var(--dk-on-accent);
  font-weight:800;text-decoration:none;cursor:pointer;transition:background-color .15s,box-shadow .15s}
.dk-btn:hover{background:var(--dk-accent-dark);box-shadow:0 0 24px -6px var(--dk-accent)}
.dk-empty{text-align:center;padding:36px 10px 10px}
.dk-empty-title{font-size:1.1rem;font-weight:700;margin-bottom:4px}
.dk-empty p{color:var(--dk-muted);margin-bottom:16px}
/* express checkout */
.dk-product{display:grid;grid-template-columns:56px minmax(0,1fr) auto;gap:12px;align-items:center;padding:14px 0 16px;border-bottom:1px solid var(--dk-line);margin-bottom:16px}
.dk-product img{width:56px;height:56px;border-radius:12px;object-fit:cover}
.dk-field{margin-bottom:14px}
.dk-label{display:block;font-size:.84rem;font-weight:600;margin-bottom:6px}
.dk-input{display:block;width:100%;padding:11px 13px;border:1px solid var(--dk-edge);border-radius:10px;background:var(--dk-field);transition:border-color .15s,box-shadow .15s}
.dk-input::placeholder{color:#8b95a7}
.dk-input:focus{outline:none;border-color:var(--dk-accent);box-shadow:0 0 0 3px var(--dk-ring)}
.dk-input[aria-invalid="true"]{border-color:var(--dk-error)}
.dk-hint{font-size:.78rem;color:var(--dk-muted);margin-top:5px}
.dk-err{display:flex;gap:6px;color:var(--dk-error);font-size:.8rem;font-weight:600;margin-top:6px}
.dk-err::before{content:"!";flex:none;width:16px;height:16px;margin-top:1px;border-radius:50%;background:var(--dk-error);color:#0b1019;font-size:.7rem;font-weight:800;display:grid;place-items:center}
.dk-err:empty{display:none}
.dk-pair{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.dk-set{border:0;padding:0;margin:0 0 16px;min-width:0}
.dk-set legend{float:left;width:100%;font-size:.84rem;font-weight:600;margin-bottom:6px;padding:0}
.dk-set legend+*{clear:left}
.dk-seg{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.dk-opt{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;padding:11px 12px;border:1px solid var(--dk-edge);border-radius:12px;background:var(--dk-field);cursor:pointer;transition:border-color .15s}
.dk-opt:hover{border-color:var(--dk-muted)}
.dk-opt:has(:checked){border-color:var(--dk-accent);box-shadow:inset 0 0 0 1px var(--dk-accent)}
.dk-opt input{grid-row:span 2;width:16px;height:16px;margin:3px 0 0;accent-color:var(--dk-accent);cursor:pointer}
.dk-opt input:focus-visible{outline:2px solid var(--dk-accent);outline-offset:2px}
.dk-opt span{font-size:.8rem;color:var(--dk-muted)}
.dk-opt strong{font-size:.9rem}
.dk-demo{display:flex;gap:10px;align-items:flex-start;padding:10px 12px;margin-bottom:14px;border-radius:10px;border:1px dashed #8a6d1f;background:#1d1a10;color:#fcd98b;font-size:.8rem}
.dk-demo svg{flex:none}
.dk-status:not(:empty){margin-top:12px;padding:10px 12px;border-radius:10px;background:#0f2a1a;color:var(--dk-ok);font-weight:600;font-size:.84rem}
.dk-fine{text-align:center;color:var(--dk-muted);font-size:.78rem;margin-top:10px}
@media (max-width:520px){.dk{padding:20px 16px}.dk-line{grid-template-columns:56px minmax(0,1fr) auto;grid-template-areas:"img info total" "img qty rm";gap:8px 12px}.dk-img{width:56px;height:56px}.dk-seg{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.dk-fill,.dk-btn,.dk-input,.dk-opt{transition:none}}
JavaScript
// Cart: quantity steppers, remove buttons and totals for every [data-cart] on the page. Prices come from
// data-price on each [data-line] and are summed in cents. Shipping is data-ship unless the subtotal reaches
// data-free-over, and [data-free-msg] / [data-free-bar] show how far off free shipping is. [data-badge]
// gets the item count. Changes are announced through the cart's polite [data-announce] region.
document.querySelectorAll('[data-cart]').forEach(function (cart) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var freeOver = cart.dataset.freeOver ? cents(cart.dataset.freeOver) : Infinity;
  var fee = cents(cart.dataset.ship || 0);
  var status = cart.querySelector('[data-announce]');
  function put(sel, text) { cart.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = 0, count = 0;
    cart.querySelectorAll('[data-line]').forEach(function (line) {
      var input = line.querySelector('[data-qty]'), min = +input.min || 1, max = +input.max || 99;
      var qty = Math.min(max, Math.max(min, parseInt(input.value, 10) || min));
      var total = cents(line.dataset.price) * qty;
      input.value = qty; sub += total; count += qty;
      line.querySelector('[data-line-total]').textContent = money.format(total / 100);
      line.querySelector('[data-dec]').setAttribute('aria-disabled', qty <= min);
      line.querySelector('[data-inc]').setAttribute('aria-disabled', qty >= max);
    });
    var ship = sub === 0 || sub >= freeOver ? 0 : fee;
    put('[data-subtotal]', money.format(sub / 100));
    put('[data-shipping]', ship ? money.format(ship / 100) : 'Free');
    put('[data-total]', money.format((sub + ship) / 100));
    put('[data-count]', count + (count === 1 ? ' item' : ' items'));
    put('[data-badge]', String(count));
    put('[data-free-msg]', sub >= freeOver ? 'You have unlocked free shipping.' : 'Spend ' + money.format((freeOver - sub) / 100) + ' more for free shipping.');
    cart.querySelectorAll('[data-free-bar]').forEach(function (bar) {
      var pct = Math.min(100, Math.round(sub / freeOver * 100));
      bar.style.width = pct + '%';
      bar.parentElement.setAttribute('aria-valuenow', pct);
    });
    cart.querySelectorAll('[data-filled]').forEach(function (el) { el.hidden = !count; });
    cart.querySelectorAll('[data-empty]').forEach(function (el) { el.hidden = !!count; });
    return { sub: sub, count: count };
  }
  function announce(text) { if (status) status.textContent = text; }
  cart.addEventListener('click', function (e) {
    var btn = e.target.closest('[data-inc],[data-dec],[data-remove]');
    if (!btn) return;
    var line = btn.closest('[data-line]'), name = line.dataset.name, input = line.querySelector('[data-qty]');
    if (btn.hasAttribute('data-remove')) {
      var next = line.nextElementSibling || line.previousElementSibling;
      line.remove();
      var r = render();
      announce('Removed ' + name + '. ' + (r.count ? 'Subtotal ' + money.format(r.sub / 100) + '.' : 'Your cart is empty.'));
      var target = r.count ? next.querySelector('[data-remove]') : cart.querySelector('[data-empty] [tabindex="-1"]');
      if (target) target.focus();
      return;
    }
    if (btn.getAttribute('aria-disabled') === 'true') return;
    input.value = (parseInt(input.value, 10) || 1) + (btn.hasAttribute('data-inc') ? 1 : -1);
    var s = render();
    announce(name + ', quantity ' + input.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  cart.addEventListener('change', function (e) {
    if (!e.target.matches('[data-qty]')) return;
    var s = render();
    announce(e.target.closest('[data-line]').dataset.name + ', quantity ' + e.target.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  render();
});

// Checkout form (demo): nothing is sent and no card is charged. On submit every field is checked with the
// browser's own validity rules; a failing field gets aria-invalid and the message from its data-missing or
// data-bad attribute in the element with id "<field id>-err" (already in its aria-describedby), and focus
// moves to the first problem. [data-ship-option] radios update shipping and the total in the summary.
document.querySelectorAll('form[data-checkout]').forEach(function (form) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var fmt = function (c) { return money.format(c / 100); };
  var rate = parseFloat(form.dataset.tax || 0), status = form.querySelector('[data-status]');
  function put(sel, text) { form.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = cents(form.dataset.subtotal), opt = form.querySelector('[data-ship-option]:checked');
    var ship = opt ? cents(opt.dataset.price) : 0, tax = Math.round(sub * rate / 100), total = sub + ship + tax;
    put('[data-subtotal]', fmt(sub)); put('[data-tax]', fmt(tax)); put('[data-total]', fmt(total));
    put('[data-shipping]', ship ? fmt(ship) : 'Free');
    if (opt) put('[data-ship-label]', opt.dataset.label);
    return total;
  }
  function check(field) {
    var v = field.validity, msg = v.valid ? '' : v.valueMissing ? field.dataset.missing : (field.dataset.bad || field.validationMessage);
    var out = document.getElementById(field.id + '-err');
    field.classList.toggle('is-invalid', !!msg);
    if (msg) field.setAttribute('aria-invalid', 'true'); else field.removeAttribute('aria-invalid');
    if (out) out.textContent = msg || '';
    return !msg;
  }
  form.noValidate = true;
  form.addEventListener('change', function (e) {
    if (!e.target.matches('[data-ship-option]')) return;
    var total = render();
    if (status) status.textContent = e.target.dataset.label + ' selected. Total ' + fmt(total) + '.';
  });
  form.addEventListener('input', function (e) { if (e.target.hasAttribute('aria-invalid')) check(e.target); });
  form.addEventListener('submit', function (e) {
    e.preventDefault(); // demo only: nothing is sent anywhere
    var bad = Array.prototype.filter.call(form.elements, function (f) { return f.willValidate && f.id && !check(f); });
    if (bad.length) { if (status) status.textContent = ''; bad[0].focus(); return; }
    if (status) status.textContent = 'Demo order placed. Nothing was charged: this form is not connected to a payment provider.';
  });
  render();
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="demo-row">
<div><span class="lbl">Dark cart</span>
<section class="card dk" data-bs-theme="dark" data-cart data-free-over="300" data-ship="12.00" aria-labelledby="dk-title"><div class="card-body">
  <div class="dk-head"><h2 class="dk-title" id="dk-title">Your cart</h2><span class="dk-count" data-count>2 items</span></div>
  <div data-filled>
    <div class="dk-free"><p id="dk-free" data-free-msg>You have unlocked free shipping.</p><div class="progress" role="progressbar" aria-labelledby="dk-free" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"><div class="progress-bar" data-free-bar style="width:100%"></div></div></div>
    <ul class="list-group">
      <li class="list-group-item dk-line" data-line data-name="Studio monitor headphones" data-price="189.00">
        <img class="dk-img" src="https://images.unsplash.com/photo-1583394838336-acd977736f90?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="dk-info"><h3 class="dk-name">Studio monitor headphones</h3><p class="dk-meta">Graphite · Wired and wireless · $189.00</p></div>
        <div class="dk-qty"><label class="form-label d-block" for="dk-q1">Qty<span class="visually-hidden"> of Studio monitor headphones</span></label><div class="input-group"><button class="btn" type="button" data-dec aria-label="Decrease quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input class="form-control" id="dk-q1" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button class="btn" type="button" data-inc aria-label="Increase quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="dk-total" data-line-total>$189.00</p>
        <button class="btn btn-link dk-remove" type="button" data-remove>Remove<span class="visually-hidden"> Studio monitor headphones</span></button>
      </li>
      <li class="list-group-item dk-line" data-line data-name="Field watch" data-price="149.00">
        <img class="dk-img" src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="dk-info"><h3 class="dk-name">Field watch</h3><p class="dk-meta">White · 38 mm · $149.00</p></div>
        <div class="dk-qty"><label class="form-label d-block" for="dk-q2">Qty<span class="visually-hidden"> of Field watch</span></label><div class="input-group"><button class="btn" type="button" data-dec aria-label="Decrease quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input class="form-control" id="dk-q2" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button class="btn" type="button" data-inc aria-label="Increase quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="dk-total" data-line-total>$149.00</p>
        <button class="btn btn-link dk-remove" type="button" data-remove>Remove<span class="visually-hidden"> Field watch</span></button>
      </li>
    </ul>
    <dl class="dk-rows">
      <div class="dk-row"><dt>Subtotal</dt><dd data-subtotal>$338.00</dd></div>
      <div class="dk-row"><dt>Shipping</dt><dd data-shipping>Free</dd></div>
    </dl>
    <dl class="dk-sum"><dt>Total</dt><dd data-total>$338.00</dd></dl>
    <a class="btn btn-primary" href="#"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Checkout</a>
  </div>
  <div class="dk-empty" data-empty hidden><h3 class="dk-empty-title" tabindex="-1">Your cart is empty</h3><p>Nothing here yet. The new monitor range is in stock.</p><a class="btn btn-primary" href="#">Browse headphones</a></div>
  <p class="visually-hidden" role="status" data-announce></p>
</div></section>
</div>
<div><span class="lbl">Dark express checkout</span>
<form class="card dk" data-bs-theme="dark" action="#" method="post" data-checkout data-subtotal="129.00" aria-labelledby="dk-x-title"><div class="card-body">
  <h2 class="dk-title" id="dk-x-title">Express checkout</h2>
  <div class="dk-product"><img src="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=112&h=112&fit=crop&auto=format&q=70" alt="" width="56" height="56"><div><p class="dk-name">Travel headphones</p><p class="dk-meta">Matte black · Qty 1</p></div><p class="dk-price" data-subtotal>$129.00</p></div>
  <div class="mb-3"><label class="form-label" for="dk-email">Email</label><input class="form-control" id="dk-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required aria-describedby="dk-email-err" data-missing="Enter your email address." data-bad="Enter an email address like [email protected]."><p class="invalid-feedback" id="dk-email-err"></p></div>
  <fieldset class="dk-set"><legend>Delivery</legend><div class="dk-seg"><label class="form-check dk-opt"><input class="form-check-input" type="radio" name="delivery" value="std" data-ship-option data-label="Standard" data-price="9.00" checked><strong>Standard · $9.00</strong><span>4 to 6 days</span></label><label class="form-check dk-opt"><input class="form-check-input" type="radio" name="delivery" value="exp" data-ship-option data-label="Express" data-price="19.00"><strong>Express · $19.00</strong><span>1 to 2 days</span></label></div></fieldset>
  <div class="alert"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 3v6c0 4.5-3.4 8-8 9-4.6-1-8-4.5-8-9V6z"/><path d="M12 8v4M12 16h.01"/></svg><p>Demo only. These card fields are not connected to a payment provider and nothing is charged.</p></div>
  <div class="mb-3"><label class="form-label" for="dk-card">Card number</label><input class="form-control" id="dk-card" type="text" autocomplete="cc-number" inputmode="numeric" pattern="[0-9 ]{13,23}" placeholder="1234 1234 1234 1234" required aria-describedby="dk-card-err" data-missing="Enter a card number." data-bad="Enter the 13 to 19 digits on the front of the card."><p class="invalid-feedback" id="dk-card-err"></p></div>
  <div class="row g-3"><div class="col-6"><div class=""><label class="form-label" for="dk-exp">Expiry date</label><input class="form-control" id="dk-exp" type="text" autocomplete="cc-exp" inputmode="numeric" pattern="(0[1-9]|1[0-2]) ?/ ?[0-9]{2}" placeholder="MM / YY" required aria-describedby="dk-exp-err" data-missing="Enter the expiry date." data-bad="Use the format MM / YY."><p class="invalid-feedback" id="dk-exp-err"></p></div></div><div class="col-6"><div class=""><label class="form-label" for="dk-cvc">Security code</label><input class="form-control" id="dk-cvc" type="text" autocomplete="cc-csc" inputmode="numeric" pattern="[0-9]{3,4}" required aria-describedby="dk-cvc-hint dk-cvc-err" data-missing="Enter the security code." data-bad="Enter the 3 or 4 digit code."><p class="form-text" id="dk-cvc-hint">3 or 4 digits on the back.</p><p class="invalid-feedback" id="dk-cvc-err"></p></div></div></div>
  <button class="btn btn-primary" type="submit"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Place demo order · <span data-total>$138.00</span></button>
  <p class="dk-fine">Includes <span data-shipping>$9.00</span> delivery. No payment is taken.</p>
  <p class="dk-status" role="status" data-status></p>
</div></form>
</div>
</div>
CSS
/* Bootstrap's dark colour mode (data-bs-theme="dark") on .card, with .list-group, .input-group steppers, .progress,
   .form-control, .form-check, .alert and .btn retuned through Bootstrap's CSS variables. Borders keep 3:1 and grey
   text 4.5:1 on the dark cards. The card fields are a front-end mock with no name attribute. */
.dk{--dk-bg:#121821;--dk-field:#0b1019;--dk-ink:#e8edf5;--dk-muted:#9aa4b5;--dk-line:#263040;--dk-edge:#6b7588;--dk-accent:#a3e635;--dk-accent-dark:#bef264;--dk-on-accent:#0b1019;
  --dk-ring:rgba(163,230,53,.3);--dk-error:#f87171;--dk-ok:#4ade80;
  --bs-card-bg:var(--dk-bg);--bs-card-border-color:var(--dk-line);--bs-card-border-radius:20px;--bs-card-spacer-x:24px;--bs-card-spacer-y:24px;
  --bs-body-bg:var(--dk-field);--bs-body-color:var(--dk-ink);--bs-border-color:var(--dk-edge);--bs-border-radius:10px;--bs-secondary-color:var(--dk-muted);
  --bs-form-invalid-border-color:var(--dk-error);--bs-form-invalid-color:var(--dk-error);
  color:var(--dk-ink);font-size:.93rem;line-height:1.45;box-shadow:0 30px 60px -30px rgba(0,0,0,.8)}
:where(.dk) :is(p,h2,h3,dl,dd,ul){margin:0}
.dk .btn:focus-visible,.dk a:focus-visible,.dk-empty-title:focus-visible{outline:2px solid var(--dk-accent);outline-offset:2px;box-shadow:none}
.dk-empty-title:focus:not(:focus-visible){outline:none}
.dk-head{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.dk-title{font-size:1.25rem;font-weight:700;letter-spacing:-.01em}
.dk-count{color:var(--dk-muted);font-size:.86rem}
.dk-free{margin:16px 0 4px;padding:12px 14px;border-radius:12px;background:#18202c;font-size:.85rem;font-weight:600}
.dk-free p{display:flex;gap:8px;align-items:center}
.dk-free p::before{content:"";width:8px;height:8px;border-radius:50%;background:var(--dk-accent);box-shadow:0 0 10px var(--dk-accent)}
.dk .progress{--bs-progress-height:6px;--bs-progress-bg:#2a3444;--bs-progress-bar-bg:var(--dk-accent);--bs-progress-border-radius:99px;margin-top:8px}
.dk .list-group{--bs-list-group-bg:transparent;--bs-list-group-border-width:0;--bs-list-group-item-padding-x:0;--bs-list-group-item-padding-y:16px;--bs-list-group-color:var(--dk-ink)}
.dk-line{display:grid!important;grid-template-columns:64px minmax(0,1fr) auto auto;grid-template-areas:"img info qty total" "img info qty rm";gap:4px 16px;align-items:center;border-bottom:1px solid var(--dk-line)!important;border-radius:0!important}
.dk-img{grid-area:img;width:64px;height:64px;border-radius:12px;object-fit:cover;background:#1b2330}
.dk-info{grid-area:info;min-width:0}
.dk-name{font-weight:600;line-height:1.3;font-size:1rem}
.dk-meta{font-size:.82rem;color:var(--dk-muted)}
.dk-qty{grid-area:qty}
.dk-qty .form-label{font-size:.72rem;font-weight:600;color:var(--dk-muted);margin-bottom:3px}
.dk .input-group{width:auto;flex-wrap:nowrap;border:1px solid var(--dk-edge);border-radius:10px;background:var(--dk-field)}
.dk .input-group:focus-within{border-color:var(--dk-accent);box-shadow:0 0 0 3px var(--dk-ring)}
.dk .input-group .btn{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-color:var(--dk-ink);--bs-btn-hover-color:var(--dk-ink);--bs-btn-hover-bg:#1e2733;--bs-btn-active-bg:#263040;
  width:32px;height:32px;display:grid;place-items:center;border-radius:9px!important;z-index:0}
.dk .input-group .btn[aria-disabled="true"]{color:#5d6778;cursor:not-allowed;background:none}
.dk .input-group .form-control{flex:none;width:34px;height:32px;padding:0;border:0;background:none;text-align:center;font-weight:600;box-shadow:none;-moz-appearance:textfield;appearance:textfield}
.dk .input-group .form-control::-webkit-inner-spin-button,.dk .input-group .form-control::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.dk-total{grid-area:total;text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.dk-price{text-align:right;font-weight:700;font-variant-numeric:tabular-nums}
.dk-remove{--bs-btn-padding-x:0;--bs-btn-padding-y:2px;--bs-btn-font-size:.8rem;--bs-btn-color:var(--dk-muted);--bs-btn-hover-color:var(--dk-ink);--bs-btn-active-color:var(--dk-ink);grid-area:rm;justify-self:end;text-underline-offset:3px}
.dk-rows{display:grid;gap:8px;padding:16px 0;border-bottom:1px solid var(--dk-line)}
.dk-row{display:flex;justify-content:space-between;gap:12px}
.dk-row dt{color:var(--dk-muted);font-weight:400}
.dk-row dd{font-weight:600;font-variant-numeric:tabular-nums}
.dk-sum{display:flex;justify-content:space-between;align-items:baseline;padding-top:14px;font-weight:700}
.dk-sum dd{font-size:1.4rem;font-weight:800;font-variant-numeric:tabular-nums}
.dk .btn-primary{--bs-btn-bg:var(--dk-accent);--bs-btn-border-color:var(--dk-accent);--bs-btn-color:var(--dk-on-accent);--bs-btn-hover-bg:var(--dk-accent-dark);--bs-btn-hover-border-color:var(--dk-accent-dark);
  --bs-btn-hover-color:var(--dk-on-accent);--bs-btn-active-bg:var(--dk-accent-dark);--bs-btn-active-color:var(--dk-on-accent);--bs-btn-active-border-color:var(--dk-accent-dark);
  --bs-btn-padding-y:12px;--bs-btn-font-weight:800;--bs-btn-border-radius:12px;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;margin-top:16px}
.dk .btn-primary:hover{box-shadow:0 0 24px -6px var(--dk-accent)}
.dk-empty{text-align:center;padding:36px 10px 10px}
.dk-empty-title{font-size:1.1rem;font-weight:700;margin-bottom:4px!important}
.dk-empty p{color:var(--dk-muted);margin-bottom:16px}
.dk-product{display:grid;grid-template-columns:56px minmax(0,1fr) auto;gap:12px;align-items:center;padding:14px 0 16px;border-bottom:1px solid var(--dk-line);margin-bottom:16px}
.dk-product img{width:56px;height:56px;border-radius:12px;object-fit:cover}
.dk .form-label{font-size:.84rem;font-weight:600;margin-bottom:6px}
.dk .form-control{padding:11px 13px;font-size:inherit;line-height:inherit;transition:border-color .15s,box-shadow .15s}
.dk .form-control::placeholder{color:#8b95a7}
.dk .form-control:focus{border-color:var(--dk-accent);box-shadow:0 0 0 3px var(--dk-ring);background:var(--dk-field)}
.dk .form-control.is-invalid{background-image:none;padding-right:13px}
.dk .form-text{font-size:.78rem;color:var(--dk-muted);margin-top:5px}
.dk .invalid-feedback{display:flex;gap:6px;font-size:.8rem;font-weight:600;margin-top:6px}
.dk .invalid-feedback::before{content:"!";flex:none;width:16px;height:16px;margin-top:1px;border-radius:50%;background:var(--dk-error);color:#0b1019;font-size:.7rem;font-weight:800;display:grid;place-items:center}
.dk .invalid-feedback:empty{display:none}
.dk-set{margin-bottom:16px;min-width:0}
.dk-set legend{font-size:.84rem;font-weight:600;margin-bottom:6px}
.dk-seg{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.dk-opt.form-check{display:grid;grid-template-columns:auto 1fr;gap:2px 10px;padding:11px 12px;margin:0;min-height:0;border:1px solid var(--dk-edge);border-radius:12px;background:var(--dk-field);cursor:pointer;transition:border-color .15s}
.dk-opt:hover{border-color:var(--dk-muted)}
.dk-opt:has(:checked){border-color:var(--dk-accent);box-shadow:inset 0 0 0 1px var(--dk-accent)}
.dk-opt .form-check-input{float:none;grid-row:span 2;width:16px;height:16px;margin:3px 0 0;border-color:var(--dk-edge);cursor:pointer}
.dk-opt .form-check-input:checked{background-color:var(--dk-accent);border-color:var(--dk-accent);--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%230b1019'/%3e%3c/svg%3e")}
.dk-opt .form-check-input:focus{box-shadow:none}
.dk-opt .form-check-input:focus-visible{outline:2px solid var(--dk-accent);outline-offset:2px}
.dk-opt span{font-size:.8rem;color:var(--dk-muted)}
.dk-opt strong{font-size:.9rem}
.dk .alert{--bs-alert-bg:#1d1a10;--bs-alert-border-color:#8a6d1f;--bs-alert-color:#fcd98b;--bs-alert-padding-x:12px;--bs-alert-padding-y:10px;--bs-alert-border-radius:10px;--bs-alert-margin-bottom:14px;
  display:flex;gap:10px;align-items:flex-start;border-style:dashed;font-size:.8rem}
.dk .alert svg{flex:none}
.dk-status:not(:empty){margin-top:12px;padding:10px 12px;border-radius:10px;background:#0f2a1a;color:var(--dk-ok);font-weight:600;font-size:.84rem}
.dk-fine{text-align:center;color:var(--dk-muted);font-size:.78rem;margin-top:10px!important}
@media (max-width:520px){.dk{--bs-card-spacer-x:16px;--bs-card-spacer-y:20px}.dk-line{grid-template-columns:56px minmax(0,1fr) auto;grid-template-areas:"img info total" "img qty rm";gap:8px 12px}.dk-img{width:56px;height:56px}.dk-seg{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.dk .progress-bar,.dk .btn,.dk .form-control,.dk-opt{transition:none}}
JavaScript
// Cart: quantity steppers, remove buttons and totals for every [data-cart] on the page. Prices come from
// data-price on each [data-line] and are summed in cents. Shipping is data-ship unless the subtotal reaches
// data-free-over, and [data-free-msg] / [data-free-bar] show how far off free shipping is. [data-badge]
// gets the item count. Changes are announced through the cart's polite [data-announce] region.
document.querySelectorAll('[data-cart]').forEach(function (cart) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var freeOver = cart.dataset.freeOver ? cents(cart.dataset.freeOver) : Infinity;
  var fee = cents(cart.dataset.ship || 0);
  var status = cart.querySelector('[data-announce]');
  function put(sel, text) { cart.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = 0, count = 0;
    cart.querySelectorAll('[data-line]').forEach(function (line) {
      var input = line.querySelector('[data-qty]'), min = +input.min || 1, max = +input.max || 99;
      var qty = Math.min(max, Math.max(min, parseInt(input.value, 10) || min));
      var total = cents(line.dataset.price) * qty;
      input.value = qty; sub += total; count += qty;
      line.querySelector('[data-line-total]').textContent = money.format(total / 100);
      line.querySelector('[data-dec]').setAttribute('aria-disabled', qty <= min);
      line.querySelector('[data-inc]').setAttribute('aria-disabled', qty >= max);
    });
    var ship = sub === 0 || sub >= freeOver ? 0 : fee;
    put('[data-subtotal]', money.format(sub / 100));
    put('[data-shipping]', ship ? money.format(ship / 100) : 'Free');
    put('[data-total]', money.format((sub + ship) / 100));
    put('[data-count]', count + (count === 1 ? ' item' : ' items'));
    put('[data-badge]', String(count));
    put('[data-free-msg]', sub >= freeOver ? 'You have unlocked free shipping.' : 'Spend ' + money.format((freeOver - sub) / 100) + ' more for free shipping.');
    cart.querySelectorAll('[data-free-bar]').forEach(function (bar) {
      var pct = Math.min(100, Math.round(sub / freeOver * 100));
      bar.style.width = pct + '%';
      bar.parentElement.setAttribute('aria-valuenow', pct);
    });
    cart.querySelectorAll('[data-filled]').forEach(function (el) { el.hidden = !count; });
    cart.querySelectorAll('[data-empty]').forEach(function (el) { el.hidden = !!count; });
    return { sub: sub, count: count };
  }
  function announce(text) { if (status) status.textContent = text; }
  cart.addEventListener('click', function (e) {
    var btn = e.target.closest('[data-inc],[data-dec],[data-remove]');
    if (!btn) return;
    var line = btn.closest('[data-line]'), name = line.dataset.name, input = line.querySelector('[data-qty]');
    if (btn.hasAttribute('data-remove')) {
      var next = line.nextElementSibling || line.previousElementSibling;
      line.remove();
      var r = render();
      announce('Removed ' + name + '. ' + (r.count ? 'Subtotal ' + money.format(r.sub / 100) + '.' : 'Your cart is empty.'));
      var target = r.count ? next.querySelector('[data-remove]') : cart.querySelector('[data-empty] [tabindex="-1"]');
      if (target) target.focus();
      return;
    }
    if (btn.getAttribute('aria-disabled') === 'true') return;
    input.value = (parseInt(input.value, 10) || 1) + (btn.hasAttribute('data-inc') ? 1 : -1);
    var s = render();
    announce(name + ', quantity ' + input.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  cart.addEventListener('change', function (e) {
    if (!e.target.matches('[data-qty]')) return;
    var s = render();
    announce(e.target.closest('[data-line]').dataset.name + ', quantity ' + e.target.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  render();
});

// Checkout form (demo): nothing is sent and no card is charged. On submit every field is checked with the
// browser's own validity rules; a failing field gets aria-invalid and the message from its data-missing or
// data-bad attribute in the element with id "<field id>-err" (already in its aria-describedby), and focus
// moves to the first problem. [data-ship-option] radios update shipping and the total in the summary.
document.querySelectorAll('form[data-checkout]').forEach(function (form) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var fmt = function (c) { return money.format(c / 100); };
  var rate = parseFloat(form.dataset.tax || 0), status = form.querySelector('[data-status]');
  function put(sel, text) { form.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = cents(form.dataset.subtotal), opt = form.querySelector('[data-ship-option]:checked');
    var ship = opt ? cents(opt.dataset.price) : 0, tax = Math.round(sub * rate / 100), total = sub + ship + tax;
    put('[data-subtotal]', fmt(sub)); put('[data-tax]', fmt(tax)); put('[data-total]', fmt(total));
    put('[data-shipping]', ship ? fmt(ship) : 'Free');
    if (opt) put('[data-ship-label]', opt.dataset.label);
    return total;
  }
  function check(field) {
    var v = field.validity, msg = v.valid ? '' : v.valueMissing ? field.dataset.missing : (field.dataset.bad || field.validationMessage);
    var out = document.getElementById(field.id + '-err');
    field.classList.toggle('is-invalid', !!msg);
    if (msg) field.setAttribute('aria-invalid', 'true'); else field.removeAttribute('aria-invalid');
    if (out) out.textContent = msg || '';
    return !msg;
  }
  form.noValidate = true;
  form.addEventListener('change', function (e) {
    if (!e.target.matches('[data-ship-option]')) return;
    var total = render();
    if (status) status.textContent = e.target.dataset.label + ' selected. Total ' + fmt(total) + '.';
  });
  form.addEventListener('input', function (e) { if (e.target.hasAttribute('aria-invalid')) check(e.target); });
  form.addEventListener('submit', function (e) {
    e.preventDefault(); // demo only: nothing is sent anywhere
    var bad = Array.prototype.filter.call(form.elements, function (f) { return f.willValidate && f.id && !check(f); });
    if (bad.length) { if (status) status.textContent = ''; bad[0].focus(); return; }
    if (status) status.textContent = 'Demo order placed. Nothing was charged: this form is not connected to a payment provider.';
  });
  render();
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="grid grid-cols-[minmax(0,1.35fr)_minmax(0,1fr)] items-start gap-[30px] max-[860px]:grid-cols-1">
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#98a2b3] uppercase">Dark cart</span>
<section class="rounded-[20px] border border-dk-line bg-dk-bg p-6 text-[.93rem] leading-[1.45] text-dk-ink shadow-[0_30px_60px_-30px_rgba(0,0,0,.8)] max-[520px]:px-4 max-[520px]:py-5" data-cart data-free-over="300" data-ship="12.00" aria-labelledby="dk-title">
  <div class="flex items-baseline justify-between gap-3"><h2 class="text-[1.25rem] font-bold tracking-[-.01em]" id="dk-title">Your cart</h2><span class="text-[.86rem] text-dk-muted" data-count>2 items</span></div>
  <div data-filled>
    <div class="mt-4 mb-1 rounded-xl bg-[#18202c] px-3.5 py-3 text-[.85rem] font-semibold"><p class="flex items-center gap-2 before:size-2 before:rounded-full before:bg-dk-accent before:shadow-[0_0_10px_var(--color-dk-accent)] before:content-['']" id="dk-free" data-free-msg>You have unlocked free shipping.</p><div class="mt-2 h-1.5 overflow-hidden rounded-full bg-[#2a3444]" role="progressbar" aria-labelledby="dk-free" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100"><span class="block h-full rounded-[inherit] bg-dk-accent transition-[width] duration-300 motion-reduce:transition-none" data-free-bar style="width:100%"></span></div></div>
    <ul>
      <li class="grid grid-cols-[64px_minmax(0,1fr)_auto_auto] items-center gap-x-4 gap-y-1 border-b border-dk-line py-4 [grid-template-areas:'img_info_qty_total'_'img_info_qty_rm'] max-[520px]:grid-cols-[56px_minmax(0,1fr)_auto] max-[520px]:gap-x-3 max-[520px]:gap-y-2 max-[520px]:[grid-template-areas:'img_info_total'_'img_qty_rm']" data-line data-name="Studio monitor headphones" data-price="189.00">
        <img class="[grid-area:img] size-16 rounded-xl bg-[#1b2330] object-cover max-[520px]:size-14" src="https://images.unsplash.com/photo-1583394838336-acd977736f90?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="[grid-area:info] min-w-0"><h3 class="leading-[1.3] font-semibold">Studio monitor headphones</h3><p class="text-[.82rem] text-dk-muted">Graphite · Wired and wireless · $189.00</p></div>
        <div class="[grid-area:qty]"><label class="mb-[3px] block text-[.72rem] font-semibold text-dk-muted" for="dk-q1">Qty<span class="sr-only"> of Studio monitor headphones</span></label><div class="inline-flex items-center rounded-[10px] border border-dk-edge bg-dk-field focus-within:border-dk-accent focus-within:shadow-[0_0_0_3px_rgba(163,230,53,.3)]"><button class="grid size-8 cursor-pointer place-items-center rounded-[9px] hover:bg-[#1e2733] aria-disabled:cursor-not-allowed aria-disabled:text-[#5d6778] aria-disabled:hover:bg-transparent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-dec aria-label="Decrease quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input class="h-8 w-[34px] appearance-none bg-transparent text-center font-semibold [-moz-appearance:textfield] focus:outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" id="dk-q1" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button class="grid size-8 cursor-pointer place-items-center rounded-[9px] hover:bg-[#1e2733] aria-disabled:cursor-not-allowed aria-disabled:text-[#5d6778] aria-disabled:hover:bg-transparent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-inc aria-label="Increase quantity of Studio monitor headphones"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="[grid-area:total] text-right font-bold tabular-nums" data-line-total>$189.00</p>
        <button class="[grid-area:rm] cursor-pointer justify-self-end py-0.5 text-[.8rem] text-dk-muted underline underline-offset-[3px] hover:text-dk-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-remove>Remove<span class="sr-only"> Studio monitor headphones</span></button>
      </li>
      <li class="grid grid-cols-[64px_minmax(0,1fr)_auto_auto] items-center gap-x-4 gap-y-1 border-b border-dk-line py-4 [grid-template-areas:'img_info_qty_total'_'img_info_qty_rm'] max-[520px]:grid-cols-[56px_minmax(0,1fr)_auto] max-[520px]:gap-x-3 max-[520px]:gap-y-2 max-[520px]:[grid-template-areas:'img_info_total'_'img_qty_rm']" data-line data-name="Field watch" data-price="149.00">
        <img class="[grid-area:img] size-16 rounded-xl bg-[#1b2330] object-cover max-[520px]:size-14" src="https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=128&h=128&fit=crop&auto=format&q=70" alt="" width="64" height="64">
        <div class="[grid-area:info] min-w-0"><h3 class="leading-[1.3] font-semibold">Field watch</h3><p class="text-[.82rem] text-dk-muted">White · 38 mm · $149.00</p></div>
        <div class="[grid-area:qty]"><label class="mb-[3px] block text-[.72rem] font-semibold text-dk-muted" for="dk-q2">Qty<span class="sr-only"> of Field watch</span></label><div class="inline-flex items-center rounded-[10px] border border-dk-edge bg-dk-field focus-within:border-dk-accent focus-within:shadow-[0_0_0_3px_rgba(163,230,53,.3)]"><button class="grid size-8 cursor-pointer place-items-center rounded-[9px] hover:bg-[#1e2733] aria-disabled:cursor-not-allowed aria-disabled:text-[#5d6778] aria-disabled:hover:bg-transparent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-dec aria-label="Decrease quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button><input class="h-8 w-[34px] appearance-none bg-transparent text-center font-semibold [-moz-appearance:textfield] focus:outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" id="dk-q2" type="number" inputmode="numeric" min="1" max="5" value="1" data-qty><button class="grid size-8 cursor-pointer place-items-center rounded-[9px] hover:bg-[#1e2733] aria-disabled:cursor-not-allowed aria-disabled:text-[#5d6778] aria-disabled:hover:bg-transparent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-inc aria-label="Increase quantity of Field watch"><svg width="12" height="12" viewBox="0 0 14 14" aria-hidden="true"><path d="M2 7h10M7 2v10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button></div></div>
        <p class="[grid-area:total] text-right font-bold tabular-nums" data-line-total>$149.00</p>
        <button class="[grid-area:rm] cursor-pointer justify-self-end py-0.5 text-[.8rem] text-dk-muted underline underline-offset-[3px] hover:text-dk-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="button" data-remove>Remove<span class="sr-only"> Field watch</span></button>
      </li>
    </ul>
    <dl class="grid gap-2 border-b border-dk-line py-4">
      <div class="flex justify-between gap-3"><dt class="text-dk-muted">Subtotal</dt><dd class="font-semibold tabular-nums" data-subtotal>$338.00</dd></div>
      <div class="flex justify-between gap-3"><dt class="text-dk-muted">Shipping</dt><dd class="font-semibold tabular-nums" data-shipping>Free</dd></div>
    </dl>
    <dl class="flex items-baseline justify-between pt-3.5 font-bold"><dt>Total</dt><dd class="text-[1.4rem] font-extrabold tabular-nums" data-total>$338.00</dd></dl>
    <a class="mt-4 flex w-full cursor-pointer items-center justify-center gap-2 rounded-xl bg-dk-accent px-[18px] py-[13px] font-extrabold text-dk-on-accent transition-[background-color,box-shadow] duration-150 hover:bg-dk-accent-dark hover:shadow-[0_0_24px_-6px_var(--color-dk-accent)] motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" href="#"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Checkout</a>
  </div>
  <div class="px-2.5 pt-9 pb-2.5 text-center" data-empty hidden><h3 class="mb-1 text-[1.1rem] font-bold focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" tabindex="-1">Your cart is empty</h3><p class="mb-4 text-dk-muted">Nothing here yet. The new monitor range is in stock.</p><a class="mt-4 flex w-full cursor-pointer items-center justify-center gap-2 rounded-xl bg-dk-accent px-[18px] py-[13px] font-extrabold text-dk-on-accent transition-[background-color,box-shadow] duration-150 hover:bg-dk-accent-dark hover:shadow-[0_0_24px_-6px_var(--color-dk-accent)] motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" href="#">Browse headphones</a></div>
  <p class="sr-only" role="status" data-announce></p>
</section>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#98a2b3] uppercase">Dark express checkout</span>
<form class="rounded-[20px] border border-dk-line bg-dk-bg p-6 text-[.93rem] leading-[1.45] text-dk-ink shadow-[0_30px_60px_-30px_rgba(0,0,0,.8)] max-[520px]:px-4 max-[520px]:py-5" action="#" method="post" data-checkout data-subtotal="129.00" aria-labelledby="dk-x-title">
  <h2 class="text-[1.25rem] font-bold tracking-[-.01em]" id="dk-x-title">Express checkout</h2>
  <div class="mb-4 grid grid-cols-[56px_minmax(0,1fr)_auto] items-center gap-3 border-b border-dk-line pt-3.5 pb-4"><img class="size-14 rounded-xl object-cover" src="https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=112&h=112&fit=crop&auto=format&q=70" alt="" width="56" height="56"><div><p class="leading-[1.3] font-semibold">Travel headphones</p><p class="text-[.82rem] text-dk-muted">Matte black · Qty 1</p></div><p class="text-right font-bold tabular-nums" data-subtotal>$129.00</p></div>
  <div class="mb-3.5"><label class="mb-1.5 block text-[.84rem] font-semibold" for="dk-email">Email</label><input class="block w-full rounded-[10px] border border-dk-edge bg-dk-field px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 placeholder:text-[#8b95a7] focus:border-dk-accent focus:shadow-[0_0_0_3px_rgba(163,230,53,.3)] focus:outline-none aria-invalid:border-dk-error motion-reduce:transition-none" id="dk-email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required aria-describedby="dk-email-err" data-missing="Enter your email address." data-bad="Enter an email address like [email protected]."><p class="mt-1.5 flex gap-1.5 text-[.8rem] font-semibold text-dk-error empty:hidden before:mt-px before:grid before:size-4 before:flex-none before:place-items-center before:rounded-full before:bg-dk-error before:text-[.7rem] before:font-extrabold before:text-dk-field before:content-['!']" id="dk-email-err"></p></div>
  <fieldset class="mb-4 min-w-0"><legend class="float-left mb-1.5 w-full text-[.84rem] font-semibold">Delivery</legend><div class="clear-left grid grid-cols-2 gap-2 max-[520px]:grid-cols-1"><label class="grid cursor-pointer grid-cols-[auto_1fr] gap-x-2.5 gap-y-0.5 rounded-xl border border-dk-edge bg-dk-field px-3 py-[11px] transition-colors duration-150 hover:border-dk-muted has-checked:border-dk-accent has-checked:shadow-[inset_0_0_0_1px_var(--color-dk-accent)] motion-reduce:transition-none"><input class="row-span-2 mt-[3px] size-4 cursor-pointer accent-dk-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="radio" name="delivery" value="std" data-ship-option data-label="Standard" data-price="9.00" checked><strong class="text-[.9rem]">Standard · $9.00</strong><span class="text-[.8rem] text-dk-muted">4 to 6 days</span></label><label class="grid cursor-pointer grid-cols-[auto_1fr] gap-x-2.5 gap-y-0.5 rounded-xl border border-dk-edge bg-dk-field px-3 py-[11px] transition-colors duration-150 hover:border-dk-muted has-checked:border-dk-accent has-checked:shadow-[inset_0_0_0_1px_var(--color-dk-accent)] motion-reduce:transition-none"><input class="row-span-2 mt-[3px] size-4 cursor-pointer accent-dk-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="radio" name="delivery" value="exp" data-ship-option data-label="Express" data-price="19.00"><strong class="text-[.9rem]">Express · $19.00</strong><span class="text-[.8rem] text-dk-muted">1 to 2 days</span></label></div></fieldset>
  <div class="mb-3.5 flex items-start gap-2.5 rounded-[10px] border border-dashed border-[#8a6d1f] bg-[#1d1a10] px-3 py-2.5 text-[.8rem] text-[#fcd98b] [&>svg]:flex-none"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 3v6c0 4.5-3.4 8-8 9-4.6-1-8-4.5-8-9V6z"/><path d="M12 8v4M12 16h.01"/></svg><p>Demo only. These card fields are not connected to a payment provider and nothing is charged.</p></div>
  <div class="mb-3.5"><label class="mb-1.5 block text-[.84rem] font-semibold" for="dk-card">Card number</label><input class="block w-full rounded-[10px] border border-dk-edge bg-dk-field px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 placeholder:text-[#8b95a7] focus:border-dk-accent focus:shadow-[0_0_0_3px_rgba(163,230,53,.3)] focus:outline-none aria-invalid:border-dk-error motion-reduce:transition-none" id="dk-card" type="text" autocomplete="cc-number" inputmode="numeric" pattern="[0-9 ]{13,23}" placeholder="1234 1234 1234 1234" required aria-describedby="dk-card-err" data-missing="Enter a card number." data-bad="Enter the 13 to 19 digits on the front of the card."><p class="mt-1.5 flex gap-1.5 text-[.8rem] font-semibold text-dk-error empty:hidden before:mt-px before:grid before:size-4 before:flex-none before:place-items-center before:rounded-full before:bg-dk-error before:text-[.7rem] before:font-extrabold before:text-dk-field before:content-['!']" id="dk-card-err"></p></div>
  <div class="grid grid-cols-2 gap-3"><div class=""><label class="mb-1.5 block text-[.84rem] font-semibold" for="dk-exp">Expiry date</label><input class="block w-full rounded-[10px] border border-dk-edge bg-dk-field px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 placeholder:text-[#8b95a7] focus:border-dk-accent focus:shadow-[0_0_0_3px_rgba(163,230,53,.3)] focus:outline-none aria-invalid:border-dk-error motion-reduce:transition-none" id="dk-exp" type="text" autocomplete="cc-exp" inputmode="numeric" pattern="(0[1-9]|1[0-2]) ?/ ?[0-9]{2}" placeholder="MM / YY" required aria-describedby="dk-exp-err" data-missing="Enter the expiry date." data-bad="Use the format MM / YY."><p class="mt-1.5 flex gap-1.5 text-[.8rem] font-semibold text-dk-error empty:hidden before:mt-px before:grid before:size-4 before:flex-none before:place-items-center before:rounded-full before:bg-dk-error before:text-[.7rem] before:font-extrabold before:text-dk-field before:content-['!']" id="dk-exp-err"></p></div><div class=""><label class="mb-1.5 block text-[.84rem] font-semibold" for="dk-cvc">Security code</label><input class="block w-full rounded-[10px] border border-dk-edge bg-dk-field px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 placeholder:text-[#8b95a7] focus:border-dk-accent focus:shadow-[0_0_0_3px_rgba(163,230,53,.3)] focus:outline-none aria-invalid:border-dk-error motion-reduce:transition-none" id="dk-cvc" type="text" autocomplete="cc-csc" inputmode="numeric" pattern="[0-9]{3,4}" required aria-describedby="dk-cvc-hint dk-cvc-err" data-missing="Enter the security code." data-bad="Enter the 3 or 4 digit code."><p class="mt-[5px] text-[.78rem] text-dk-muted" id="dk-cvc-hint">3 or 4 digits on the back.</p><p class="mt-1.5 flex gap-1.5 text-[.8rem] font-semibold text-dk-error empty:hidden before:mt-px before:grid before:size-4 before:flex-none before:place-items-center before:rounded-full before:bg-dk-error before:text-[.7rem] before:font-extrabold before:text-dk-field before:content-['!']" id="dk-cvc-err"></p></div></div>
  <button class="mt-4 flex w-full cursor-pointer items-center justify-center gap-2 rounded-xl bg-dk-accent px-[18px] py-[13px] font-extrabold text-dk-on-accent transition-[background-color,box-shadow] duration-150 hover:bg-dk-accent-dark hover:shadow-[0_0_24px_-6px_var(--color-dk-accent)] motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dk-accent" type="submit"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/></svg>Place demo order · <span data-total>$138.00</span></button>
  <p class="mt-2.5 text-center text-[.78rem] text-dk-muted">Includes <span data-shipping>$9.00</span> delivery. No payment is taken.</p>
  <p class="text-[.84rem] font-semibold text-dk-ok [&:not(:empty)]:mt-3 [&:not(:empty)]:rounded-[10px] [&:not(:empty)]:bg-[#0f2a1a] [&:not(:empty)]:px-3 [&:not(:empty)]:py-2.5" role="status" data-status></p>
</form>
</div>
</div>
CSS
@theme {
  --color-dk-bg: #121821;
  --color-dk-field: #0b1019;
  --color-dk-ink: #e8edf5;
  --color-dk-muted: #9aa4b5;
  --color-dk-line: #263040;
  --color-dk-edge: #6b7588;
  --color-dk-accent: #a3e635;
  --color-dk-accent-dark: #bef264;
  --color-dk-on-accent: #0b1019;
  --color-dk-error: #f87171;
  --color-dk-ok: #4ade80;
}
JavaScript
// Cart: quantity steppers, remove buttons and totals for every [data-cart] on the page. Prices come from
// data-price on each [data-line] and are summed in cents. Shipping is data-ship unless the subtotal reaches
// data-free-over, and [data-free-msg] / [data-free-bar] show how far off free shipping is. [data-badge]
// gets the item count. Changes are announced through the cart's polite [data-announce] region.
document.querySelectorAll('[data-cart]').forEach(function (cart) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var freeOver = cart.dataset.freeOver ? cents(cart.dataset.freeOver) : Infinity;
  var fee = cents(cart.dataset.ship || 0);
  var status = cart.querySelector('[data-announce]');
  function put(sel, text) { cart.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = 0, count = 0;
    cart.querySelectorAll('[data-line]').forEach(function (line) {
      var input = line.querySelector('[data-qty]'), min = +input.min || 1, max = +input.max || 99;
      var qty = Math.min(max, Math.max(min, parseInt(input.value, 10) || min));
      var total = cents(line.dataset.price) * qty;
      input.value = qty; sub += total; count += qty;
      line.querySelector('[data-line-total]').textContent = money.format(total / 100);
      line.querySelector('[data-dec]').setAttribute('aria-disabled', qty <= min);
      line.querySelector('[data-inc]').setAttribute('aria-disabled', qty >= max);
    });
    var ship = sub === 0 || sub >= freeOver ? 0 : fee;
    put('[data-subtotal]', money.format(sub / 100));
    put('[data-shipping]', ship ? money.format(ship / 100) : 'Free');
    put('[data-total]', money.format((sub + ship) / 100));
    put('[data-count]', count + (count === 1 ? ' item' : ' items'));
    put('[data-badge]', String(count));
    put('[data-free-msg]', sub >= freeOver ? 'You have unlocked free shipping.' : 'Spend ' + money.format((freeOver - sub) / 100) + ' more for free shipping.');
    cart.querySelectorAll('[data-free-bar]').forEach(function (bar) {
      var pct = Math.min(100, Math.round(sub / freeOver * 100));
      bar.style.width = pct + '%';
      bar.parentElement.setAttribute('aria-valuenow', pct);
    });
    cart.querySelectorAll('[data-filled]').forEach(function (el) { el.hidden = !count; });
    cart.querySelectorAll('[data-empty]').forEach(function (el) { el.hidden = !!count; });
    return { sub: sub, count: count };
  }
  function announce(text) { if (status) status.textContent = text; }
  cart.addEventListener('click', function (e) {
    var btn = e.target.closest('[data-inc],[data-dec],[data-remove]');
    if (!btn) return;
    var line = btn.closest('[data-line]'), name = line.dataset.name, input = line.querySelector('[data-qty]');
    if (btn.hasAttribute('data-remove')) {
      var next = line.nextElementSibling || line.previousElementSibling;
      line.remove();
      var r = render();
      announce('Removed ' + name + '. ' + (r.count ? 'Subtotal ' + money.format(r.sub / 100) + '.' : 'Your cart is empty.'));
      var target = r.count ? next.querySelector('[data-remove]') : cart.querySelector('[data-empty] [tabindex="-1"]');
      if (target) target.focus();
      return;
    }
    if (btn.getAttribute('aria-disabled') === 'true') return;
    input.value = (parseInt(input.value, 10) || 1) + (btn.hasAttribute('data-inc') ? 1 : -1);
    var s = render();
    announce(name + ', quantity ' + input.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  cart.addEventListener('change', function (e) {
    if (!e.target.matches('[data-qty]')) return;
    var s = render();
    announce(e.target.closest('[data-line]').dataset.name + ', quantity ' + e.target.value + '. Subtotal ' + money.format(s.sub / 100) + '.');
  });
  render();
});

// Checkout form (demo): nothing is sent and no card is charged. On submit every field is checked with the
// browser's own validity rules; a failing field gets aria-invalid and the message from its data-missing or
// data-bad attribute in the element with id "<field id>-err" (already in its aria-describedby), and focus
// moves to the first problem. [data-ship-option] radios update shipping and the total in the summary.
document.querySelectorAll('form[data-checkout]').forEach(function (form) {
  var money = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
  var cents = function (v) { return Math.round(parseFloat(v) * 100); };
  var fmt = function (c) { return money.format(c / 100); };
  var rate = parseFloat(form.dataset.tax || 0), status = form.querySelector('[data-status]');
  function put(sel, text) { form.querySelectorAll(sel).forEach(function (el) { el.textContent = text; }); }
  function render() {
    var sub = cents(form.dataset.subtotal), opt = form.querySelector('[data-ship-option]:checked');
    var ship = opt ? cents(opt.dataset.price) : 0, tax = Math.round(sub * rate / 100), total = sub + ship + tax;
    put('[data-subtotal]', fmt(sub)); put('[data-tax]', fmt(tax)); put('[data-total]', fmt(total));
    put('[data-shipping]', ship ? fmt(ship) : 'Free');
    if (opt) put('[data-ship-label]', opt.dataset.label);
    return total;
  }
  function check(field) {
    var v = field.validity, msg = v.valid ? '' : v.valueMissing ? field.dataset.missing : (field.dataset.bad || field.validationMessage);
    var out = document.getElementById(field.id + '-err');
    field.classList.toggle('is-invalid', !!msg);
    if (msg) field.setAttribute('aria-invalid', 'true'); else field.removeAttribute('aria-invalid');
    if (out) out.textContent = msg || '';
    return !msg;
  }
  form.noValidate = true;
  form.addEventListener('change', function (e) {
    if (!e.target.matches('[data-ship-option]')) return;
    var total = render();
    if (status) status.textContent = e.target.dataset.label + ' selected. Total ' + fmt(total) + '.';
  });
  form.addEventListener('input', function (e) { if (e.target.hasAttribute('aria-invalid')) check(e.target); });
  form.addEventListener('submit', function (e) {
    e.preventDefault(); // demo only: nothing is sent anywhere
    var bad = Array.prototype.filter.call(form.elements, function (f) { return f.willValidate && f.id && !check(f); });
    if (bad.length) { if (status) status.textContent = ''; bad[0].focus(); return; }
    if (status) status.textContent = 'Demo order placed. Nothing was charged: this form is not connected to a payment provider.';
  });
  render();
});

About this set

A cart and an express checkout for dark storefronts, with a single lime accent on near-black cards. Dark cart is an audio shop cart with a glowing free-shipping message and bar, line items with dark quantity steppers and remove links, the subtotal, shipping and total, and a lime checkout button; removing everything shows an empty state. Dark express checkout is a compact buy-now card for one product: an email field, two delivery options as radio cards, a dashed demo notice, card number, expiry and security code, and a Place demo order button that shows the running total. The cart uses the same short script as the other cart sets to recalculate totals in cents, and the checkout uses the same validation script as the checkout sets, with inline messages and focus on the first problem. Borders, grey text and error red are tuned to keep their contrast on the dark background, and native controls render dark through color-scheme. The Bootstrap version switches on Bootstrap’s own dark colour mode with data-bs-theme=”dark” and retunes its variables. Use the set in dark themes, gaming and audio shops, and night-mode storefronts.

What’s included

  • Dark cart with steppers, free-shipping bar and empty state
  • Express checkout card with delivery radio cards and demo card fields
  • Running total in the pay button
  • Contrast checked for text, borders and error messages on dark cards
  • Bootstrap version uses data-bs-theme="dark"

Accessibility

Every field and quantity input has a visible label, the stepper and remove buttons are named for their product, and errors are linked with aria-describedby and marked with aria-invalid. The free-shipping bar is a labelled progressbar, changes are announced in polite status regions, and the lime focus ring stands out against the dark cards. Transitions stop under prefers-reduced-motion.

Customise it

Change --dk-bg, --dk-field, --dk-accent and --dk-on-accent on .dk; keep --dk-edge at 3:1 or more against --dk-bg so field borders stay visible. In Bootstrap the same variables feed the --bs-* overrides under data-bs-theme="dark"; in Tailwind edit the dk-* colours in @theme.