03 / 14
All 14 in 14 Material Design Components in Tailwind CSS
- 01Tailwind Material Design 3 Buttons
- 02Tailwind Material Design 3 Cards
- 03Tailwind Material Design Navigation Bar, Rail and Drawer
- 04Tailwind Material Design Text Fields and Form Controls
- 05Tailwind Material Design Dialog, Bottom Sheet and Snackbar
- 06Tailwind Material Design Data Table
- 07Tailwind Material Design Tabs with Sliding Indicator
- 08Tailwind Material Design Chips with Live Filtering
- 09Tailwind Material Design Stepper and Checkout Flow
- 10Tailwind Material Design Tooltips, Progress and Badges
- 11Tailwind Material Design Expansion Panels and Accordion
- 12Tailwind Material Design Lists and Swipeable Rows
- 13Tailwind Material Design Color Palette and Theme Switcher
- 14Tailwind Material Design Responsive App Shell Layout
Tailwind Material Design Navigation Bar, Rail and Drawer
The three M3 navigation containers side by side — bottom navigation bar, rail and standard drawer — all driven by radios and peer-checked:, so the active tab, its pill indicator and its filled icon all change with no JavaScript. The port's win is the indicator: an M3 active pill is one peer-checked: background on the icon box, not a ::before rule per item.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tmd-03 w-full min-h-svh bg-tmd03-bg font-tmd03-sans text-tmd03-onsurface px-5 py-9" aria-label="Material Design navigation containers demo">
<div class="w-[min(1100px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Navigation bar, rail & drawer</h2>
<p class="text-sm text-tmd03-onvariant">One item recipe, three containers. Radios carry the state; <code class="font-tmd03-mono text-[12px]">peer-checked:</code> carries the look.</p>
</header>
<div class="grid gap-5 grid-cols-[repeat(auto-fit,minmax(min(320px,100%),1fr))]">
<div class="grid grid-rows-[1fr_auto] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<div class="grid content-start gap-3 p-5">
<div class="flex items-center gap-3">
<button type="button" aria-label="Open navigation" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<span class="text-[17px] font-medium">Inbox</span>
<span class="ml-auto font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Bottom bar</span>
</div>
<div class="h-3 w-[78%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[62%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[70%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
<nav aria-label="Primary" class="group/bar grid grid-cols-4 border-t border-tmd03-outlinevariant bg-tmd03-surfacecontainer">
<input type="radio" name="tmd03-bar" id="tmd03-bar-1" class="peer/b1 sr-only" checked>
<label for="tmd03-bar-1" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsurface peer-focus-visible/b1:outline-2 peer-focus-visible/b1:-outline-offset-2 peer-focus-visible/b1:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-1:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span></span>Inbox
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-2" class="peer/b2 sr-only">
<label for="tmd03-bar-2" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsurface peer-focus-visible/b2:outline-2 peer-focus-visible/b2:-outline-offset-2 peer-focus-visible/b2:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-2:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">send</span><b class="absolute top-0.5 right-2.5 min-w-4 h-4 px-1 grid place-items-center rounded-full bg-tmd03-error text-tmd03-onerror font-tmd03-mono text-[10px] tabular-nums">3</b></span>Sent
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-3" class="peer/b3 sr-only">
<label for="tmd03-bar-3" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsurface peer-focus-visible/b3:outline-2 peer-focus-visible/b3:-outline-offset-2 peer-focus-visible/b3:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-3:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">drafts</span></span>Drafts
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-4" class="peer/b4 sr-only">
<label for="tmd03-bar-4" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsurface peer-focus-visible/b4:outline-2 peer-focus-visible/b4:-outline-offset-2 peer-focus-visible/b4:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-4:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">settings</span></span>Settings
</label>
</nav>
</div>
<div class="grid grid-cols-[80px_1fr] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<nav aria-label="Rail" class="group/rail grid content-start gap-2 py-4 bg-tmd03-surfacecontainer border-r border-tmd03-outlinevariant">
<button type="button" aria-label="Open menu" class="relative overflow-hidden grid place-items-center justify-self-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface mb-1 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<input type="radio" name="tmd03-rail" id="tmd03-rail-1" class="peer/r1 sr-only" checked>
<label for="tmd03-rail-1" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsurface peer-focus-visible/r1:outline-2 peer-focus-visible/r1:-outline-offset-2 peer-focus-visible/r1:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-1:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-1:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">dashboard</span></span>Home
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-2" class="peer/r2 sr-only">
<label for="tmd03-rail-2" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsurface peer-focus-visible/r2:outline-2 peer-focus-visible/r2:-outline-offset-2 peer-focus-visible/r2:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-2:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-2:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">bar_chart</span></span>Reports
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-3" class="peer/r3 sr-only">
<label for="tmd03-rail-3" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsurface peer-focus-visible/r3:outline-2 peer-focus-visible/r3:-outline-offset-2 peer-focus-visible/r3:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-3:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-3:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">group</span></span>Team
</label>
</nav>
<div class="grid content-start gap-3 p-5">
<span class="font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Navigation rail · 80px</span>
<div class="h-28 rounded-2xl bg-linear-[150deg,#c62828,#ef5350_70%,#ffcdd2]" aria-hidden="true"></div>
<div class="h-3 w-[72%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[54%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
</div>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(256px,100%),1fr))] gap-5">
<nav aria-label="Drawer" class="group/dr has-checked:ring-1 has-checked:ring-tmd03-outlinevariant grid content-start gap-1 p-3 rounded-3xl bg-tmd03-surfacecontainer">
<span class="px-4 py-2 font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Standard drawer</span>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-1" class="peer/d1 sr-only" checked>
<label for="tmd03-dr-1" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-1:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-1:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d1:outline-2 peer-focus-visible/d1:-outline-offset-2 peer-focus-visible/d1:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-1:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span>Inbox<span class="ml-auto font-tmd03-mono text-xs tabular-nums">24</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-2" class="peer/d2 sr-only">
<label for="tmd03-dr-2" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-2:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-2:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d2:outline-2 peer-focus-visible/d2:-outline-offset-2 peer-focus-visible/d2:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-2:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">star</span>Starred<span class="ml-auto font-tmd03-mono text-xs tabular-nums">6</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-3" class="peer/d3 sr-only">
<label for="tmd03-dr-3" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-3:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-3:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d3:outline-2 peer-focus-visible/d3:-outline-offset-2 peer-focus-visible/d3:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-3:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">label</span>Labels</label>
<hr class="my-2 border-0 border-t border-tmd03-outlinevariant">
<label for="tmd03-dr-1" class="px-4 pb-1 text-xs text-tmd03-onvariant">Selecting any destination adds a hairline ring to this drawer via <span class="font-tmd03-mono text-[11px]">has-checked:</span>.</label>
</nav>
<div class="grid content-center gap-2 p-6 rounded-3xl bg-tmd03-surface shadow-tmd03-1">
<h3 class="text-lg font-medium">Why radios, not divs</h3>
<p class="text-sm leading-6 text-tmd03-onvariant text-pretty">A native radio group is one tab stop, arrow-key traversable, announced as “2 of 4”, and restores on back-navigation. Every one of those behaviours would be JavaScript in a <span class="font-tmd03-mono text-[12px]">div role="tab"</span> rebuild — and the styling hook, <span class="font-tmd03-mono text-[12px]">peer-checked:</span>, is shorter than the class-toggling it replaces.</p>
</div>
</div>
</div>
</section><section class="tmd-03 w-full min-h-svh bg-tmd03-bg font-tmd03-sans text-tmd03-onsurface px-5 py-9" aria-label="Material Design navigation containers demo">
<div class="w-[min(1100px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Navigation bar, rail & drawer</h2>
<p class="text-sm text-tmd03-onvariant">One item recipe, three containers. Radios carry the state; <code class="font-tmd03-mono text-[12px]">peer-checked:</code> carries the look.</p>
</header>
<div class="grid gap-5 grid-cols-[repeat(auto-fit,minmax(min(320px,100%),1fr))]">
<div class="grid grid-rows-[1fr_auto] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<div class="grid content-start gap-3 p-5">
<div class="flex items-center gap-3">
<button type="button" aria-label="Open navigation" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<span class="text-[17px] font-medium">Inbox</span>
<span class="ml-auto font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Bottom bar</span>
</div>
<div class="h-3 w-[78%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[62%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[70%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
<nav aria-label="Primary" class="group/bar grid grid-cols-4 border-t border-tmd03-outlinevariant bg-tmd03-surfacecontainer">
<input type="radio" name="tmd03-bar" id="tmd03-bar-1" class="peer/b1 sr-only" checked>
<label for="tmd03-bar-1" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsurface peer-focus-visible/b1:outline-2 peer-focus-visible/b1:-outline-offset-2 peer-focus-visible/b1:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-1:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span></span>Inbox
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-2" class="peer/b2 sr-only">
<label for="tmd03-bar-2" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsurface peer-focus-visible/b2:outline-2 peer-focus-visible/b2:-outline-offset-2 peer-focus-visible/b2:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-2:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">send</span><b class="absolute top-0.5 right-2.5 min-w-4 h-4 px-1 grid place-items-center rounded-full bg-tmd03-error text-tmd03-onerror font-tmd03-mono text-[10px] tabular-nums">3</b></span>Sent
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-3" class="peer/b3 sr-only">
<label for="tmd03-bar-3" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsurface peer-focus-visible/b3:outline-2 peer-focus-visible/b3:-outline-offset-2 peer-focus-visible/b3:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-3:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">drafts</span></span>Drafts
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-4" class="peer/b4 sr-only">
<label for="tmd03-bar-4" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsurface peer-focus-visible/b4:outline-2 peer-focus-visible/b4:-outline-offset-2 peer-focus-visible/b4:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-4:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">settings</span></span>Settings
</label>
</nav>
</div>
<div class="grid grid-cols-[80px_1fr] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<nav aria-label="Rail" class="group/rail grid content-start gap-2 py-4 bg-tmd03-surfacecontainer border-r border-tmd03-outlinevariant">
<button type="button" aria-label="Open menu" class="relative overflow-hidden grid place-items-center justify-self-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface mb-1 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<input type="radio" name="tmd03-rail" id="tmd03-rail-1" class="peer/r1 sr-only" checked>
<label for="tmd03-rail-1" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsurface peer-focus-visible/r1:outline-2 peer-focus-visible/r1:-outline-offset-2 peer-focus-visible/r1:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-1:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-1:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">dashboard</span></span>Home
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-2" class="peer/r2 sr-only">
<label for="tmd03-rail-2" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsurface peer-focus-visible/r2:outline-2 peer-focus-visible/r2:-outline-offset-2 peer-focus-visible/r2:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-2:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-2:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">bar_chart</span></span>Reports
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-3" class="peer/r3 sr-only">
<label for="tmd03-rail-3" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsurface peer-focus-visible/r3:outline-2 peer-focus-visible/r3:-outline-offset-2 peer-focus-visible/r3:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-3:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-3:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">group</span></span>Team
</label>
</nav>
<div class="grid content-start gap-3 p-5">
<span class="font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Navigation rail · 80px</span>
<div class="h-28 rounded-2xl bg-linear-[150deg,#c62828,#ef5350_70%,#ffcdd2]" aria-hidden="true"></div>
<div class="h-3 w-[72%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[54%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
</div>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(256px,100%),1fr))] gap-5">
<nav aria-label="Drawer" class="group/dr has-checked:ring-1 has-checked:ring-tmd03-outlinevariant grid content-start gap-1 p-3 rounded-3xl bg-tmd03-surfacecontainer">
<span class="px-4 py-2 font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Standard drawer</span>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-1" class="peer/d1 sr-only" checked>
<label for="tmd03-dr-1" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-1:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-1:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d1:outline-2 peer-focus-visible/d1:-outline-offset-2 peer-focus-visible/d1:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-1:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span>Inbox<span class="ml-auto font-tmd03-mono text-xs tabular-nums">24</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-2" class="peer/d2 sr-only">
<label for="tmd03-dr-2" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-2:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-2:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d2:outline-2 peer-focus-visible/d2:-outline-offset-2 peer-focus-visible/d2:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-2:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">star</span>Starred<span class="ml-auto font-tmd03-mono text-xs tabular-nums">6</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-3" class="peer/d3 sr-only">
<label for="tmd03-dr-3" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-3:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-3:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d3:outline-2 peer-focus-visible/d3:-outline-offset-2 peer-focus-visible/d3:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-3:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">label</span>Labels</label>
<hr class="my-2 border-0 border-t border-tmd03-outlinevariant">
<label for="tmd03-dr-1" class="px-4 pb-1 text-xs text-tmd03-onvariant">Selecting any destination adds a hairline ring to this drawer via <span class="font-tmd03-mono text-[11px]">has-checked:</span>.</label>
</nav>
<div class="grid content-center gap-2 p-6 rounded-3xl bg-tmd03-surface shadow-tmd03-1">
<h3 class="text-lg font-medium">Why radios, not divs</h3>
<p class="text-sm leading-6 text-tmd03-onvariant text-pretty">A native radio group is one tab stop, arrow-key traversable, announced as “2 of 4”, and restores on back-navigation. Every one of those behaviours would be JavaScript in a <span class="font-tmd03-mono text-[12px]">div role="tab"</span> rebuild — and the styling hook, <span class="font-tmd03-mono text-[12px]">peer-checked:</span>, is shorter than the class-toggling it replaces.</p>
</div>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}<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=Roboto+Flex:opsz,wght@8..144,300..700&family=Roboto+Mono:wght@400;500&family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0..1,0&display=swap">
<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=Roboto+Flex:opsz,wght@8..144,300..700&family=Roboto+Mono:wght@400;500&family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0..1,0&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The M3 active indicator stops being a
::beforepill and becomes a background on an already-present icon box — fewer nodes, and the pill animates its colour for free. group-has-[#id:checked]/bar:[font-variation-settings:'FILL'_1]replaces the second, filled icon set the CSS version needed. One glyph, one axis, one utility.- The direction of the state read is the thing to get right: a sibling label can use
peer-checked/b1:, but anything inside that label must usegroup-has-[#id:checked]/bar:—peer-*matches following siblings only, never their descendants. - The three containers now share one item class list and differ only in geometry utilities, which makes the M3 claim that they are the same component visible in the source.
has-checked:on the drawer is the ancestor half of the same state read — the utility answer to what used to need a parent class toggled in JS.- No JS at all, including focus rings:
peer-focus-visible/d1:puts the ring on the label while the real focus sits on thesr-onlyinput.
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 Material Design Navigation Bar, Rail and Drawer
Source: https://codefronts.com/design-styles/tailwind-material-design-components/tailwind-material-design-navigation-bar-rail-drawer/
The three M3 navigation containers side by side — bottom navigation bar, rail and standard drawer — all driven by radios and peer-checked:, so the active tab, its pill indicator and its filled icon all change with no JavaScript. The port's win is the indicator: an M3 active pill is one peer-checked: background on the icon box, not a ::before rule per item.
## HTML
```html
<section class="tmd-03 w-full min-h-svh bg-tmd03-bg font-tmd03-sans text-tmd03-onsurface px-5 py-9" aria-label="Material Design navigation containers demo">
<div class="w-[min(1100px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Navigation bar, rail & drawer</h2>
<p class="text-sm text-tmd03-onvariant">One item recipe, three containers. Radios carry the state; <code class="font-tmd03-mono text-[12px]">peer-checked:</code> carries the look.</p>
</header>
<div class="grid gap-5 grid-cols-[repeat(auto-fit,minmax(min(320px,100%),1fr))]">
<div class="grid grid-rows-[1fr_auto] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<div class="grid content-start gap-3 p-5">
<div class="flex items-center gap-3">
<button type="button" aria-label="Open navigation" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<span class="text-[17px] font-medium">Inbox</span>
<span class="ml-auto font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Bottom bar</span>
</div>
<div class="h-3 w-[78%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[62%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[70%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
<nav aria-label="Primary" class="group/bar grid grid-cols-4 border-t border-tmd03-outlinevariant bg-tmd03-surfacecontainer">
<input type="radio" name="tmd03-bar" id="tmd03-bar-1" class="peer/b1 sr-only" checked>
<label for="tmd03-bar-1" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsurface peer-focus-visible/b1:outline-2 peer-focus-visible/b1:-outline-offset-2 peer-focus-visible/b1:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-1:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span></span>Inbox
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-2" class="peer/b2 sr-only">
<label for="tmd03-bar-2" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsurface peer-focus-visible/b2:outline-2 peer-focus-visible/b2:-outline-offset-2 peer-focus-visible/b2:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-2:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">send</span><b class="absolute top-0.5 right-2.5 min-w-4 h-4 px-1 grid place-items-center rounded-full bg-tmd03-error text-tmd03-onerror font-tmd03-mono text-[10px] tabular-nums">3</b></span>Sent
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-3" class="peer/b3 sr-only">
<label for="tmd03-bar-3" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsurface peer-focus-visible/b3:outline-2 peer-focus-visible/b3:-outline-offset-2 peer-focus-visible/b3:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-3:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">drafts</span></span>Drafts
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-4" class="peer/b4 sr-only">
<label for="tmd03-bar-4" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsurface peer-focus-visible/b4:outline-2 peer-focus-visible/b4:-outline-offset-2 peer-focus-visible/b4:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-4:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">settings</span></span>Settings
</label>
</nav>
</div>
<div class="grid grid-cols-[80px_1fr] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<nav aria-label="Rail" class="group/rail grid content-start gap-2 py-4 bg-tmd03-surfacecontainer border-r border-tmd03-outlinevariant">
<button type="button" aria-label="Open menu" class="relative overflow-hidden grid place-items-center justify-self-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface mb-1 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<input type="radio" name="tmd03-rail" id="tmd03-rail-1" class="peer/r1 sr-only" checked>
<label for="tmd03-rail-1" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsurface peer-focus-visible/r1:outline-2 peer-focus-visible/r1:-outline-offset-2 peer-focus-visible/r1:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-1:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-1:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">dashboard</span></span>Home
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-2" class="peer/r2 sr-only">
<label for="tmd03-rail-2" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsurface peer-focus-visible/r2:outline-2 peer-focus-visible/r2:-outline-offset-2 peer-focus-visible/r2:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-2:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-2:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">bar_chart</span></span>Reports
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-3" class="peer/r3 sr-only">
<label for="tmd03-rail-3" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsurface peer-focus-visible/r3:outline-2 peer-focus-visible/r3:-outline-offset-2 peer-focus-visible/r3:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-3:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-3:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">group</span></span>Team
</label>
</nav>
<div class="grid content-start gap-3 p-5">
<span class="font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Navigation rail · 80px</span>
<div class="h-28 rounded-2xl bg-linear-[150deg,#c62828,#ef5350_70%,#ffcdd2]" aria-hidden="true"></div>
<div class="h-3 w-[72%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[54%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
</div>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(256px,100%),1fr))] gap-5">
<nav aria-label="Drawer" class="group/dr has-checked:ring-1 has-checked:ring-tmd03-outlinevariant grid content-start gap-1 p-3 rounded-3xl bg-tmd03-surfacecontainer">
<span class="px-4 py-2 font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Standard drawer</span>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-1" class="peer/d1 sr-only" checked>
<label for="tmd03-dr-1" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-1:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-1:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d1:outline-2 peer-focus-visible/d1:-outline-offset-2 peer-focus-visible/d1:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-1:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span>Inbox<span class="ml-auto font-tmd03-mono text-xs tabular-nums">24</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-2" class="peer/d2 sr-only">
<label for="tmd03-dr-2" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-2:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-2:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d2:outline-2 peer-focus-visible/d2:-outline-offset-2 peer-focus-visible/d2:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-2:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">star</span>Starred<span class="ml-auto font-tmd03-mono text-xs tabular-nums">6</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-3" class="peer/d3 sr-only">
<label for="tmd03-dr-3" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-3:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-3:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d3:outline-2 peer-focus-visible/d3:-outline-offset-2 peer-focus-visible/d3:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-3:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">label</span>Labels</label>
<hr class="my-2 border-0 border-t border-tmd03-outlinevariant">
<label for="tmd03-dr-1" class="px-4 pb-1 text-xs text-tmd03-onvariant">Selecting any destination adds a hairline ring to this drawer via <span class="font-tmd03-mono text-[11px]">has-checked:</span>.</label>
</nav>
<div class="grid content-center gap-2 p-6 rounded-3xl bg-tmd03-surface shadow-tmd03-1">
<h3 class="text-lg font-medium">Why radios, not divs</h3>
<p class="text-sm leading-6 text-tmd03-onvariant text-pretty">A native radio group is one tab stop, arrow-key traversable, announced as “2 of 4”, and restores on back-navigation. Every one of those behaviours would be JavaScript in a <span class="font-tmd03-mono text-[12px]">div role="tab"</span> rebuild — and the styling hook, <span class="font-tmd03-mono text-[12px]">peer-checked:</span>, is shorter than the class-toggling it replaces.</p>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}
```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 Material Design Navigation Bar, Rail and Drawer
Source: https://codefronts.com/design-styles/tailwind-material-design-components/tailwind-material-design-navigation-bar-rail-drawer/
The three M3 navigation containers side by side — bottom navigation bar, rail and standard drawer — all driven by radios and peer-checked:, so the active tab, its pill indicator and its filled icon all change with no JavaScript. The port's win is the indicator: an M3 active pill is one peer-checked: background on the icon box, not a ::before rule per item.
## HTML
```html
<section class="tmd-03 w-full min-h-svh bg-tmd03-bg font-tmd03-sans text-tmd03-onsurface px-5 py-9" aria-label="Material Design navigation containers demo">
<div class="w-[min(1100px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Navigation bar, rail & drawer</h2>
<p class="text-sm text-tmd03-onvariant">One item recipe, three containers. Radios carry the state; <code class="font-tmd03-mono text-[12px]">peer-checked:</code> carries the look.</p>
</header>
<div class="grid gap-5 grid-cols-[repeat(auto-fit,minmax(min(320px,100%),1fr))]">
<div class="grid grid-rows-[1fr_auto] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<div class="grid content-start gap-3 p-5">
<div class="flex items-center gap-3">
<button type="button" aria-label="Open navigation" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<span class="text-[17px] font-medium">Inbox</span>
<span class="ml-auto font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Bottom bar</span>
</div>
<div class="h-3 w-[78%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[62%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[70%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
<nav aria-label="Primary" class="group/bar grid grid-cols-4 border-t border-tmd03-outlinevariant bg-tmd03-surfacecontainer">
<input type="radio" name="tmd03-bar" id="tmd03-bar-1" class="peer/b1 sr-only" checked>
<label for="tmd03-bar-1" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsurface peer-focus-visible/b1:outline-2 peer-focus-visible/b1:-outline-offset-2 peer-focus-visible/b1:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-1:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-1:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-1:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span></span>Inbox
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-2" class="peer/b2 sr-only">
<label for="tmd03-bar-2" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsurface peer-focus-visible/b2:outline-2 peer-focus-visible/b2:-outline-offset-2 peer-focus-visible/b2:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-2:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-2:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-2:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">send</span><b class="absolute top-0.5 right-2.5 min-w-4 h-4 px-1 grid place-items-center rounded-full bg-tmd03-error text-tmd03-onerror font-tmd03-mono text-[10px] tabular-nums">3</b></span>Sent
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-3" class="peer/b3 sr-only">
<label for="tmd03-bar-3" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsurface peer-focus-visible/b3:outline-2 peer-focus-visible/b3:-outline-offset-2 peer-focus-visible/b3:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-3:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-3:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-3:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">drafts</span></span>Drafts
</label>
<input type="radio" name="tmd03-bar" id="tmd03-bar-4" class="peer/b4 sr-only">
<label for="tmd03-bar-4" class="grid justify-items-center gap-1 pt-3 pb-2.5 cursor-pointer text-tmd03-onvariant text-xs font-medium transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsurface peer-focus-visible/b4:outline-2 peer-focus-visible/b4:-outline-offset-2 peer-focus-visible/b4:outline-tmd03-primary">
<span class="relative grid place-items-center w-16 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-bar-4:checked]/bar:bg-tmd03-secondarycontainer group-has-[#tmd03-bar-4:checked]/bar:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-bar-4:checked]/bar:[font-variation-settings:'FILL'_1]" aria-hidden="true">settings</span></span>Settings
</label>
</nav>
</div>
<div class="grid grid-cols-[80px_1fr] min-h-80 rounded-3xl overflow-hidden bg-tmd03-surface shadow-tmd03-1">
<nav aria-label="Rail" class="group/rail grid content-start gap-2 py-4 bg-tmd03-surfacecontainer border-r border-tmd03-outlinevariant">
<button type="button" aria-label="Open menu" class="relative overflow-hidden grid place-items-center justify-self-center min-w-11 min-h-11 rounded-full border-0 bg-transparent cursor-pointer text-tmd03-onsurface mb-1 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd03-primary before:absolute before:inset-1 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none" aria-hidden="true">menu</span></button>
<input type="radio" name="tmd03-rail" id="tmd03-rail-1" class="peer/r1 sr-only" checked>
<label for="tmd03-rail-1" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsurface peer-focus-visible/r1:outline-2 peer-focus-visible/r1:-outline-offset-2 peer-focus-visible/r1:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-1:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-1:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-1:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">dashboard</span></span>Home
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-2" class="peer/r2 sr-only">
<label for="tmd03-rail-2" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsurface peer-focus-visible/r2:outline-2 peer-focus-visible/r2:-outline-offset-2 peer-focus-visible/r2:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-2:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-2:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-2:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">bar_chart</span></span>Reports
</label>
<input type="radio" name="tmd03-rail" id="tmd03-rail-3" class="peer/r3 sr-only">
<label for="tmd03-rail-3" class="grid justify-items-center gap-1 py-1.5 cursor-pointer text-tmd03-onvariant text-[11px] font-medium group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsurface peer-focus-visible/r3:outline-2 peer-focus-visible/r3:-outline-offset-2 peer-focus-visible/r3:outline-tmd03-primary">
<span class="grid place-items-center w-14 h-8 rounded-full transition-colors duration-200 group-has-[#tmd03-rail-3:checked]/rail:bg-tmd03-secondarycontainer group-has-[#tmd03-rail-3:checked]/rail:text-tmd03-onsecondarycontainer motion-reduce:transition-none"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-rail-3:checked]/rail:[font-variation-settings:'FILL'_1]" aria-hidden="true">group</span></span>Team
</label>
</nav>
<div class="grid content-start gap-3 p-5">
<span class="font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Navigation rail · 80px</span>
<div class="h-28 rounded-2xl bg-linear-[150deg,#c62828,#ef5350_70%,#ffcdd2]" aria-hidden="true"></div>
<div class="h-3 w-[72%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
<div class="h-3 w-[54%] rounded-full bg-tmd03-outlinevariant" aria-hidden="true"></div>
</div>
</div>
</div>
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(256px,100%),1fr))] gap-5">
<nav aria-label="Drawer" class="group/dr has-checked:ring-1 has-checked:ring-tmd03-outlinevariant grid content-start gap-1 p-3 rounded-3xl bg-tmd03-surfacecontainer">
<span class="px-4 py-2 font-tmd03-mono text-[11px] uppercase tracking-[0.08em] text-tmd03-onvariant">Standard drawer</span>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-1" class="peer/d1 sr-only" checked>
<label for="tmd03-dr-1" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-1:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-1:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d1:outline-2 peer-focus-visible/d1:-outline-offset-2 peer-focus-visible/d1:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-1:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">inbox</span>Inbox<span class="ml-auto font-tmd03-mono text-xs tabular-nums">24</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-2" class="peer/d2 sr-only">
<label for="tmd03-dr-2" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-2:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-2:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d2:outline-2 peer-focus-visible/d2:-outline-offset-2 peer-focus-visible/d2:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-2:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">star</span>Starred<span class="ml-auto font-tmd03-mono text-xs tabular-nums">6</span></label>
<input type="radio" name="tmd03-drawer" id="tmd03-dr-3" class="peer/d3 sr-only">
<label for="tmd03-dr-3" class="relative overflow-hidden flex items-center gap-3 min-h-14 px-4 rounded-full cursor-pointer text-sm font-medium text-tmd03-onvariant transition-colors duration-200 group-has-[#tmd03-dr-3:checked]/dr:bg-tmd03-secondarycontainer group-has-[#tmd03-dr-3:checked]/dr:text-tmd03-onsecondarycontainer peer-focus-visible/d3:outline-2 peer-focus-visible/d3:-outline-offset-2 peer-focus-visible/d3:outline-tmd03-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd03-icon text-[22px] leading-none group-has-[#tmd03-dr-3:checked]/dr:[font-variation-settings:'FILL'_1]" aria-hidden="true">label</span>Labels</label>
<hr class="my-2 border-0 border-t border-tmd03-outlinevariant">
<label for="tmd03-dr-1" class="px-4 pb-1 text-xs text-tmd03-onvariant">Selecting any destination adds a hairline ring to this drawer via <span class="font-tmd03-mono text-[11px]">has-checked:</span>.</label>
</nav>
<div class="grid content-center gap-2 p-6 rounded-3xl bg-tmd03-surface shadow-tmd03-1">
<h3 class="text-lg font-medium">Why radios, not divs</h3>
<p class="text-sm leading-6 text-tmd03-onvariant text-pretty">A native radio group is one tab stop, arrow-key traversable, announced as “2 of 4”, and restores on back-navigation. Every one of those behaviours would be JavaScript in a <span class="font-tmd03-mono text-[12px]">div role="tab"</span> rebuild — and the styling hook, <span class="font-tmd03-mono text-[12px]">peer-checked:</span>, is shorter than the class-toggling it replaces.</p>
</div>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tmd03-bg: #fafafa;
--color-tmd03-bg: oklch(0.985 0 89.9);
--color-tmd03-surface: #fffbfe;
--color-tmd03-surface: oklch(0.992 0.006 334);
--color-tmd03-surfacecontainer: #f3eff4;
--color-tmd03-surfacecontainer: oklch(0.957 0.008 319.4);
--color-tmd03-primary: #c62828;
--color-tmd03-primary: oklch(0.539 0.194 26.7);
--color-tmd03-secondarycontainer: #ffdad6;
--color-tmd03-secondarycontainer: oklch(0.918 0.042 25.2);
--color-tmd03-onsecondarycontainer: #410002;
--color-tmd03-onsecondarycontainer: oklch(0.236 0.096 27.2);
--color-tmd03-onsurface: #1c1b1f;
--color-tmd03-onsurface: oklch(0.225 0.008 297.2);
--color-tmd03-onvariant: #4f4b52;
--color-tmd03-onvariant: oklch(0.419 0.013 311);
--color-tmd03-outlinevariant: #ddd5dd;
--color-tmd03-outlinevariant: oklch(0.882 0.014 325.7);
--color-tmd03-error: #b3261e;
--color-tmd03-error: oklch(0.501 0.178 28.7);
--color-tmd03-onerror: #ffffff;
--color-tmd03-onerror: oklch(1 0 89.9);
--font-tmd03-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd03-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd03-icon: 'Material Symbols Outlined';
--shadow-tmd03-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-03 {
width: 100%;
min-height: 100svh;
}
.tmd-03 [class*="font-tmd03-icon"] {
font-feature-settings: 'liga';
}
```How this works
A tab bar is a radio group, and the nav reads it. Each destination is a visually hidden radio plus a label. The pieces that change — the icon box, the glyph fill — are inside the label, so they are not siblings of the radio: peer-* cannot reach them and the state is read from the nav instead.
<nav class="group/bar grid grid-cols-4">
<input type="radio" name="tmd03-bar" id="tmd03-bar-1" class="sr-only" checked>
<label for="tmd03-bar-1">
<span class="w-16 h-8 rounded-full grid place-items-center
group-has-[#tmd03-bar-1:checked]/bar:bg-tmd03-secondarycontainer">inbox</span>Native radios give arrow-key traversal, a single tab stop for the group and correct screen-reader grouping — all the things a div role=tab reimplementation gets wrong.
The filled-icon trick. Material Symbols is a variable font with a FILL axis, so the active icon is the same glyph at FILL 1. As a utility that is peer-checked/a:[font-variation-settings:'FILL'_1] — an arbitrary property, because a font axis is not a design-scale value, and it removes the second icon set entirely.
Three containers, one vocabulary. The bar is a 4-track grid pinned to the bottom of its stage; the rail is an 80px column with the same item recipe stacked; the drawer is a 256px column where the item becomes a full-width pill. Nothing but geometry changes between them, which is exactly what the M3 spec claims and what the CSS version's three unrelated blocks obscured.
Make it yours
- Add a destination by copying one input/label pair and giving it a fresh peer name — the grid track count is the only other edit.
- Turn the rail into an extended rail by swapping
w-20forw-56and the item'sgridforflex items-center gap-3. - Recolour the active pill through
--color-tmd03-secondarycontainer; the indicator, the drawer pill and the badge all read it. - Badges are absolutely positioned spans on the icon box; delete them for a quieter bar.
- For a real app, promote the bar to
fixed bottom-0 inset-x-0and addpb-[env(safe-area-inset-bottom)].
Gotchas — read before shipping
sr-only, neverhidden, on the radios — adisplay:noneinput is unfocusable and the whole group leaves the tab order.peer-*compiles to a sibling selector, so it never reaches a descendant of the label. That mistake fails silently — the active pill simply never paints.- The active icon box must be at least 44px tall in its hit target even though the M3 pill is 32px — pad the label, not the pill.
aria-current="page"belongs on drawer links (real navigation); radios express a view switch, not a page change. Do not mix the two idioms in one container.- Arbitrary
font-variation-settingsvalues need the underscore escape and quoted axis name:[font-variation-settings:'FILL'_1]. - The rail's labels are optional in M3 but never omit them for the bar; icon-only bottom bars fail comprehension tests at every literacy level.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
has-checked: compiles to :has(), which landed in Firefox 121; the rest runs anywhere Tailwind v4's sheet does. The FILL axis needs the variable Material Symbols font, which the setup block loads.