17 CSS Sticky Elements13 / 17

CSS onlyMIT licensed

CSS Sticky Checkout Order Summary Sidebar

The checkout layout that measurably reduces abandonment: form fields on the left, order summary pinned on the right, so the total never leaves the buyer's sight while they type a shipping address. One grid, one sticky aside with align-self: start, and real form semantics — labels, autocomplete tokens, focus rings — because a checkout demo without them teaches the wrong lesson. On narrow screens the summary un-sticks and stacks on top, the honest mobile translation.

Published

Live Demo
Try it

The code

<section class="cst-13" aria-label="Sticky checkout order summary demo">
  <div class="cst-13__stage" id="cst13-top">
    <header class="cst-13__mast">
      <a class="cst-13__brand" href="#cst13-top">Foundry Supply Co.</a>
      <p class="cst-13__steps"><span>Cart</span><i aria-hidden="true">→</i><strong aria-current="step">Checkout</strong><i aria-hidden="true">→</i><span>Done</span></p>
      <span class="cst-13__secure"><svg viewBox="0 0 20 20" width="13" height="13" aria-hidden="true"><rect x="4" y="9" width="12" height="8" rx="2" fill="currentColor"/><path d="M7 9V6.5a3 3 0 0 1 6 0V9" fill="none" stroke="currentColor" stroke-width="2"/></svg>256-bit TLS</span>
    </header>
    <form class="cst-13__checkout" onsubmit="return false">
      <div class="cst-13__form">
        <section>
          <h2><span>1</span>Contact</h2>
          <div class="cst-13__field"><label for="cst13-email">Email</label><input id="cst13-email" type="email" autocomplete="email" placeholder="you@example.com"></div>
        </section>
        <section>
          <h2><span>2</span>Shipping</h2>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-fn">First name</label><input id="cst13-fn" autocomplete="given-name"></div>
            <div class="cst-13__field"><label for="cst13-ln">Last name</label><input id="cst13-ln" autocomplete="family-name"></div>
          </div>
          <div class="cst-13__field"><label for="cst13-addr">Address</label><input id="cst13-addr" autocomplete="street-address" placeholder="Street and number"></div>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-city">City</label><input id="cst13-city" autocomplete="address-level2"></div>
            <div class="cst-13__field"><label for="cst13-zip">Postcode</label><input id="cst13-zip" autocomplete="postal-code" inputmode="numeric"></div>
          </div>
          <div class="cst-13__field"><label for="cst13-country">Country</label><select id="cst13-country" autocomplete="country-name"><option>United States</option><option>Canada</option><option>United Kingdom</option><option>Australia</option><option>New Zealand</option></select></div>
        </section>
        <section>
          <h2><span>3</span>Payment</h2>
          <div class="cst-13__field"><label for="cst13-card">Card number</label><input id="cst13-card" autocomplete="cc-number" inputmode="numeric" placeholder="4242 4242 4242 4242"></div>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-exp">Expiry</label><input id="cst13-exp" autocomplete="cc-exp" inputmode="numeric" placeholder="MM / YY"></div>
            <div class="cst-13__field"><label for="cst13-cvc">Security code</label><input id="cst13-cvc" autocomplete="cc-csc" inputmode="numeric" placeholder="CVC"></div>
          </div>
          <button class="cst-13__pay" type="submit">Pay $347.88</button>
          <p class="cst-13__fine">Charged in USD. The summary on the right is <code>position: sticky; top: 24px; align-self: start</code> — the total never scrolls out of sight.</p>
        </section>
      </div>
      <aside class="cst-13__summary" aria-label="Order summary">
        <h2>Order summary</h2>
        <ul>
          <li><i style="--img:url('https://images.unsplash.com/photo-1523275335684-37898b6baf30?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Atlas field watch</strong><span>38mm · sand strap</span></div><em>$189.00</em></li>
          <li><i style="--img:url('https://images.unsplash.com/photo-1553062407-98eeb64c6a62?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Canvas weekender</strong><span>Waxed olive</span></div><em>$98.00</em></li>
          <li><i style="--img:url('https://images.unsplash.com/photo-1523362628745-0c100150b504?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Trail bottle, 750ml</strong><span>Steel</span></div><em>$24.00</em></li>
        </ul>
        <div class="cst-13__promo"><label class="cst-13__vh" for="cst13-promo">Promo code</label><input id="cst13-promo" placeholder="Promo code"><button type="button">Apply</button></div>
        <dl class="cst-13__totals">
          <div><dt>Subtotal</dt><dd>$311.00</dd></div>
          <div><dt>Shipping</dt><dd>$12.00</dd></div>
          <div><dt>Tax (8%)</dt><dd>$24.88</dd></div>
          <div class="cst-13__grand"><dt>Total</dt><dd>$347.88</dd></div>
        </dl>
        <p class="cst-13__promise">Free returns · 60 days · carbon-neutral shipping</p>
      </aside>
    </form>
    <footer class="cst-13__footer"><p><strong>Foundry Supply Co.</strong> — a demo from the CodeFronts sticky-elements collection.</p><a href="#cst13-top">Back to top ↑</a></footer>
  </div>
</section>
.cst-13,
.cst-13 *,
.cst-13 *::before,
.cst-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cst-13 {
  --bg: oklch(0.972 0.004 270);
  --pane: oklch(1 0 0);
  --ink: oklch(0.23 0.015 270);
  --mut: oklch(0.51 0.012 270);
  --line: oklch(0.9 0.007 270);
  --acc: oklch(0.5 0.16 270);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.cst-13__stage {
  width: 100%;
  container: cst13/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.cst-13__mast {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  padding: 18px clamp(16px,4cqi,44px);
  border-bottom: 1px solid var(--line);
  background: var(--pane);
}

.cst-13__brand {
  font-size: 15.5px;
  font-weight: 800;
  letter-spacing: -.01em;
  color: inherit;
  text-decoration: none;
}

.cst-13__brand:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cst-13__steps {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-inline: auto;
  font-size: 12.5px;
  color: var(--mut);
}

.cst-13__steps strong {
  color: var(--ink);
  font-weight: 750;
}

.cst-13__steps i {
  font-style: normal;
  opacity: .5;
}

.cst-13__secure {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 700;
  color: oklch(0.45 0.1 155);
}

.cst-13__checkout {
  display: grid;
  grid-template-columns: minmax(0,1fr) 360px;
  gap: clamp(24px,4cqi,56px);
  align-items: start;
  padding: clamp(22px,4cqi,44px) clamp(16px,4cqi,44px);
}

.cst-13__form {
  display: grid;
  gap: 30px;
  max-width: 60ch;
}

.cst-13__form h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 16.5px;
  letter-spacing: -.015em;
  font-weight: 800;
  margin-bottom: 14px;
}

.cst-13__form h2 span {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  font-size: 12px;
  color: oklch(0.99 0 0);
  background: var(--ink);
}

.cst-13__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.cst-13__field {
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
}

.cst-13__field label {
  font-size: 12px;
  font-weight: 700;
  color: var(--mut);
}

.cst-13__field input,
.cst-13__field select {
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  border-radius: 11px;
  border: 1.5px solid var(--line);
  background: var(--pane);
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  transition: border-color .15s,box-shadow .15s;
}

.cst-13__field input::placeholder {
  color: oklch(0.68 0.008 270);
}

.cst-13__field input:hover,
.cst-13__field select:hover {
  border-color: oklch(0.78 0.015 270);
}

.cst-13__field input:focus-visible,
.cst-13__field select:focus-visible {
  outline: none;
  border-color: var(--acc);
  box-shadow: 0 0 0 3px oklch(0.5 0.16 270/.16);
}

.cst-13__pay {
  width: 100%;
  min-height: 52px;
  margin-top: 6px;
  border: none;
  border-radius: 13px;
  font: inherit;
  font-size: 15px;
  font-weight: 800;
  color: oklch(0.99 0 0);
  background: var(--acc);
  cursor: pointer;
  transition: background .2s,translate .2s,box-shadow .2s;
}

.cst-13__pay:hover,
.cst-13__pay:focus-visible {
  background: color-mix(in oklch,var(--acc) 86%,black);
  translate: 0 -1px;
  box-shadow: 0 10px 24px oklch(0.5 0.16 270/.3);
}

.cst-13__pay:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cst-13__fine {
  margin-top: 12px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--mut);
}

.cst-13__fine code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .9em;
  background: oklch(0.92 0.008 270);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

.cst-13__summary {
  position: sticky;
  top: 24px;
  align-self: start;
  max-height: calc(100vh - 48px);
  max-height: calc(100svh - 48px);
  overflow: auto;
  padding: 22px;
  border-radius: 18px;
  background: var(--pane);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px oklch(0.3 0.02 270/.05);
  scrollbar-width: thin;
}

.cst-13__summary h2 {
  font-size: 15px;
  letter-spacing: -.01em;
  font-weight: 800;
  margin-bottom: 16px;
}

.cst-13__summary ul {
  list-style: none;
  display: grid;
  gap: 14px;
}

.cst-13__summary li {
  display: flex;
  align-items: center;
  gap: 12px;
}

.cst-13__summary li i {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 11px;
  background-image: var(--img,none),linear-gradient(150deg,oklch(0.86 0.03 270),oklch(0.68 0.06 270));
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line);
}

.cst-13__summary li strong {
  display: block;
  font-size: 13.5px;
  letter-spacing: -.005em;
}

.cst-13__summary li span {
  font-size: 11.5px;
  color: var(--mut);
}

.cst-13__summary li em {
  margin-left: auto;
  font-style: normal;
  font-size: 13.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.cst-13__vh {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.cst-13__promo {
  display: flex;
  gap: 8px;
  margin: 18px 0;
}

.cst-13__promo input {
  flex: 1;
  min-width: 0;
  min-height: 42px;
  padding: 0 12px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--pane);
  font: inherit;
  font-size: 13px;
  transition: border-color .15s,box-shadow .15s;
}

.cst-13__promo input:focus-visible {
  outline: none;
  border-color: var(--acc);
  box-shadow: 0 0 0 3px oklch(0.5 0.16 270/.16);
}

.cst-13__promo button {
  min-height: 42px;
  padding: 0 16px;
  border: none;
  border-radius: 10px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  background: oklch(0.93 0.008 270);
  cursor: pointer;
  transition: background .2s;
}

.cst-13__promo button:hover,
.cst-13__promo button:focus-visible {
  background: oklch(0.89 0.012 270);
}

.cst-13__promo button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

.cst-13__totals {
  display: grid;
  gap: 9px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.cst-13__totals div {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--mut);
}

.cst-13__totals dd {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--ink);
}

.cst-13__grand {
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.cst-13__grand dt {
  font-size: 14.5px !important;
  font-weight: 800;
  color: var(--ink) !important;
}

.cst-13__grand dd {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.01em;
}

.cst-13__promise {
  margin-top: 14px;
  font-size: 11.5px;
  color: var(--mut);
  text-align: center;
}

.cst-13__footer {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 26px;
  align-items: center;
  justify-content: space-between;
  padding: 24px clamp(16px,4cqi,44px) 36px;
  border-top: 1px solid var(--line);
}

.cst-13__footer p {
  font-size: 12.5px;
  color: var(--mut);
}

.cst-13__footer a {
  display: grid;
  place-items: center;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  border: 1.5px solid var(--line);
  transition: border-color .2s;
}

.cst-13__footer a:hover,
.cst-13__footer a:focus-visible {
  border-color: var(--acc);
}

.cst-13__footer a:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

@container cst13 (width < 760px) {
  .cst-13__checkout {
    grid-template-columns: 1fr;
  }

  .cst-13__summary {
    position: static;
    max-height: none;
    order: -1;
  }

  .cst-13__steps {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cst-13 * {
    transition-duration: .01ms !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Sticky Element from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Sticky Checkout Order Summary Sidebar
Source: https://codefronts.com/layouts/css-sticky-elements/css-sticky-checkout-order-summary-sidebar/

The checkout layout that measurably reduces abandonment: form fields on the left, order summary pinned on the right, so the total never leaves the buyer's sight while they type a shipping address. One grid, one sticky aside with align-self: start, and real form semantics — labels, autocomplete tokens, focus rings — because a checkout demo without them teaches the wrong lesson. On narrow screens the summary un-sticks and stacks on top, the honest mobile translation.
## HTML
```html
<section class="cst-13" aria-label="Sticky checkout order summary demo">
  <div class="cst-13__stage" id="cst13-top">
    <header class="cst-13__mast">
      <a class="cst-13__brand" href="#cst13-top">Foundry Supply Co.</a>
      <p class="cst-13__steps"><span>Cart</span><i aria-hidden="true">→</i><strong aria-current="step">Checkout</strong><i aria-hidden="true">→</i><span>Done</span></p>
      <span class="cst-13__secure"><svg viewBox="0 0 20 20" width="13" height="13" aria-hidden="true"><rect x="4" y="9" width="12" height="8" rx="2" fill="currentColor"/><path d="M7 9V6.5a3 3 0 0 1 6 0V9" fill="none" stroke="currentColor" stroke-width="2"/></svg>256-bit TLS</span>
    </header>
    <form class="cst-13__checkout" onsubmit="return false">
      <div class="cst-13__form">
        <section>
          <h2><span>1</span>Contact</h2>
          <div class="cst-13__field"><label for="cst13-email">Email</label><input id="cst13-email" type="email" autocomplete="email" placeholder="you@example.com"></div>
        </section>
        <section>
          <h2><span>2</span>Shipping</h2>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-fn">First name</label><input id="cst13-fn" autocomplete="given-name"></div>
            <div class="cst-13__field"><label for="cst13-ln">Last name</label><input id="cst13-ln" autocomplete="family-name"></div>
          </div>
          <div class="cst-13__field"><label for="cst13-addr">Address</label><input id="cst13-addr" autocomplete="street-address" placeholder="Street and number"></div>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-city">City</label><input id="cst13-city" autocomplete="address-level2"></div>
            <div class="cst-13__field"><label for="cst13-zip">Postcode</label><input id="cst13-zip" autocomplete="postal-code" inputmode="numeric"></div>
          </div>
          <div class="cst-13__field"><label for="cst13-country">Country</label><select id="cst13-country" autocomplete="country-name"><option>United States</option><option>Canada</option><option>United Kingdom</option><option>Australia</option><option>New Zealand</option></select></div>
        </section>
        <section>
          <h2><span>3</span>Payment</h2>
          <div class="cst-13__field"><label for="cst13-card">Card number</label><input id="cst13-card" autocomplete="cc-number" inputmode="numeric" placeholder="4242 4242 4242 4242"></div>
          <div class="cst-13__row">
            <div class="cst-13__field"><label for="cst13-exp">Expiry</label><input id="cst13-exp" autocomplete="cc-exp" inputmode="numeric" placeholder="MM / YY"></div>
            <div class="cst-13__field"><label for="cst13-cvc">Security code</label><input id="cst13-cvc" autocomplete="cc-csc" inputmode="numeric" placeholder="CVC"></div>
          </div>
          <button class="cst-13__pay" type="submit">Pay $347.88</button>
          <p class="cst-13__fine">Charged in USD. The summary on the right is <code>position: sticky; top: 24px; align-self: start</code> — the total never scrolls out of sight.</p>
        </section>
      </div>
      <aside class="cst-13__summary" aria-label="Order summary">
        <h2>Order summary</h2>
        <ul>
          <li><i style="--img:url('https://images.unsplash.com/photo-1523275335684-37898b6baf30?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Atlas field watch</strong><span>38mm · sand strap</span></div><em>$189.00</em></li>
          <li><i style="--img:url('https://images.unsplash.com/photo-1553062407-98eeb64c6a62?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Canvas weekender</strong><span>Waxed olive</span></div><em>$98.00</em></li>
          <li><i style="--img:url('https://images.unsplash.com/photo-1523362628745-0c100150b504?q=80&w=400&auto=format&fit=crop')"></i><div><strong>Trail bottle, 750ml</strong><span>Steel</span></div><em>$24.00</em></li>
        </ul>
        <div class="cst-13__promo"><label class="cst-13__vh" for="cst13-promo">Promo code</label><input id="cst13-promo" placeholder="Promo code"><button type="button">Apply</button></div>
        <dl class="cst-13__totals">
          <div><dt>Subtotal</dt><dd>$311.00</dd></div>
          <div><dt>Shipping</dt><dd>$12.00</dd></div>
          <div><dt>Tax (8%)</dt><dd>$24.88</dd></div>
          <div class="cst-13__grand"><dt>Total</dt><dd>$347.88</dd></div>
        </dl>
        <p class="cst-13__promise">Free returns · 60 days · carbon-neutral shipping</p>
      </aside>
    </form>
    <footer class="cst-13__footer"><p><strong>Foundry Supply Co.</strong> — a demo from the CodeFronts sticky-elements collection.</p><a href="#cst13-top">Back to top ↑</a></footer>
  </div>
</section>
```
## CSS
```css
.cst-13,
.cst-13 *,
.cst-13 *::before,
.cst-13 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cst-13 {
  --bg: oklch(0.972 0.004 270);
  --pane: oklch(1 0 0);
  --ink: oklch(0.23 0.015 270);
  --mut: oklch(0.51 0.012 270);
  --line: oklch(0.9 0.007 270);
  --acc: oklch(0.5 0.16 270);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.cst-13__stage {
  width: 100%;
  container: cst13/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.cst-13__mast {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  padding: 18px clamp(16px,4cqi,44px);
  border-bottom: 1px solid var(--line);
  background: var(--pane);
}

.cst-13__brand {
  font-size: 15.5px;
  font-weight: 800;
  letter-spacing: -.01em;
  color: inherit;
  text-decoration: none;
}

.cst-13__brand:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cst-13__steps {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-inline: auto;
  font-size: 12.5px;
  color: var(--mut);
}

.cst-13__steps strong {
  color: var(--ink);
  font-weight: 750;
}

.cst-13__steps i {
  font-style: normal;
  opacity: .5;
}

.cst-13__secure {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  font-weight: 700;
  color: oklch(0.45 0.1 155);
}

.cst-13__checkout {
  display: grid;
  grid-template-columns: minmax(0,1fr) 360px;
  gap: clamp(24px,4cqi,56px);
  align-items: start;
  padding: clamp(22px,4cqi,44px) clamp(16px,4cqi,44px);
}

.cst-13__form {
  display: grid;
  gap: 30px;
  max-width: 60ch;
}

.cst-13__form h2 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 16.5px;
  letter-spacing: -.015em;
  font-weight: 800;
  margin-bottom: 14px;
}

.cst-13__form h2 span {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  font-size: 12px;
  color: oklch(0.99 0 0);
  background: var(--ink);
}

.cst-13__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.cst-13__field {
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
}

.cst-13__field label {
  font-size: 12px;
  font-weight: 700;
  color: var(--mut);
}

.cst-13__field input,
.cst-13__field select {
  width: 100%;
  min-height: 46px;
  padding: 0 14px;
  border-radius: 11px;
  border: 1.5px solid var(--line);
  background: var(--pane);
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  transition: border-color .15s,box-shadow .15s;
}

.cst-13__field input::placeholder {
  color: oklch(0.68 0.008 270);
}

.cst-13__field input:hover,
.cst-13__field select:hover {
  border-color: oklch(0.78 0.015 270);
}

.cst-13__field input:focus-visible,
.cst-13__field select:focus-visible {
  outline: none;
  border-color: var(--acc);
  box-shadow: 0 0 0 3px oklch(0.5 0.16 270/.16);
}

.cst-13__pay {
  width: 100%;
  min-height: 52px;
  margin-top: 6px;
  border: none;
  border-radius: 13px;
  font: inherit;
  font-size: 15px;
  font-weight: 800;
  color: oklch(0.99 0 0);
  background: var(--acc);
  cursor: pointer;
  transition: background .2s,translate .2s,box-shadow .2s;
}

.cst-13__pay:hover,
.cst-13__pay:focus-visible {
  background: color-mix(in oklch,var(--acc) 86%,black);
  translate: 0 -1px;
  box-shadow: 0 10px 24px oklch(0.5 0.16 270/.3);
}

.cst-13__pay:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.cst-13__fine {
  margin-top: 12px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--mut);
}

.cst-13__fine code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: .9em;
  background: oklch(0.92 0.008 270);
  padding: 1px 5px;
  border-radius: 4px;
  color: var(--ink);
}

.cst-13__summary {
  position: sticky;
  top: 24px;
  align-self: start;
  max-height: calc(100vh - 48px);
  max-height: calc(100svh - 48px);
  overflow: auto;
  padding: 22px;
  border-radius: 18px;
  background: var(--pane);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px oklch(0.3 0.02 270/.05);
  scrollbar-width: thin;
}

.cst-13__summary h2 {
  font-size: 15px;
  letter-spacing: -.01em;
  font-weight: 800;
  margin-bottom: 16px;
}

.cst-13__summary ul {
  list-style: none;
  display: grid;
  gap: 14px;
}

.cst-13__summary li {
  display: flex;
  align-items: center;
  gap: 12px;
}

.cst-13__summary li i {
  flex: none;
  width: 52px;
  height: 52px;
  border-radius: 11px;
  background-image: var(--img,none),linear-gradient(150deg,oklch(0.86 0.03 270),oklch(0.68 0.06 270));
  background-size: cover;
  background-position: center;
  border: 1px solid var(--line);
}

.cst-13__summary li strong {
  display: block;
  font-size: 13.5px;
  letter-spacing: -.005em;
}

.cst-13__summary li span {
  font-size: 11.5px;
  color: var(--mut);
}

.cst-13__summary li em {
  margin-left: auto;
  font-style: normal;
  font-size: 13.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.cst-13__vh {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.cst-13__promo {
  display: flex;
  gap: 8px;
  margin: 18px 0;
}

.cst-13__promo input {
  flex: 1;
  min-width: 0;
  min-height: 42px;
  padding: 0 12px;
  border-radius: 10px;
  border: 1.5px solid var(--line);
  background: var(--pane);
  font: inherit;
  font-size: 13px;
  transition: border-color .15s,box-shadow .15s;
}

.cst-13__promo input:focus-visible {
  outline: none;
  border-color: var(--acc);
  box-shadow: 0 0 0 3px oklch(0.5 0.16 270/.16);
}

.cst-13__promo button {
  min-height: 42px;
  padding: 0 16px;
  border: none;
  border-radius: 10px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ink);
  background: oklch(0.93 0.008 270);
  cursor: pointer;
  transition: background .2s;
}

.cst-13__promo button:hover,
.cst-13__promo button:focus-visible {
  background: oklch(0.89 0.012 270);
}

.cst-13__promo button:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 2px;
}

.cst-13__totals {
  display: grid;
  gap: 9px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}

.cst-13__totals div {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: var(--mut);
}

.cst-13__totals dd {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--ink);
}

.cst-13__grand {
  padding-top: 10px;
  border-top: 1px solid var(--line);
}

.cst-13__grand dt {
  font-size: 14.5px !important;
  font-weight: 800;
  color: var(--ink) !important;
}

.cst-13__grand dd {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.01em;
}

.cst-13__promise {
  margin-top: 14px;
  font-size: 11.5px;
  color: var(--mut);
  text-align: center;
}

.cst-13__footer {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 26px;
  align-items: center;
  justify-content: space-between;
  padding: 24px clamp(16px,4cqi,44px) 36px;
  border-top: 1px solid var(--line);
}

.cst-13__footer p {
  font-size: 12.5px;
  color: var(--mut);
}

.cst-13__footer a {
  display: grid;
  place-items: center;
  min-height: 44px;
  padding: 0 14px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  border: 1.5px solid var(--line);
  transition: border-color .2s;
}

.cst-13__footer a:hover,
.cst-13__footer a:focus-visible {
  border-color: var(--acc);
}

.cst-13__footer a:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

@container cst13 (width < 760px) {
  .cst-13__checkout {
    grid-template-columns: 1fr;
  }

  .cst-13__summary {
    position: static;
    max-height: none;
    order: -1;
  }

  .cst-13__steps {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cst-13 * {
    transition-duration: .01ms !important;
  }
}
```

How this works

Grid version of demo 05's physics. The two-column shell is one declaration, and the aside needs align-self:start for exactly the same reason flexbox needed flex-start — a grid area stretches to the row's full height by default, which erases the travel range sticky needs:

.checkout{ display:grid; gap:clamp(24px,4vw,56px);
           grid-template-columns:minmax(0,1fr) 360px; }
.summary { position:sticky; top:24px; align-self:start;
           max-height:calc(100svh - 48px); overflow:auto; }

Why sticky instead of letting the summary scroll away? Checkout is where buyers second-guess: the moment shipping cost or total is off-screen, a meaningful slice of them scroll back up to re-check — and every scroll-up is an exit ramp. Pinning the summary keeps price, line items and the promo field in the same glance as the card form. The max-height + overflow:auto seatbelt matters more here than anywhere: long carts make tall summaries, and a summary taller than the viewport would pin uselessly (you'd only ever see its middle).

The form side carries the production details worth copying: every input has a real <label>, autocomplete tokens (email, postal-code, cc-number…) so browsers autofill correctly, inputmode='numeric' on card fields for the right mobile keyboard, and focus styles that ride on :focus-visible box-shadows instead of stripped outlines. None of it is sticky-specific; all of it is why this layout converts.

Make it yours

  • Column width: the 360px summary track. Below ~320px line items start truncating; above 420px the form feels cramped — tune with the gap together.
  • Mobile order: this demo stacks summary-first via order:-1 in the container query, matching Shopify. For summary-last, delete one line.
  • Sticky offset: top:24px assumes no page header; under a sticky store header use top:calc(var(--headerH) + 16px) — demo 04's handshake again.
  • Collapsible mobile summary: wrap the line items in <details> with the total in the <summary> so small screens show 'Total $347.88 ▸' — expandable without JS.

Gotchas — read before shipping

  • Grid stretch is the silent killer again: without align-self:start the summary is exactly as tall as the form column and 'sticky' does visibly nothing.
  • Don't make the PAYMENT column sticky too — two independently-scrolling sticky columns fight for the wheel event and feel broken. Pin the short column only.
  • minmax(0,1fr) on the form track is required: a plain 1fr lets a long autofilled email address blow the column out past the gap.
  • Sticky summaries hide footers on short viewports: the max-height cap plus internal scroll keeps the Pay button reachable even at 600px tall.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

Sticky-in-grid works everywhere since 2017 (with align-self). Floor comes from oklch()/color-mix() and container queries; swap for hex + media queries to reach 2018 engines.

Techniques used in this demo

Search CodeFronts

Loading…