36 Tailwind CSS Modals
08 / 36

All 36 in 36 Tailwind CSS Modals

  1. 01Tailwind Login / Signup Modal with Native Dialog & Starting-Style
  2. 02Tailwind Password Reset Modal with In-Place Success State
  3. 03Tailwind Age Verification Gate with :has() and Zero JavaScript
  4. 04Tailwind Paywall Modal with Masked Article Fade
  5. 05Tailwind Product Quick-View Modal with has-checked Variant Chips
  6. 06Tailwind Shopping Cart Slide-Over Drawer with Dialog
  7. 07Tailwind Timed Newsletter Promo Popup with Frequency Cap
  8. 08Tailwind Size Guide Overlay with Zero-JS cm/inch Toggle
  9. 09Tailwind Zero-JS Lightbox Gallery with the target Variant
  10. 10Tailwind Video Trailer Modal with Aspect-Locked Iframe
  11. 11Tailwind Destructive Delete Modal with Type-to-Confirm Gate
  12. 12Tailwind Success / Error Result Modal with Drawn SVG Icon
  13. 13Tailwind GDPR Cookie Consent Banner with details Disclosure
  14. 14Tailwind Multi-Step Onboarding Modal with data-step Variants
  15. 15Tailwind Filter & Sort Drawer that Becomes a Bottom Sheet
  16. 16Tailwind Contact Support Modal with Live Validation
  17. 17Tailwind Tabbed Settings Modal with Zero-JS Radio Tabs
  18. 18Tailwind Command Palette Modal with ⌘K and Roving Selection
  19. 19Tailwind Confirm Delete Modal with Corner Slide-and-Spin Entrance
  20. 20Tailwind Login Modal with Staggered Blur-In Content Drop
  21. 21Tailwind Signup Modal with 3D Origami Fan Deploy Panels
  22. 22Tailwind Payment Checkout Modal with 3D Paper Fold Entrance
  23. 23Tailwind HIPAA Patient Consent Modal with Scroll-Gated Agreement
  24. 24Tailwind Age Verification Modal with Clip-Path Iris Aperture
  25. 25Tailwind GDPR Cookie Preferences Modal with Unfolding Category Rows
  26. 26Tailwind Video Lightbox Modal with Chromatic Glitch Materialize
  27. 27Tailwind Image Gallery Lightbox with Clip-Path Diamond Reveal
  28. 28Tailwind Add to Cart Confirmation with SVG Liquid Morph Entrance
  29. 29Tailwind Product Quick View Modal with Elastic Button Morph
  30. 30Tailwind Newsletter Signup Modal with Depth-Layer Parallax Entrance
  31. 31Tailwind Exit-Intent Discount Modal with Live Countdown and Coupon Copy
  32. 32Tailwind Restaurant Reservation Modal with Cascading Fan Panels
  33. 33Tailwind Order Success Modal with CSS-Only Confetti Burst
  34. 34Tailwind SaaS Onboarding Welcome Modal with Cascading Blur-In
  35. 35Tailwind Terms of Service Modal with Letter-Slide and Consent Gate
  36. 36Tailwind Password Reset Modal with Enumeration-Safe Confirmation
Pure TailwindMIT licensed

Tailwind Size Guide Overlay with Zero-JS cm/inch Toggle

An apparel size chart that opens from a text link and flips between centimetres and inches — no JavaScript in either interaction. Two checkbox/radio gates, read with group-has-checked: and group-has-[#tm-08-in:checked]:, do what two :has() rules did before.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

The code

<section class="tm-08 group relative w-full min-h-svh grid place-items-center bg-tm08-bg font-tm08-sans px-6 py-10">
  <div class="flex flex-col items-start gap-4 p-6 rounded-2xl bg-tm08-panel border border-tm08-edge">
    <div class="flex items-center gap-2">
      <span class="mr-1.5 text-xs uppercase tracking-[0.16em] text-tm08-mut">Size</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">S</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-white border border-tm08-acc bg-tm08-acctint">M</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">L</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">XL</span>
    </div>
    <input type="checkbox" id="tm-08-t" class="peer sr-only">
    <label for="tm-08-t" class="inline-flex items-center gap-1.5 min-h-11 px-1 text-[13px] font-semibold text-tm08-acc underline underline-offset-[3px] cursor-pointer transition-[filter] duration-200 hover:brightness-125 peer-focus-visible:outline-2 peer-focus-visible:outline-tm08-acc peer-focus-visible:outline-offset-2 peer-focus-visible:rounded-md"><span aria-hidden="true">📏</span> Size guide</label>
  </div>

  <div role="dialog" aria-modal="true" aria-labelledby="tm-08-title" class="fixed inset-0 z-10 grid place-items-center p-6 bg-tm08-scrim backdrop-blur-[4px] opacity-0 invisible pointer-events-none transition-[opacity,visibility] duration-[280ms] group-has-checked:opacity-100 group-has-checked:visible group-has-checked:pointer-events-auto motion-reduce:transition-none">
    <div class="flex flex-col w-[min(460px,94vw)] max-h-[88dvh] rounded-[18px] bg-tm08-card border border-tm08-cardedge shadow-[0_40px_90px_-30px_#000] scale-[0.96] transition-transform duration-[280ms] group-has-checked:scale-100 motion-reduce:transition-none">
      <div class="flex items-center gap-3.5 px-5 py-4 border-b border-tm08-edge">
        <h2 id="tm-08-title" class="flex-1 text-lg font-bold text-tm08-hi">Size guide</h2>
        <div class="inline-flex rounded-[9px] border border-tm08-edge overflow-hidden">
          <input type="radio" name="tm-08-u" id="tm-08-cm" checked class="peer/cm sr-only">
          <label for="tm-08-cm" class="grid place-items-center min-h-11 px-4 text-xs font-semibold text-tm08-mut bg-tm08-well cursor-pointer transition-colors duration-150 peer-checked/cm:bg-tm08-acctint peer-checked/cm:text-white peer-focus-visible/cm:outline-2 peer-focus-visible/cm:outline-tm08-acc peer-focus-visible/cm:-outline-offset-2">cm</label>
          <input type="radio" name="tm-08-u" id="tm-08-in" class="peer/in sr-only">
          <label for="tm-08-in" class="grid place-items-center min-h-11 px-4 text-xs font-semibold text-tm08-mut bg-tm08-well cursor-pointer transition-colors duration-150 peer-checked/in:bg-tm08-acctint peer-checked/in:text-white peer-focus-visible/in:outline-2 peer-focus-visible/in:outline-tm08-acc peer-focus-visible/in:-outline-offset-2">in</label>
        </div>
        <label for="tm-08-t" aria-label="Close size guide" class="grid place-items-center size-11 rounded-lg text-[22px] leading-none text-tm08-mut cursor-pointer transition-colors duration-200 hover:bg-tm08-well hover:text-white">&times;</label>
      </div>

      <div class="overflow-auto px-2 py-1.5">
        <table class="w-full border-collapse text-sm">
          <thead>
            <tr>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Size</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Chest</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Waist</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Length</th>
            </tr>
          </thead>
          <tbody class="group-has-[#tm-08-in:checked]:hidden">
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XS</th><td class="px-2.5 py-3 text-center text-tm08-ink">86</td><td class="px-2.5 py-3 text-center text-tm08-ink">71</td><td class="px-2.5 py-3 text-center text-tm08-ink">68</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">S</th><td class="px-2.5 py-3 text-center text-tm08-ink">91</td><td class="px-2.5 py-3 text-center text-tm08-ink">76</td><td class="px-2.5 py-3 text-center text-tm08-ink">70</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">M</th><td class="px-2.5 py-3 text-center text-tm08-ink">97</td><td class="px-2.5 py-3 text-center text-tm08-ink">81</td><td class="px-2.5 py-3 text-center text-tm08-ink">72</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">L</th><td class="px-2.5 py-3 text-center text-tm08-ink">102</td><td class="px-2.5 py-3 text-center text-tm08-ink">86</td><td class="px-2.5 py-3 text-center text-tm08-ink">74</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XL</th><td class="px-2.5 py-3 text-center text-tm08-ink">109</td><td class="px-2.5 py-3 text-center text-tm08-ink">94</td><td class="px-2.5 py-3 text-center text-tm08-ink">76</td></tr>
          </tbody>
          <tbody class="hidden group-has-[#tm-08-in:checked]:table-row-group">
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XS</th><td class="px-2.5 py-3 text-center text-tm08-ink">34</td><td class="px-2.5 py-3 text-center text-tm08-ink">28</td><td class="px-2.5 py-3 text-center text-tm08-ink">26.8</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">S</th><td class="px-2.5 py-3 text-center text-tm08-ink">36</td><td class="px-2.5 py-3 text-center text-tm08-ink">30</td><td class="px-2.5 py-3 text-center text-tm08-ink">27.6</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">M</th><td class="px-2.5 py-3 text-center text-tm08-ink">38</td><td class="px-2.5 py-3 text-center text-tm08-ink">32</td><td class="px-2.5 py-3 text-center text-tm08-ink">28.3</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">L</th><td class="px-2.5 py-3 text-center text-tm08-ink">40</td><td class="px-2.5 py-3 text-center text-tm08-ink">34</td><td class="px-2.5 py-3 text-center text-tm08-ink">29.1</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XL</th><td class="px-2.5 py-3 text-center text-tm08-ink">43</td><td class="px-2.5 py-3 text-center text-tm08-ink">37</td><td class="px-2.5 py-3 text-center text-tm08-ink">29.9</td></tr>
          </tbody>
        </table>
      </div>
      <p class="px-5 pt-4 pb-5 text-[12.5px] leading-relaxed text-tm08-mut border-t border-tm08-edge">Body measurements. Between sizes? Size up for a relaxed fit. Press <kbd class="px-1.5 py-0.5 rounded border border-tm08-edge text-[11px]">Space</kbd> on the focused control to open or close this guide.</p>
    </div>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tm08-bg: #07080b;
  --color-tm08-bg: oklch(0.15 0.012 260);
  --color-tm08-panel: #12151d;
  --color-tm08-panel: oklch(0.2 0.015 260);
  --color-tm08-card: #141822;
  --color-tm08-card: oklch(0.21 0.017 260);
  --color-tm08-thead: #161b26;
  --color-tm08-thead: oklch(0.22 0.018 260);
  --color-tm08-well: #0e1119;
  --color-tm08-well: oklch(0.18 0.015 260);
  --color-tm08-edge: #2a3140;
  --color-tm08-edge: oklch(0.3 0.018 260);
  --color-tm08-cardedge: #262c3c;
  --color-tm08-cardedge: oklch(0.29 0.018 260);
  --color-tm08-hi: #f2f5fc;
  --color-tm08-hi: oklch(0.97 0.006 260);
  --color-tm08-ink: #c7cedb;
  --color-tm08-ink: oklch(0.84 0.012 260);
  --color-tm08-mut: #98a1b4;
  --color-tm08-mut: oklch(0.71 0.018 260);
  --color-tm08-acc: #7fb0ff;
  --color-tm08-acc: oklch(0.75 0.14 250);
  --color-tm08-acctint: #24344f;
  --color-tm08-acctint: oklch(0.33 0.06 250);
  --color-tm08-rowhover: #7fb0ff1f;
  --color-tm08-rowhover: oklch(0.75 0.14 250 / 0.12);
  --color-tm08-scrim: #06080ca8;
  --color-tm08-scrim: oklch(0.13 0.01 260 / 0.66);
  --font-tm08-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-08 {
  width: 100%;
}

Setup — do this once per project

Skip this if your project already runs Tailwind v4.

1Load Tailwind
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

Compiles in the browser — perfect for a CodePen or a quick test, not for production.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@import "tailwindcss";

@theme {
  --color-tm08-bg: #07080b;
  --color-tm08-bg: oklch(0.15 0.012 260);
  --color-tm08-panel: #12151d;
  --color-tm08-panel: oklch(0.2 0.015 260);
  --color-tm08-card: #141822;
  --color-tm08-card: oklch(0.21 0.017 260);
  --color-tm08-thead: #161b26;
  --color-tm08-thead: oklch(0.22 0.018 260);
  --color-tm08-well: #0e1119;
  --color-tm08-well: oklch(0.18 0.015 260);
  --color-tm08-edge: #2a3140;
  --color-tm08-edge: oklch(0.3 0.018 260);
  --color-tm08-cardedge: #262c3c;
  --color-tm08-cardedge: oklch(0.29 0.018 260);
  --color-tm08-hi: #f2f5fc;
  --color-tm08-hi: oklch(0.97 0.006 260);
  --color-tm08-ink: #c7cedb;
  --color-tm08-ink: oklch(0.84 0.012 260);
  --color-tm08-mut: #98a1b4;
  --color-tm08-mut: oklch(0.71 0.018 260);
  --color-tm08-acc: #7fb0ff;
  --color-tm08-acc: oklch(0.75 0.14 250);
  --color-tm08-acctint: #24344f;
  --color-tm08-acctint: oklch(0.33 0.06 250);
  --color-tm08-rowhover: #7fb0ff1f;
  --color-tm08-rowhover: oklch(0.75 0.14 250 / 0.12);
  --color-tm08-scrim: #06080ca8;
  --color-tm08-scrim: oklch(0.13 0.01 260 / 0.66);
  --font-tm08-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-08 {
  width: 100%;
}
3Fonts used by this demooptional — skip if you have your own type stack
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • Two :has() rules become two variants on the elements they affect, and the id-scoped form (group-has-[#tm-08-in:checked]:) is the honest way to say 'this gate, not the other one under the same group'.
  • Zebra striping via odd:bg-white/[0.02] has to be repeated per row, which is the port's real cost on a table — worth it only because every row already carries hover and padding utilities.
  • UI change from the CSS original: every size chip, unit toggle and the close control moved to a 44px minimum (the originals were 38px, 30px and 34px), the close ✕ is a real <label> with an aria-label, and the footnote now tells keyboard users which key operates the gate.
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 demo 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: Tailwind Size Guide Overlay with Zero-JS cm/inch Toggle
Source: https://codefronts.com/components/tailwind-css-modals/tailwind-size-guide-overlay-zero-js-unit-toggle/

An apparel size chart that opens from a text link and flips between centimetres and inches — no JavaScript in either interaction. Two checkbox/radio gates, read with group-has-checked: and group-has-[#tm-08-in:checked]:, do what two :has() rules did before.
## HTML
```html
<section class="tm-08 group relative w-full min-h-svh grid place-items-center bg-tm08-bg font-tm08-sans px-6 py-10">
  <div class="flex flex-col items-start gap-4 p-6 rounded-2xl bg-tm08-panel border border-tm08-edge">
    <div class="flex items-center gap-2">
      <span class="mr-1.5 text-xs uppercase tracking-[0.16em] text-tm08-mut">Size</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">S</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-white border border-tm08-acc bg-tm08-acctint">M</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">L</span>
      <span class="grid place-items-center min-w-11 min-h-11 px-3 rounded-lg text-[13px] font-semibold text-tm08-ink border border-tm08-edge bg-tm08-well">XL</span>
    </div>
    <input type="checkbox" id="tm-08-t" class="peer sr-only">
    <label for="tm-08-t" class="inline-flex items-center gap-1.5 min-h-11 px-1 text-[13px] font-semibold text-tm08-acc underline underline-offset-[3px] cursor-pointer transition-[filter] duration-200 hover:brightness-125 peer-focus-visible:outline-2 peer-focus-visible:outline-tm08-acc peer-focus-visible:outline-offset-2 peer-focus-visible:rounded-md"><span aria-hidden="true">📏</span> Size guide</label>
  </div>

  <div role="dialog" aria-modal="true" aria-labelledby="tm-08-title" class="fixed inset-0 z-10 grid place-items-center p-6 bg-tm08-scrim backdrop-blur-[4px] opacity-0 invisible pointer-events-none transition-[opacity,visibility] duration-[280ms] group-has-checked:opacity-100 group-has-checked:visible group-has-checked:pointer-events-auto motion-reduce:transition-none">
    <div class="flex flex-col w-[min(460px,94vw)] max-h-[88dvh] rounded-[18px] bg-tm08-card border border-tm08-cardedge shadow-[0_40px_90px_-30px_#000] scale-[0.96] transition-transform duration-[280ms] group-has-checked:scale-100 motion-reduce:transition-none">
      <div class="flex items-center gap-3.5 px-5 py-4 border-b border-tm08-edge">
        <h2 id="tm-08-title" class="flex-1 text-lg font-bold text-tm08-hi">Size guide</h2>
        <div class="inline-flex rounded-[9px] border border-tm08-edge overflow-hidden">
          <input type="radio" name="tm-08-u" id="tm-08-cm" checked class="peer/cm sr-only">
          <label for="tm-08-cm" class="grid place-items-center min-h-11 px-4 text-xs font-semibold text-tm08-mut bg-tm08-well cursor-pointer transition-colors duration-150 peer-checked/cm:bg-tm08-acctint peer-checked/cm:text-white peer-focus-visible/cm:outline-2 peer-focus-visible/cm:outline-tm08-acc peer-focus-visible/cm:-outline-offset-2">cm</label>
          <input type="radio" name="tm-08-u" id="tm-08-in" class="peer/in sr-only">
          <label for="tm-08-in" class="grid place-items-center min-h-11 px-4 text-xs font-semibold text-tm08-mut bg-tm08-well cursor-pointer transition-colors duration-150 peer-checked/in:bg-tm08-acctint peer-checked/in:text-white peer-focus-visible/in:outline-2 peer-focus-visible/in:outline-tm08-acc peer-focus-visible/in:-outline-offset-2">in</label>
        </div>
        <label for="tm-08-t" aria-label="Close size guide" class="grid place-items-center size-11 rounded-lg text-[22px] leading-none text-tm08-mut cursor-pointer transition-colors duration-200 hover:bg-tm08-well hover:text-white">&times;</label>
      </div>

      <div class="overflow-auto px-2 py-1.5">
        <table class="w-full border-collapse text-sm">
          <thead>
            <tr>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Size</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Chest</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Waist</th>
              <th scope="col" class="sticky top-0 px-2.5 py-3 text-center text-xs font-semibold uppercase tracking-[0.05em] text-tm08-mut bg-tm08-thead border-b border-tm08-edge">Length</th>
            </tr>
          </thead>
          <tbody class="group-has-[#tm-08-in:checked]:hidden">
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XS</th><td class="px-2.5 py-3 text-center text-tm08-ink">86</td><td class="px-2.5 py-3 text-center text-tm08-ink">71</td><td class="px-2.5 py-3 text-center text-tm08-ink">68</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">S</th><td class="px-2.5 py-3 text-center text-tm08-ink">91</td><td class="px-2.5 py-3 text-center text-tm08-ink">76</td><td class="px-2.5 py-3 text-center text-tm08-ink">70</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">M</th><td class="px-2.5 py-3 text-center text-tm08-ink">97</td><td class="px-2.5 py-3 text-center text-tm08-ink">81</td><td class="px-2.5 py-3 text-center text-tm08-ink">72</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">L</th><td class="px-2.5 py-3 text-center text-tm08-ink">102</td><td class="px-2.5 py-3 text-center text-tm08-ink">86</td><td class="px-2.5 py-3 text-center text-tm08-ink">74</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XL</th><td class="px-2.5 py-3 text-center text-tm08-ink">109</td><td class="px-2.5 py-3 text-center text-tm08-ink">94</td><td class="px-2.5 py-3 text-center text-tm08-ink">76</td></tr>
          </tbody>
          <tbody class="hidden group-has-[#tm-08-in:checked]:table-row-group">
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XS</th><td class="px-2.5 py-3 text-center text-tm08-ink">34</td><td class="px-2.5 py-3 text-center text-tm08-ink">28</td><td class="px-2.5 py-3 text-center text-tm08-ink">26.8</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">S</th><td class="px-2.5 py-3 text-center text-tm08-ink">36</td><td class="px-2.5 py-3 text-center text-tm08-ink">30</td><td class="px-2.5 py-3 text-center text-tm08-ink">27.6</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">M</th><td class="px-2.5 py-3 text-center text-tm08-ink">38</td><td class="px-2.5 py-3 text-center text-tm08-ink">32</td><td class="px-2.5 py-3 text-center text-tm08-ink">28.3</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">L</th><td class="px-2.5 py-3 text-center text-tm08-ink">40</td><td class="px-2.5 py-3 text-center text-tm08-ink">34</td><td class="px-2.5 py-3 text-center text-tm08-ink">29.1</td></tr>
            <tr class="odd:bg-white/[0.02] hover:bg-tm08-rowhover"><th scope="row" class="pl-4 py-3 text-left font-bold text-tm08-hi">XL</th><td class="px-2.5 py-3 text-center text-tm08-ink">43</td><td class="px-2.5 py-3 text-center text-tm08-ink">37</td><td class="px-2.5 py-3 text-center text-tm08-ink">29.9</td></tr>
          </tbody>
        </table>
      </div>
      <p class="px-5 pt-4 pb-5 text-[12.5px] leading-relaxed text-tm08-mut border-t border-tm08-edge">Body measurements. Between sizes? Size up for a relaxed fit. Press <kbd class="px-1.5 py-0.5 rounded border border-tm08-edge text-[11px]">Space</kbd> on the focused control to open or close this guide.</p>
    </div>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tm08-bg: #07080b;
  --color-tm08-bg: oklch(0.15 0.012 260);
  --color-tm08-panel: #12151d;
  --color-tm08-panel: oklch(0.2 0.015 260);
  --color-tm08-card: #141822;
  --color-tm08-card: oklch(0.21 0.017 260);
  --color-tm08-thead: #161b26;
  --color-tm08-thead: oklch(0.22 0.018 260);
  --color-tm08-well: #0e1119;
  --color-tm08-well: oklch(0.18 0.015 260);
  --color-tm08-edge: #2a3140;
  --color-tm08-edge: oklch(0.3 0.018 260);
  --color-tm08-cardedge: #262c3c;
  --color-tm08-cardedge: oklch(0.29 0.018 260);
  --color-tm08-hi: #f2f5fc;
  --color-tm08-hi: oklch(0.97 0.006 260);
  --color-tm08-ink: #c7cedb;
  --color-tm08-ink: oklch(0.84 0.012 260);
  --color-tm08-mut: #98a1b4;
  --color-tm08-mut: oklch(0.71 0.018 260);
  --color-tm08-acc: #7fb0ff;
  --color-tm08-acc: oklch(0.75 0.14 250);
  --color-tm08-acctint: #24344f;
  --color-tm08-acctint: oklch(0.33 0.06 250);
  --color-tm08-rowhover: #7fb0ff1f;
  --color-tm08-rowhover: oklch(0.75 0.14 250 / 0.12);
  --color-tm08-scrim: #06080ca8;
  --color-tm08-scrim: oklch(0.13 0.01 260 / 0.66);
  --font-tm08-sans: 'Space Grotesk', system-ui, sans-serif;
}

.tm-08 {
  width: 100%;
}
```

How this works

Two gates, one wrapper. A checkbox opens the overlay; a radio pair picks the unit. Both live under the same group, which is why the unit rule needs the explicit id form — group-has-checked: alone would match either input and flip the table whenever the overlay opened.

The data stays a table. <th scope="col"> and <th scope="row"> mean a screen-reader user hears "M, chest, 97" rather than a wall of numbers — and the figures stay selectable, translatable and indexable, which a screenshot of a size chart never is.

Both tbodies ship. The inch table is hidden, not absent, so the toggle is a display swap with no reflow of the panel and no duplicate source of truth to keep in sync at build time.

Make it yours

  • Default to inches by moving checked to #tm-08-in.
  • Add a 'how to measure' column of small diagrams — the sticky header and striping adapt with no rule changes.
  • Retheme through --color-tm08-acc, which drives the chip, the link, the active unit and every focus ring.

Gotchas — read before shipping

  • A checkbox overlay cannot trap focus. Tab still reaches the page behind, and there is no Esc. If the chart must corral keyboard users, make it a <dialog>.
  • Never hide the driving inputs with hidden or display:none — sr-only keeps them focusable, which is the only reason this pattern is keyboard-operable at all.
  • group-has-[#tm-08-in:checked]: needs the id, not a bare has-checked. Two gates under one group is the classic way this pattern silently misfires.
  • Let the table scroll horizontally on narrow screens rather than shrinking the columns; squashed numeric columns are harder to read than a scroll.

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

:has() is the floor (Firefox 121). Without it the overlay never opens — so on a critical flow, ship the <dialog> version instead.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…