Open demo

CSS code

HTML
<div><span class="lbl">Checkout with :has(), :user-invalid and a sticky summary</span>
<div class="mo">
  <div class="mo-layout">
    <form class="mo-card mo-form" method="dialog" aria-labelledby="mo-title">
      <h2 class="mo-h" id="mo-title">Where to?</h2>
      <p class="mo-sub">Every piece is packed by hand in recycled paper. All fields are required.</p>
      <div class="mo-fields">
          <div class="mo-field mo-full"><label class="mo-label" for="mo-email">Email<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="mo-input" id="mo-email" name="email" type="email" autocomplete="email" required aria-describedby="mo-email-hint"><p class="mo-hint" id="mo-email-hint"><span>For your receipt.</span><b>Enter an email address like [email protected].</b></p></div>
          <div class="mo-field mo-full"><label class="mo-label" for="mo-name">Full name<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="mo-input" id="mo-name" name="name" type="text" autocomplete="shipping name" required aria-describedby="mo-name-hint"><p class="mo-hint" id="mo-name-hint"><span>As it should appear on the parcel.</span><b>Enter your full name.</b></p></div>
          <div class="mo-field mo-full"><label class="mo-label" for="mo-address">Street address<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="mo-input" id="mo-address" name="address" type="text" autocomplete="shipping address-line1" required aria-describedby="mo-address-hint"><p class="mo-hint" id="mo-address-hint"><span>House number and street.</span><b>Enter your street address.</b></p></div>
          <div class="mo-field"><label class="mo-label" for="mo-city">City<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="mo-input" id="mo-city" name="city" type="text" autocomplete="shipping address-level2" required aria-describedby="mo-city-hint"><p class="mo-hint" id="mo-city-hint"><span>Town or city.</span><b>Enter your city.</b></p></div>
          <div class="mo-field"><label class="mo-label" for="mo-zip">ZIP code<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="mo-input" id="mo-zip" name="zip" type="text" autocomplete="shipping postal-code" inputmode="numeric" pattern="[0-9]{5}" required aria-describedby="mo-zip-hint"><p class="mo-hint" id="mo-zip-hint"><span>5 digits, like 87501.</span><b>Enter a 5-digit ZIP code, like 87501.</b></p></div>
      </div>
      <fieldset class="mo-set"><legend>Delivery</legend>
          <div class="mo-opts">
            <label class="mo-opt"><input type="radio" id="mo-std" name="delivery" value="std" checked><strong>Standard</strong><span>4 to 6 business days</span><b>$6.00</b></label>
            <label class="mo-opt"><input type="radio" id="mo-exp" name="delivery" value="exp"><strong>Express</strong><span>2 business days</span><b>$15.00</b></label>
            <label class="mo-opt"><input type="radio" id="mo-pick" name="delivery" value="pick"><strong>Collect from the studio</strong><span>Ready Thursday · 9 Kiln Yard</span><b>Free</b></label>
          </div>
      </fieldset>
      <label class="mo-gift"><input type="checkbox" id="mo-gift" name="gift"><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="M4 11h16v10H4zM3 7h18v4H3zM12 7v14M12 7c-2-4-6-3-5 0M12 7c2-4 6-3 5 0"/></svg><span>This is a gift: wrap it and add a note (free)</span></label>
      <div class="mo-note"><label class="mo-label" for="mo-message">Gift note (optional)</label><textarea class="mo-input" id="mo-message" name="message" maxlength="200" aria-describedby="mo-message-hint"></textarea><p class="mo-hint" id="mo-message-hint">Up to 200 characters, handwritten on a card.</p></div>
      <div class="mo-actions"><button class="mo-btn" type="submit">Continue to payment</button><p class="mo-ready"><span class="is-no">Fill in every field to continue.</span><span class="is-yes">All set.</span></p></div>
    </form>
    <aside class="mo-card mo-sum" aria-labelledby="mo-sum-title">
      <div class="mo-sum-in">
      <h2 id="mo-sum-title">Order summary</h2>
      <ul class="mo-items">
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Moon vase</p><p class="mo-meta">Chalk glaze · 24 cm</p></div><p class="mo-price">$58.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Tide side plates, set of 4</p><p class="mo-meta">Blue slip</p></div><p class="mo-price">$64.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Handwoven flat rug</p><p class="mo-meta">Sand · 160 × 230 cm</p></div><p class="mo-price">$240.00</p></li>
      </ul>
      <div>
      <dl class="mo-rows">
        <div class="mo-row"><dt>Subtotal</dt><dd>$362.00</dd></div>
        <div class="mo-row"><dt>Shipping</dt><dd><span class="mo-v" data-for="std">$6.00</span><span class="mo-v" data-for="exp">$15.00</span><span class="mo-v" data-for="pick">Free</span></dd></div>
      </dl>
      <dl class="mo-total"><dt>Total</dt><dd><span class="mo-v" data-for="std">$368.00</span><span class="mo-v" data-for="exp">$377.00</span><span class="mo-v" data-for="pick">$362.00</span></dd></dl>
      </div>
      </div>
    </aside>
  </div>
</div>
</div>
<div><span class="lbl">The same summary in a wide slot (container query)</span>
<div class="mo">
  <aside class="mo-card mo-sum" aria-labelledby="mo-sum2-title">
      <div class="mo-sum-in">
      <h2 id="mo-sum2-title">Order summary</h2>
      <ul class="mo-items">
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Moon vase</p><p class="mo-meta">Chalk glaze · 24 cm</p></div><p class="mo-price">$58.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Tide side plates, set of 4</p><p class="mo-meta">Blue slip</p></div><p class="mo-price">$64.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Handwoven flat rug</p><p class="mo-meta">Sand · 160 × 230 cm</p></div><p class="mo-price">$240.00</p></li>
      </ul>
      <div>
      <dl class="mo-rows">
        <div class="mo-row"><dt>Subtotal</dt><dd>$362.00</dd></div>
        <div class="mo-row"><dt>Shipping</dt><dd><span class="mo-v" data-for="std">$6.00</span><span class="mo-v" data-for="exp">$15.00</span><span class="mo-v" data-for="pick">Free</span></dd></div>
      </dl>
      <dl class="mo-total"><dt>Total</dt><dd><span class="mo-v" data-for="std">$368.00</span><span class="mo-v" data-for="exp">$377.00</span><span class="mo-v" data-for="pick">$362.00</span></dd></dl>
      </div>
      </div>
  </aside>
</div>
</div>
CSS
/* Modern-CSS checkout, no JavaScript. :has() picks the shipping card, the matching shipping and total lines and the
   gift note; :user-invalid turns hints into errors only after someone has edited a field; container queries lay
   out the page and the summary by the width of their own slot; the summary is position: sticky.
   method="dialog" keeps this demo on the page: give the form a real action and method when you wire it up. */
.mo{--mo-accent:#9c4221;--mo-accent-dark:#7f3519;--mo-ink:#2a2118;--mo-muted:#6b5e50;--mo-line:#e6ddd0;--mo-field:#8c7e6f;--mo-soft:#f7f1e8;--mo-card:#fffdf9;
  --mo-ring:rgba(156,66,33,.25);--mo-error:#b42318;--mo-ok:#2f6b3a;container-type:inline-size;color:var(--mo-ink);font-size:.93rem;line-height:1.45}
.mo .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}
.mo :where(button,input,textarea){font:inherit;color:inherit}
.mo :is(a,button):focus-visible{outline:2px solid var(--mo-accent);outline-offset:2px}
.mo-layout{display:grid;gap:24px;align-items:start}
@container (min-width:760px){.mo-layout{grid-template-columns:minmax(0,1fr) 330px}}
.mo-card{background:var(--mo-card);border:1px solid var(--mo-line);border-radius:20px;padding:26px}
.mo-h{font-family:Georgia,"Times New Roman",serif;font-weight:400;font-size:1.5rem;line-height:1.2;margin-bottom:4px}
.mo-sub{color:var(--mo-muted);margin-bottom:20px}
.mo-fields{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.mo-full{grid-column:1/-1}
.mo-label{display:flex;justify-content:space-between;font-size:.84rem;font-weight:600;margin-bottom:6px}
.mo-tick{display:none;color:var(--mo-ok)}
.mo-field:has(:user-valid) .mo-tick{display:block}
.mo-input{display:block;width:100%;padding:11px 13px;border:1px solid var(--mo-field);border-radius:12px;background:#fff;transition:border-color .15s,box-shadow .15s}
.mo-input:focus{outline:none;border-color:var(--mo-accent);box-shadow:0 0 0 3px var(--mo-ring)}
.mo-input:user-invalid{border-color:var(--mo-error);background:#fff8f7}
.mo-input:user-invalid:focus{box-shadow:0 0 0 3px rgba(180,35,24,.2)}
.mo-input:user-valid{border-color:var(--mo-ok)}
.mo-hint{font-size:.78rem;color:var(--mo-muted);margin-top:5px}
.mo-hint b{display:none;font-weight:600}
.mo-input:user-invalid~.mo-hint{color:var(--mo-error)}
.mo-input:user-invalid~.mo-hint span{display:none}
.mo-input:user-invalid~.mo-hint b{display:inline}
textarea.mo-input{min-height:84px;resize:vertical}
.mo-set{border:0;padding:0;margin:22px 0 0;min-width:0}
.mo-set legend{float:left;width:100%;font-weight:700;margin-bottom:10px;padding:0}
.mo-set legend+*{clear:left}
.mo-opts{display:grid;gap:10px}
@container (min-width:560px){.mo-opts{grid-template-columns:repeat(3,1fr)}}
.mo-opt{position:relative;display:grid;gap:2px;padding:14px 14px 14px 44px;border:1px solid var(--mo-field);border-radius:14px;background:#fff;cursor:pointer;transition:border-color .15s,background-color .15s}
.mo-opt input{position:absolute;left:14px;top:15px;width:18px;height:18px;margin:0;accent-color:var(--mo-accent);cursor:pointer}
.mo-opt input:focus-visible{outline:2px solid var(--mo-accent);outline-offset:2px}
.mo-opt:hover{border-color:var(--mo-ink)}
.mo-opt:has(:checked){border-color:var(--mo-accent);background:var(--mo-soft);box-shadow:inset 0 0 0 1px var(--mo-accent)}
.mo-opt strong{font-weight:600}
.mo-opt span{font-size:.8rem;color:var(--mo-muted)}
.mo-opt b{font-variant-numeric:tabular-nums;margin-top:4px}
.mo-gift{display:flex;gap:10px;align-items:center;margin-top:20px;padding:12px 14px;border-radius:12px;background:var(--mo-soft);cursor:pointer}
.mo-gift input{width:18px;height:18px;margin:0;accent-color:var(--mo-accent);cursor:pointer}
.mo-gift input:focus-visible{outline:2px solid var(--mo-accent);outline-offset:2px}
.mo-gift svg{color:var(--mo-accent)}
.mo-note{display:none;margin-top:12px}
.mo:has(#mo-gift:checked) .mo-note{display:block;animation:mo-in .25s ease-out}
@keyframes mo-in{from{opacity:0;transform:translateY(-4px)}}
.mo-actions{display:flex;flex-wrap:wrap;align-items:center;gap:12px 16px;margin-top:24px}
.mo-btn{padding:13px 22px;border:1.5px solid var(--mo-accent);border-radius:999px;background:var(--mo-accent);color:#fff;font-weight:700;cursor:pointer;transition:background-color .2s,color .2s}
.mo-btn:hover{background:var(--mo-accent-dark)}
.mo-form:invalid .mo-btn{background:#fff;color:var(--mo-accent);border-style:dashed}
.mo-form:invalid .mo-btn:hover{background:var(--mo-soft)}
.mo-ready{font-size:.84rem;color:var(--mo-muted)}
.mo-ready .is-yes{display:none;color:var(--mo-ok);font-weight:600}
.mo-form:valid .mo-ready .is-no{display:none}
.mo-form:valid .mo-ready .is-yes{display:inline}
/* summary: sticky, and laid out by its own width */
.mo-sum{position:sticky;top:16px;container:sum/inline-size;padding:22px}
.mo-sum-in{display:grid;gap:4px}
.mo-sum h2{font-size:1.05rem;font-weight:700;margin-bottom:6px}
.mo-items{list-style:none;display:grid;gap:12px;padding-bottom:14px;border-bottom:1px solid var(--mo-line)}
.mo-item{display:grid;grid-template-columns:52px minmax(0,1fr) auto;gap:12px;align-items:center}
.mo-item img{width:52px;height:52px;border-radius:12px;object-fit:cover;background:var(--mo-soft)}
.mo-item p:first-child{font-weight:600;font-size:.88rem;line-height:1.3}
.mo-meta{font-size:.78rem;color:var(--mo-muted)}
.mo-price{font-weight:600;font-variant-numeric:tabular-nums}
.mo-rows{display:grid;gap:8px;padding:14px 0;border-bottom:1px solid var(--mo-line)}
.mo-row{display:flex;justify-content:space-between;gap:12px}
.mo-row dt{color:var(--mo-muted)}
.mo-row dd{font-weight:600;font-variant-numeric:tabular-nums}
.mo-total{display:flex;justify-content:space-between;align-items:baseline;padding-top:14px;font-weight:700}
.mo-total dd{font-size:1.35rem;font-weight:800;font-variant-numeric:tabular-nums}
/* Standard is shown by default; :has() swaps in the value for the chosen option */
.mo-v{display:none}
.mo-v[data-for="std"]{display:inline}
.mo:has(#mo-exp:checked) .mo-v[data-for="std"],.mo:has(#mo-pick:checked) .mo-v[data-for="std"]{display:none}
.mo:has(#mo-exp:checked) .mo-v[data-for="exp"],.mo:has(#mo-pick:checked) .mo-v[data-for="pick"]{display:inline}
@container sum (min-width:520px){
  .mo-sum-in{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:0 28px}
  .mo-sum h2{grid-column:1/-1}
  .mo-items{grid-template-columns:repeat(3,minmax(0,1fr));align-content:start;border:0;padding:0}
  .mo-item{grid-template-columns:1fr;gap:6px}
  .mo-item img{width:100%;height:auto;aspect-ratio:1}
  .mo-rows{padding-top:0}
}
@media (max-width:420px){.mo-card{padding:20px 16px}.mo-fields{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.mo-input,.mo-opt,.mo-btn{transition:none}.mo:has(#mo-gift:checked) .mo-note{animation:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Checkout with :has(), :user-invalid and a sticky summary</span>
<div class="mo">
  <div class="mo-layout">
    <form class="card mo-form" method="dialog" aria-labelledby="mo-title"><div class="card-body">
      <h2 class="mo-h" id="mo-title">Where to?</h2>
      <p class="mo-sub">Every piece is packed by hand in recycled paper. All fields are required.</p>
      <div class="mo-fields">
          <div class="mo-field mo-full"><label class="form-label" for="mo-email">Email<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="form-control" id="mo-email" name="email" type="email" autocomplete="email" required aria-describedby="mo-email-hint"><p class="form-text" id="mo-email-hint"><span>For your receipt.</span><b>Enter an email address like [email protected].</b></p></div>
          <div class="mo-field mo-full"><label class="form-label" for="mo-name">Full name<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="form-control" id="mo-name" name="name" type="text" autocomplete="shipping name" required aria-describedby="mo-name-hint"><p class="form-text" id="mo-name-hint"><span>As it should appear on the parcel.</span><b>Enter your full name.</b></p></div>
          <div class="mo-field mo-full"><label class="form-label" for="mo-address">Street address<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="form-control" id="mo-address" name="address" type="text" autocomplete="shipping address-line1" required aria-describedby="mo-address-hint"><p class="form-text" id="mo-address-hint"><span>House number and street.</span><b>Enter your street address.</b></p></div>
          <div class="mo-field"><label class="form-label" for="mo-city">City<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="form-control" id="mo-city" name="city" type="text" autocomplete="shipping address-level2" required aria-describedby="mo-city-hint"><p class="form-text" id="mo-city-hint"><span>Town or city.</span><b>Enter your city.</b></p></div>
          <div class="mo-field"><label class="form-label" for="mo-zip">ZIP code<span class="mo-tick" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="form-control" id="mo-zip" name="zip" type="text" autocomplete="shipping postal-code" inputmode="numeric" pattern="[0-9]{5}" required aria-describedby="mo-zip-hint"><p class="form-text" id="mo-zip-hint"><span>5 digits, like 87501.</span><b>Enter a 5-digit ZIP code, like 87501.</b></p></div>
      </div>
      <fieldset class="mo-set"><legend>Delivery</legend>
          <div class="mo-opts">
            <label class="form-check mo-opt"><input class="form-check-input" type="radio" id="mo-std" name="delivery" value="std" checked><strong>Standard</strong><span>4 to 6 business days</span><b>$6.00</b></label>
            <label class="form-check mo-opt"><input class="form-check-input" type="radio" id="mo-exp" name="delivery" value="exp"><strong>Express</strong><span>2 business days</span><b>$15.00</b></label>
            <label class="form-check mo-opt"><input class="form-check-input" type="radio" id="mo-pick" name="delivery" value="pick"><strong>Collect from the studio</strong><span>Ready Thursday · 9 Kiln Yard</span><b>Free</b></label>
          </div>
      </fieldset>
      <label class="form-check mo-gift"><input class="form-check-input" type="checkbox" id="mo-gift" name="gift"><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="M4 11h16v10H4zM3 7h18v4H3zM12 7v14M12 7c-2-4-6-3-5 0M12 7c2-4 6-3 5 0"/></svg><span>This is a gift: wrap it and add a note (free)</span></label>
      <div class="mo-note"><label class="form-label" for="mo-message">Gift note (optional)</label><textarea class="form-control" id="mo-message" name="message" rows="3" maxlength="200" aria-describedby="mo-message-hint"></textarea><p class="form-text" id="mo-message-hint">Up to 200 characters, handwritten on a card.</p></div>
      <div class="mo-actions"><button class="btn btn-primary" type="submit">Continue to payment</button><p class="mo-ready"><span class="is-no">Fill in every field to continue.</span><span class="is-yes">All set.</span></p></div>
    </div></form>
    <aside class="card mo-sum" aria-labelledby="mo-sum-title"><div class="card-body">
      <div class="mo-sum-in">
      <h2 id="mo-sum-title">Order summary</h2>
      <ul class="mo-items">
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Moon vase</p><p class="mo-meta">Chalk glaze · 24 cm</p></div><p class="mo-price">$58.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Tide side plates, set of 4</p><p class="mo-meta">Blue slip</p></div><p class="mo-price">$64.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Handwoven flat rug</p><p class="mo-meta">Sand · 160 × 230 cm</p></div><p class="mo-price">$240.00</p></li>
      </ul>
      <div>
      <dl class="mo-rows">
        <div class="mo-row"><dt>Subtotal</dt><dd>$362.00</dd></div>
        <div class="mo-row"><dt>Shipping</dt><dd><span class="mo-v" data-for="std">$6.00</span><span class="mo-v" data-for="exp">$15.00</span><span class="mo-v" data-for="pick">Free</span></dd></div>
      </dl>
      <dl class="mo-total"><dt>Total</dt><dd><span class="mo-v" data-for="std">$368.00</span><span class="mo-v" data-for="exp">$377.00</span><span class="mo-v" data-for="pick">$362.00</span></dd></dl>
      </div>
      </div>
    </div></aside>
  </div>
</div>
</div>
<div><span class="lbl">The same summary in a wide slot (container query)</span>
<div class="mo">
  <aside class="card mo-sum" aria-labelledby="mo-sum2-title"><div class="card-body">
      <div class="mo-sum-in">
      <h2 id="mo-sum2-title">Order summary</h2>
      <ul class="mo-items">
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Moon vase</p><p class="mo-meta">Chalk glaze · 24 cm</p></div><p class="mo-price">$58.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Tide side plates, set of 4</p><p class="mo-meta">Blue slip</p></div><p class="mo-price">$64.00</p></li>
        <li class="mo-item"><img src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p>Handwoven flat rug</p><p class="mo-meta">Sand · 160 × 230 cm</p></div><p class="mo-price">$240.00</p></li>
      </ul>
      <div>
      <dl class="mo-rows">
        <div class="mo-row"><dt>Subtotal</dt><dd>$362.00</dd></div>
        <div class="mo-row"><dt>Shipping</dt><dd><span class="mo-v" data-for="std">$6.00</span><span class="mo-v" data-for="exp">$15.00</span><span class="mo-v" data-for="pick">Free</span></dd></div>
      </dl>
      <dl class="mo-total"><dt>Total</dt><dd><span class="mo-v" data-for="std">$368.00</span><span class="mo-v" data-for="exp">$377.00</span><span class="mo-v" data-for="pick">$362.00</span></dd></dl>
      </div>
      </div>
  </div></aside>
</div>
</div>
CSS
/* Bootstrap .card, .form-control, .form-check, .form-text and .btn, recoloured through Bootstrap's CSS variables,
   plus the modern CSS Bootstrap has no utilities for. No JavaScript. :has() picks the shipping card, the matching shipping and total lines and the
   gift note; :user-invalid turns hints into errors only after someone has edited a field; container queries lay
   out the page and the summary by the width of their own slot; the summary is position: sticky.
   method="dialog" keeps this demo on the page: give the form a real action and method when you wire it up. */
.mo{--mo-accent:#9c4221;--mo-accent-dark:#7f3519;--mo-ink:#2a2118;--mo-muted:#6b5e50;--mo-line:#e6ddd0;--mo-field:#8c7e6f;--mo-soft:#f7f1e8;--mo-card:#fffdf9;
  --mo-ring:rgba(156,66,33,.25);--mo-error:#b42318;--mo-ok:#2f6b3a;container-type:inline-size;color:var(--mo-ink);font-size:.93rem;line-height:1.45}
.mo :is(a,button):focus-visible{outline:2px solid var(--mo-accent);outline-offset:2px}
.mo-layout{display:grid;gap:24px;align-items:start}
@container (min-width:760px){.mo-layout{grid-template-columns:minmax(0,1fr) 330px}}
.mo-h{font-family:Georgia,"Times New Roman",serif;font-weight:400;font-size:1.5rem;line-height:1.2;margin-bottom:4px}
.mo-sub{color:var(--mo-muted);margin-bottom:20px}
.mo-fields{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.mo-full{grid-column:1/-1}
.mo-tick{display:none;color:var(--mo-ok)}
.mo-field:has(:user-valid) .mo-tick{display:block}
.mo-set{border:0;padding:0;margin:22px 0 0;min-width:0}
.mo-set legend{float:left;width:100%;font-weight:700;margin-bottom:10px;padding:0}
.mo-set legend+*{clear:left}
.mo-opts{display:grid;gap:10px}
@container (min-width:560px){.mo-opts{grid-template-columns:repeat(3,1fr)}}
.mo-opt{position:relative;display:grid;gap:2px;padding:14px 14px 14px 44px;border:1px solid var(--mo-field);border-radius:14px;background:#fff;cursor:pointer;transition:border-color .15s,background-color .15s}
.mo-opt:hover{border-color:var(--mo-ink)}
.mo-opt:has(:checked){border-color:var(--mo-accent);background:var(--mo-soft);box-shadow:inset 0 0 0 1px var(--mo-accent)}
.mo-opt strong{font-weight:600}
.mo-opt span{font-size:.8rem;color:var(--mo-muted)}
.mo-opt b{font-variant-numeric:tabular-nums;margin-top:4px}
.mo-gift{display:flex;gap:10px;align-items:center;margin-top:20px;padding:12px 14px;border-radius:12px;background:var(--mo-soft);cursor:pointer}
.mo-gift svg{color:var(--mo-accent)}
.mo-note{display:none;margin-top:12px}
.mo:has(#mo-gift:checked) .mo-note{display:block;animation:mo-in .25s ease-out}
@keyframes mo-in{from{opacity:0;transform:translateY(-4px)}}
.mo-actions{display:flex;flex-wrap:wrap;align-items:center;gap:12px 16px;margin-top:24px}
.mo-ready{font-size:.84rem;color:var(--mo-muted)}
.mo-ready .is-yes{display:none;color:var(--mo-ok);font-weight:600}
.mo-form:valid .mo-ready .is-no{display:none}
.mo-form:valid .mo-ready .is-yes{display:inline}
/* summary: sticky, and laid out by its own width */
.mo-sum{position:sticky;top:16px;container:sum/inline-size}
.mo-sum-in{display:grid;gap:4px}
.mo-sum h2{font-size:1.05rem;font-weight:700;margin-bottom:6px}
.mo-items{list-style:none;display:grid;gap:12px;padding-bottom:14px;border-bottom:1px solid var(--mo-line)}
.mo-item{display:grid;grid-template-columns:52px minmax(0,1fr) auto;gap:12px;align-items:center}
.mo-item img{width:52px;height:52px;border-radius:12px;object-fit:cover;background:var(--mo-soft)}
.mo-item p:first-child{font-weight:600;font-size:.88rem;line-height:1.3}
.mo-meta{font-size:.78rem;color:var(--mo-muted)}
.mo-price{font-weight:600;font-variant-numeric:tabular-nums}
.mo-rows{display:grid;gap:8px;padding:14px 0;border-bottom:1px solid var(--mo-line)}
.mo-row{display:flex;justify-content:space-between;gap:12px}
.mo-row dt{color:var(--mo-muted)}
.mo-row dd{font-weight:600;font-variant-numeric:tabular-nums}
.mo-total{display:flex;justify-content:space-between;align-items:baseline;padding-top:14px;font-weight:700}
.mo-total dd{font-size:1.35rem;font-weight:800;font-variant-numeric:tabular-nums}
/* Standard is shown by default; :has() swaps in the value for the chosen option */
.mo-v{display:none}
.mo-v[data-for="std"]{display:inline}
.mo:has(#mo-exp:checked) .mo-v[data-for="std"],.mo:has(#mo-pick:checked) .mo-v[data-for="std"]{display:none}
.mo:has(#mo-exp:checked) .mo-v[data-for="exp"],.mo:has(#mo-pick:checked) .mo-v[data-for="pick"]{display:inline}
@container sum (min-width:520px){
  .mo-sum-in{grid-template-columns:minmax(0,1.5fr) minmax(0,1fr);gap:0 28px}
  .mo-sum h2{grid-column:1/-1}
  .mo-items{grid-template-columns:repeat(3,minmax(0,1fr));align-content:start;border:0;padding:0}
  .mo-item{grid-template-columns:1fr;gap:6px}
  .mo-item img{width:100%;height:auto;aspect-ratio:1}
  .mo-rows{padding-top:0}
}
@media (max-width:420px){.mo .card{--bs-card-spacer-x:16px;--bs-card-spacer-y:20px}.mo-fields{grid-template-columns:1fr}}
@media (prefers-reduced-motion:reduce){.mo .form-control,.mo-opt,.mo .btn{transition:none}.mo:has(#mo-gift:checked) .mo-note{animation:none}}

.mo{--bs-body-color:var(--mo-ink);--bs-secondary-color:var(--mo-muted);--bs-border-color:var(--mo-field);--bs-border-radius:12px}
:where(.mo) :is(p,h2,dl,dd,ul){margin:0}
.mo .mo-items{padding:0}
.mo .mo-set legend{font-size:inherit;line-height:inherit}
.mo .card{--bs-card-bg:var(--mo-card);--bs-card-border-color:var(--mo-line);--bs-card-border-radius:20px;--bs-card-spacer-x:26px;--bs-card-spacer-y:26px}
.mo .card.mo-sum{--bs-card-spacer-x:22px;--bs-card-spacer-y:22px}
.mo .form-label{display:flex;justify-content:space-between;font-size:.84rem;font-weight:600;margin-bottom:6px}
.mo .form-control{padding:11px 13px;font-size:inherit;line-height:inherit;color:var(--mo-ink);transition:border-color .15s,box-shadow .15s}
.mo .form-control:focus{border-color:var(--mo-accent);box-shadow:0 0 0 3px var(--mo-ring)}
.mo .form-control:user-invalid{border-color:var(--mo-error);background:#fff8f7}
.mo .form-control:user-invalid:focus{box-shadow:0 0 0 3px rgba(180,35,24,.2)}
.mo .form-control:user-valid{border-color:var(--mo-ok)}
.mo .form-text{font-size:.78rem;color:var(--mo-muted);margin-top:5px}
.mo .form-text b{display:none;font-weight:600}
.mo .form-control:user-invalid~.form-text{color:var(--mo-error)}
.mo .form-control:user-invalid~.form-text span{display:none}
.mo .form-control:user-invalid~.form-text b{display:inline}
.mo .form-check-input{float:none;margin:0;width:18px;height:18px;border-color:var(--mo-field);cursor:pointer}
.mo .form-check-input:checked{background-color:var(--mo-accent);border-color:var(--mo-accent)}
.mo .form-check-input:focus{box-shadow:none;border-color:var(--mo-field)}
.mo .form-check-input:focus-visible{outline:2px solid var(--mo-accent);outline-offset:2px}
.mo-opt.form-check,.mo-gift.form-check{min-height:0;margin:0}
.mo-opt .form-check-input{position:absolute;left:14px;top:15px}
.mo-gift.form-check{padding:12px 14px;margin-top:20px}
.mo .btn-primary{--bs-btn-bg:var(--mo-accent);--bs-btn-border-color:var(--mo-accent);--bs-btn-hover-bg:var(--mo-accent-dark);--bs-btn-hover-border-color:var(--mo-accent-dark);
  --bs-btn-active-bg:var(--mo-accent-dark);--bs-btn-active-border-color:var(--mo-accent-dark);--bs-btn-padding-x:22px;--bs-btn-padding-y:12px;--bs-btn-font-weight:700;
  --bs-btn-border-radius:999px;--bs-btn-border-width:1.5px}
.mo-form:invalid .btn-primary{--bs-btn-bg:#fff;--bs-btn-color:var(--mo-accent);--bs-btn-hover-bg:var(--mo-soft);--bs-btn-hover-color:var(--mo-accent);border-style:dashed}
.mo .btn:focus-visible{box-shadow:none}
.mo-row dt{font-weight:400}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#665a4c] uppercase">Checkout with :has(), :user-invalid and a sticky summary</span>
<div class="group/mo @container text-[.93rem] leading-[1.45] text-mo-ink">
  <div class="grid items-start gap-6 @min-[760px]:grid-cols-[minmax(0,1fr)_330px]">
    <form class="group/form rounded-[20px] border border-mo-line bg-mo-card p-[26px] max-[420px]:px-4 max-[420px]:py-5" method="dialog" aria-labelledby="mo-title">
      <h2 class="mb-1 font-[Georgia,'Times_New_Roman',serif] text-[1.5rem] leading-[1.2] font-normal" id="mo-title">Where to?</h2>
      <p class="mb-5 text-mo-muted">Every piece is packed by hand in recycled paper. All fields are required.</p>
      <div class="grid grid-cols-2 gap-4">
          <div class="group/f col-span-2"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-email">Email<span class="hidden text-mo-ok group-has-[:user-valid]/f:block" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none" id="mo-email" name="email" type="email" autocomplete="email" required aria-describedby="mo-email-hint"><p class="mt-[5px] text-[.78rem] text-mo-muted peer-user-invalid:text-mo-error [&>b]:hidden [&>b]:font-semibold peer-user-invalid:[&>b]:inline peer-user-invalid:[&>span]:hidden" id="mo-email-hint"><span>For your receipt.</span><b>Enter an email address like [email protected].</b></p></div>
          <div class="group/f col-span-2"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-name">Full name<span class="hidden text-mo-ok group-has-[:user-valid]/f:block" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none" id="mo-name" name="name" type="text" autocomplete="shipping name" required aria-describedby="mo-name-hint"><p class="mt-[5px] text-[.78rem] text-mo-muted peer-user-invalid:text-mo-error [&>b]:hidden [&>b]:font-semibold peer-user-invalid:[&>b]:inline peer-user-invalid:[&>span]:hidden" id="mo-name-hint"><span>As it should appear on the parcel.</span><b>Enter your full name.</b></p></div>
          <div class="group/f col-span-2"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-address">Street address<span class="hidden text-mo-ok group-has-[:user-valid]/f:block" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none" id="mo-address" name="address" type="text" autocomplete="shipping address-line1" required aria-describedby="mo-address-hint"><p class="mt-[5px] text-[.78rem] text-mo-muted peer-user-invalid:text-mo-error [&>b]:hidden [&>b]:font-semibold peer-user-invalid:[&>b]:inline peer-user-invalid:[&>span]:hidden" id="mo-address-hint"><span>House number and street.</span><b>Enter your street address.</b></p></div>
          <div class="group/f col-span-1 max-[420px]:col-span-2"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-city">City<span class="hidden text-mo-ok group-has-[:user-valid]/f:block" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none" id="mo-city" name="city" type="text" autocomplete="shipping address-level2" required aria-describedby="mo-city-hint"><p class="mt-[5px] text-[.78rem] text-mo-muted peer-user-invalid:text-mo-error [&>b]:hidden [&>b]:font-semibold peer-user-invalid:[&>b]:inline peer-user-invalid:[&>span]:hidden" id="mo-city-hint"><span>Town or city.</span><b>Enter your city.</b></p></div>
          <div class="group/f col-span-1 max-[420px]:col-span-2"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-zip">ZIP code<span class="hidden text-mo-ok group-has-[:user-valid]/f:block" aria-hidden="true"><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.5l4.5 4.5L19 7.5"/></svg></span></label><input class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none" id="mo-zip" name="zip" type="text" autocomplete="shipping postal-code" inputmode="numeric" pattern="[0-9]{5}" required aria-describedby="mo-zip-hint"><p class="mt-[5px] text-[.78rem] text-mo-muted peer-user-invalid:text-mo-error [&>b]:hidden [&>b]:font-semibold peer-user-invalid:[&>b]:inline peer-user-invalid:[&>span]:hidden" id="mo-zip-hint"><span>5 digits, like 87501.</span><b>Enter a 5-digit ZIP code, like 87501.</b></p></div>
      </div>
      <fieldset class="mt-[22px] min-w-0"><legend class="float-left mb-2.5 w-full font-bold">Delivery</legend>
          <div class="clear-left grid gap-2.5 @min-[560px]:grid-cols-3">
            <label class="relative grid cursor-pointer gap-0.5 rounded-[14px] border border-mo-field bg-white py-3.5 pr-3.5 pl-11 transition-colors duration-150 hover:border-mo-ink has-checked:border-mo-accent has-checked:bg-mo-soft has-checked:shadow-[inset_0_0_0_1px_var(--color-mo-accent)] motion-reduce:transition-none"><input class="absolute top-[15px] left-3.5 size-[18px] cursor-pointer accent-mo-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-mo-accent" type="radio" id="mo-std" name="delivery" value="std" checked><strong class="font-semibold">Standard</strong><span class="text-[.8rem] text-mo-muted">4 to 6 business days</span><b class="mt-1 tabular-nums">$6.00</b></label>
            <label class="relative grid cursor-pointer gap-0.5 rounded-[14px] border border-mo-field bg-white py-3.5 pr-3.5 pl-11 transition-colors duration-150 hover:border-mo-ink has-checked:border-mo-accent has-checked:bg-mo-soft has-checked:shadow-[inset_0_0_0_1px_var(--color-mo-accent)] motion-reduce:transition-none"><input class="absolute top-[15px] left-3.5 size-[18px] cursor-pointer accent-mo-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-mo-accent" type="radio" id="mo-exp" name="delivery" value="exp"><strong class="font-semibold">Express</strong><span class="text-[.8rem] text-mo-muted">2 business days</span><b class="mt-1 tabular-nums">$15.00</b></label>
            <label class="relative grid cursor-pointer gap-0.5 rounded-[14px] border border-mo-field bg-white py-3.5 pr-3.5 pl-11 transition-colors duration-150 hover:border-mo-ink has-checked:border-mo-accent has-checked:bg-mo-soft has-checked:shadow-[inset_0_0_0_1px_var(--color-mo-accent)] motion-reduce:transition-none"><input class="absolute top-[15px] left-3.5 size-[18px] cursor-pointer accent-mo-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-mo-accent" type="radio" id="mo-pick" name="delivery" value="pick"><strong class="font-semibold">Collect from the studio</strong><span class="text-[.8rem] text-mo-muted">Ready Thursday · 9 Kiln Yard</span><b class="mt-1 tabular-nums">Free</b></label>
          </div>
      </fieldset>
      <label class="mt-5 flex cursor-pointer items-center gap-2.5 rounded-xl bg-mo-soft px-3.5 py-3 [&>svg]:text-mo-accent"><input class="size-[18px] cursor-pointer accent-mo-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-mo-accent" type="checkbox" id="mo-gift" name="gift"><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="M4 11h16v10H4zM3 7h18v4H3zM12 7v14M12 7c-2-4-6-3-5 0M12 7c2-4 6-3 5 0"/></svg><span>This is a gift: wrap it and add a note (free)</span></label>
      <div class="mt-3 hidden group-has-[#mo-gift:checked]/mo:block group-has-[#mo-gift:checked]/mo:animate-[mo-in_.25s_ease-out] motion-reduce:animate-none"><label class="mb-1.5 flex justify-between text-[.84rem] font-semibold" for="mo-message">Gift note (optional)</label><textarea class="peer block w-full rounded-xl border border-mo-field bg-white px-[13px] py-[11px] transition-[border-color,box-shadow] duration-150 focus:border-mo-accent focus:shadow-[0_0_0_3px_rgba(156,66,33,.25)] focus:outline-none user-valid:border-mo-ok user-invalid:border-mo-error user-invalid:bg-[#fff8f7] user-invalid:focus:shadow-[0_0_0_3px_rgba(180,35,24,.2)] motion-reduce:transition-none min-h-[84px] resize-y" id="mo-message" name="message" maxlength="200" aria-describedby="mo-message-hint"></textarea><p class="mt-[5px] text-[.78rem] text-mo-muted" id="mo-message-hint">Up to 200 characters, handwritten on a card.</p></div>
      <div class="mt-6 flex flex-wrap items-center gap-x-4 gap-y-3"><button class="cursor-pointer rounded-full border-[1.5px] border-mo-accent bg-mo-accent px-[22px] py-[13px] font-bold text-white transition-colors duration-200 hover:bg-mo-accent-dark group-invalid/form:border-dashed group-invalid/form:bg-white group-invalid/form:text-mo-accent group-invalid/form:hover:bg-mo-soft motion-reduce:transition-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-mo-accent" type="submit">Continue to payment</button><p class="text-[.84rem] text-mo-muted"><span class="group-valid/form:hidden">Fill in every field to continue.</span><span class="hidden font-semibold text-mo-ok group-valid/form:inline">All set.</span></p></div>
    </form>
    <aside class="sticky top-4 @container/sum rounded-[20px] border border-mo-line bg-mo-card p-[22px] max-[420px]:px-4 max-[420px]:py-5" aria-labelledby="mo-sum-title">
      <div class="grid gap-1 @min-[520px]/sum:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)] @min-[520px]/sum:gap-x-7 @min-[520px]/sum:gap-y-0">
      <h2 class="mb-1.5 text-[1.05rem] font-bold @min-[520px]/sum:col-span-full" id="mo-sum-title">Order summary</h2>
      <ul class="grid gap-3 border-b border-mo-line pb-3.5 @min-[520px]/sum:grid-cols-3 @min-[520px]/sum:content-start @min-[520px]/sum:border-0 @min-[520px]/sum:pb-0">
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Moon vase</p><p class="text-[.78rem] text-mo-muted">Chalk glaze · 24 cm</p></div><p class="font-semibold tabular-nums">$58.00</p></li>
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Tide side plates, set of 4</p><p class="text-[.78rem] text-mo-muted">Blue slip</p></div><p class="font-semibold tabular-nums">$64.00</p></li>
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Handwoven flat rug</p><p class="text-[.78rem] text-mo-muted">Sand · 160 × 230 cm</p></div><p class="font-semibold tabular-nums">$240.00</p></li>
      </ul>
      <div>
      <dl class="grid gap-2 border-b border-mo-line py-3.5 @min-[520px]/sum:pt-0">
        <div class="flex justify-between gap-3"><dt class="text-mo-muted">Subtotal</dt><dd class="font-semibold tabular-nums">$362.00</dd></div>
        <div class="flex justify-between gap-3"><dt class="text-mo-muted">Shipping</dt><dd class="font-semibold tabular-nums"><span class="inline group-has-[#mo-exp:checked]/mo:hidden group-has-[#mo-pick:checked]/mo:hidden">$6.00</span><span class="hidden group-has-[#mo-exp:checked]/mo:inline">$15.00</span><span class="hidden group-has-[#mo-pick:checked]/mo:inline">Free</span></dd></div>
      </dl>
      <dl class="flex items-baseline justify-between pt-3.5 font-bold"><dt>Total</dt><dd class="text-[1.35rem] font-extrabold tabular-nums"><span class="inline group-has-[#mo-exp:checked]/mo:hidden group-has-[#mo-pick:checked]/mo:hidden">$368.00</span><span class="hidden group-has-[#mo-exp:checked]/mo:inline">$377.00</span><span class="hidden group-has-[#mo-pick:checked]/mo:inline">$362.00</span></dd></dl>
      </div>
      </div>
    </aside>
  </div>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#665a4c] uppercase">The same summary in a wide slot (container query)</span>
<div class="group/mo @container text-[.93rem] leading-[1.45] text-mo-ink">
  <aside class="sticky top-4 @container/sum rounded-[20px] border border-mo-line bg-mo-card p-[22px] max-[420px]:px-4 max-[420px]:py-5" aria-labelledby="mo-sum2-title">
      <div class="grid gap-1 @min-[520px]/sum:grid-cols-[minmax(0,1.5fr)_minmax(0,1fr)] @min-[520px]/sum:gap-x-7 @min-[520px]/sum:gap-y-0">
      <h2 class="mb-1.5 text-[1.05rem] font-bold @min-[520px]/sum:col-span-full" id="mo-sum2-title">Order summary</h2>
      <ul class="grid gap-3 border-b border-mo-line pb-3.5 @min-[520px]/sum:grid-cols-3 @min-[520px]/sum:content-start @min-[520px]/sum:border-0 @min-[520px]/sum:pb-0">
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1612196808214-b8e1d6145a8c?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Moon vase</p><p class="text-[.78rem] text-mo-muted">Chalk glaze · 24 cm</p></div><p class="font-semibold tabular-nums">$58.00</p></li>
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1578749556568-bc2c40e68b61?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Tide side plates, set of 4</p><p class="text-[.78rem] text-mo-muted">Blue slip</p></div><p class="font-semibold tabular-nums">$64.00</p></li>
        <li class="grid grid-cols-[52px_minmax(0,1fr)_auto] items-center gap-3 @min-[520px]/sum:grid-cols-1 @min-[520px]/sum:gap-1.5"><img class="size-[52px] rounded-xl bg-mo-soft object-cover @min-[520px]/sum:aspect-square @min-[520px]/sum:h-auto @min-[520px]/sum:w-full" src="https://images.unsplash.com/photo-1600166898405-da9535204843?w=240&h=240&fit=crop&auto=format&q=70" alt="" width="52" height="52"><div><p class="text-[.88rem] leading-[1.3] font-semibold">Handwoven flat rug</p><p class="text-[.78rem] text-mo-muted">Sand · 160 × 230 cm</p></div><p class="font-semibold tabular-nums">$240.00</p></li>
      </ul>
      <div>
      <dl class="grid gap-2 border-b border-mo-line py-3.5 @min-[520px]/sum:pt-0">
        <div class="flex justify-between gap-3"><dt class="text-mo-muted">Subtotal</dt><dd class="font-semibold tabular-nums">$362.00</dd></div>
        <div class="flex justify-between gap-3"><dt class="text-mo-muted">Shipping</dt><dd class="font-semibold tabular-nums"><span class="inline group-has-[#mo-exp:checked]/mo:hidden group-has-[#mo-pick:checked]/mo:hidden">$6.00</span><span class="hidden group-has-[#mo-exp:checked]/mo:inline">$15.00</span><span class="hidden group-has-[#mo-pick:checked]/mo:inline">Free</span></dd></div>
      </dl>
      <dl class="flex items-baseline justify-between pt-3.5 font-bold"><dt>Total</dt><dd class="text-[1.35rem] font-extrabold tabular-nums"><span class="inline group-has-[#mo-exp:checked]/mo:hidden group-has-[#mo-pick:checked]/mo:hidden">$368.00</span><span class="hidden group-has-[#mo-exp:checked]/mo:inline">$377.00</span><span class="hidden group-has-[#mo-pick:checked]/mo:inline">$362.00</span></dd></dl>
      </div>
      </div>
  </aside>
</div>
</div>
CSS
@theme {
  --color-mo-accent: #9c4221;
  --color-mo-accent-dark: #7f3519;
  --color-mo-ink: #2a2118;
  --color-mo-muted: #6b5e50;
  --color-mo-line: #e6ddd0;
  --color-mo-field: #8c7e6f;
  --color-mo-soft: #f7f1e8;
  --color-mo-card: #fffdf9;
  --color-mo-error: #b42318;
  --color-mo-ok: #2f6b3a;
}
/* the gift note fades in when it is revealed */
@keyframes mo-in { from { opacity: 0; transform: translateY(-4px) } }

About this set

A checkout step for a ceramics studio that does with modern CSS what checkout templates usually do with JavaScript. The address form shows a grey hint under each field that turns into a red error only after someone has edited the field, using :user-invalid, while :user-valid adds a green tick beside the label. Delivery options are radio cards highlighted with :has(:checked), and the order summary shows the matching shipping line and total without a script: all three totals are in the markup and :has() on the wrapper displays the one for the checked option. A gift checkbox reveals a gift note field, and the Continue button is drawn as a dashed outline while the form is :invalid and fills in once it is :valid, next to a short ready message. The layout is a container query, so the form and the sticky summary sit side by side only when their wrapper is wide enough, and the summary card has its own container query that turns its item list into photo tiles in a wide slot, shown in the second demo. There is no JavaScript in any version, and the browser’s own validation still runs on submit.

What’s included

  • :user-invalid errors and :user-valid ticks after interaction
  • Shipping line and total switched by :has() with no script
  • Gift note revealed by :has(:checked)
  • Container queries for the page layout and the summary card
  • Sticky order summary

Accessibility

Every field has a visible label and a hint linked with aria-describedby, and the hint text itself becomes the error, so what is read is what is shown. Choices are native radios and a checkbox with visible focus rings, and the hidden shipping and total values use display: none so only the visible figure is read. The browser's own validation messages still appear on submit, and the reveal animation stops under prefers-reduced-motion.

Customise it

Change --mo-accent, --mo-line and --mo-field on .mo, the 760px container breakpoint on .mo-layout and the 520px one in the summary's @container sum rule. Add a shipping option by adding a radio and a matching data-for value in both the shipping and total lines. In Tailwind edit the mo-* colours in @theme and the @min-[760px] and @min-[520px]/sum variants.