08 / 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 Chips with Live Filtering
All four M3 chip types — assist, filter, input and suggestion — and then the part the CSS version could only fake: the filter chips actually filter. Checking a chip reveals its matching cards through a has-[#id:checked]: rule on the results grid, so a live filter runs with no JavaScript at all.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tmd-08 w-full min-h-svh bg-tmd08-bg font-tmd08-sans text-tmd08-onsurface px-5 py-9" aria-label="Material Design chips demo">
<div class="w-[min(900px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Chips that actually filter</h2>
<p class="text-sm text-tmd08-onvariant">Four M3 chip types, and a working facet filter built from <code class="font-tmd08-mono text-[12px]">has-[…:checked]:*:data-[tag]:block</code>.</p>
</header>
<div class="group/filter grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Filter chips — live</span>
<div class="flex flex-wrap gap-2">
<input type="checkbox" id="tmd08-f1" class="peer/f1 sr-only">
<label for="tmd08-f1" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f1:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f1:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f1:checked]/filter:border-transparent peer-focus-visible/f1:outline-2 peer-focus-visible/f1:outline-offset-2 peer-focus-visible/f1:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f1:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Deep water</label>
<input type="checkbox" id="tmd08-f2" class="peer/f2 sr-only">
<label for="tmd08-f2" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f2:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f2:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f2:checked]/filter:border-transparent peer-focus-visible/f2:outline-2 peer-focus-visible/f2:outline-offset-2 peer-focus-visible/f2:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f2:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Coastal</label>
<input type="checkbox" id="tmd08-f3" class="peer/f3 sr-only">
<label for="tmd08-f3" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f3:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f3:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f3:checked]/filter:border-transparent peer-focus-visible/f3:outline-2 peer-focus-visible/f3:outline-offset-2 peer-focus-visible/f3:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f3:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Estuary</label>
</div>
<div class="grid gap-2 grid-cols-[repeat(auto-fit,minmax(min(200px,100%),1fr))] group-has-[:checked]/filter:*:hidden group-has-[#tmd08-f1:checked]/filter:*:data-[tag=deep]:block group-has-[#tmd08-f2:checked]/filter:*:data-[tag=coastal]:block group-has-[#tmd08-f3:checked]/filter:*:data-[tag=estuary]:block">
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-014 · 1 240 m</b><span class="text-xs text-tmd08-onvariant">Deep water · North Atlantic</span></article>
<article data-tag="coastal" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-059 · 312 m</b><span class="text-xs text-tmd08-onvariant">Coastal · Irish Shelf</span></article>
<article data-tag="estuary" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-022 · 86 m</b><span class="text-xs text-tmd08-onvariant">Estuary · Baltic</span></article>
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-047 · 2 010 m</b><span class="text-xs text-tmd08-onvariant">Deep water · Norwegian Sea</span></article>
</div>
<p class="text-xs text-tmd08-onvariant">Unchecked shows everything; checking a chip narrows the grid. No script — the grid states its own filter rules.</p>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Assist, suggestion & elevated</span>
<div class="flex flex-wrap gap-2">
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">event</span>Schedule dive</button>
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">near_me</span>Nearest station</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-tmd08-surface border-0 shadow-tmd08-1 text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Elevated chip</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outlinevariant text-sm font-medium text-tmd08-onvariant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Suggestion</button>
<span aria-disabled="true" class="inline-flex items-center min-h-11 px-4 rounded-lg border border-tmd08-outline text-sm font-medium text-tmd08-onsurface opacity-38 pointer-events-none">Disabled</span>
</div>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Input chips — with remove controls</span>
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center gap-1 min-h-11 pl-1.5 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary"><span class="grid place-items-center w-7 h-7 rounded-full bg-tmd08-primary text-white font-tmd08-mono text-[11px]">MK</span>M. Kovač<button type="button" aria-label="Remove M. Kovač" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">depth > 500 m<button type="button" aria-label="Remove depth filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg bg-tmd08-secondarycontainer text-tmd08-onsecondarycontainer text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">QC flagged<button type="button" aria-label="Remove QC filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-current focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
</div>
<p class="text-xs text-tmd08-onvariant">The chip shows a ring when its remove button takes focus — <span class="font-tmd08-mono text-[11px]">has-[button:focus-visible]:ring-2</span>, the wrapper reacting to a descendant.</p>
</div>
</div>
</section><section class="tmd-08 w-full min-h-svh bg-tmd08-bg font-tmd08-sans text-tmd08-onsurface px-5 py-9" aria-label="Material Design chips demo">
<div class="w-[min(900px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Chips that actually filter</h2>
<p class="text-sm text-tmd08-onvariant">Four M3 chip types, and a working facet filter built from <code class="font-tmd08-mono text-[12px]">has-[…:checked]:*:data-[tag]:block</code>.</p>
</header>
<div class="group/filter grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Filter chips — live</span>
<div class="flex flex-wrap gap-2">
<input type="checkbox" id="tmd08-f1" class="peer/f1 sr-only">
<label for="tmd08-f1" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f1:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f1:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f1:checked]/filter:border-transparent peer-focus-visible/f1:outline-2 peer-focus-visible/f1:outline-offset-2 peer-focus-visible/f1:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f1:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Deep water</label>
<input type="checkbox" id="tmd08-f2" class="peer/f2 sr-only">
<label for="tmd08-f2" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f2:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f2:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f2:checked]/filter:border-transparent peer-focus-visible/f2:outline-2 peer-focus-visible/f2:outline-offset-2 peer-focus-visible/f2:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f2:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Coastal</label>
<input type="checkbox" id="tmd08-f3" class="peer/f3 sr-only">
<label for="tmd08-f3" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f3:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f3:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f3:checked]/filter:border-transparent peer-focus-visible/f3:outline-2 peer-focus-visible/f3:outline-offset-2 peer-focus-visible/f3:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f3:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Estuary</label>
</div>
<div class="grid gap-2 grid-cols-[repeat(auto-fit,minmax(min(200px,100%),1fr))] group-has-[:checked]/filter:*:hidden group-has-[#tmd08-f1:checked]/filter:*:data-[tag=deep]:block group-has-[#tmd08-f2:checked]/filter:*:data-[tag=coastal]:block group-has-[#tmd08-f3:checked]/filter:*:data-[tag=estuary]:block">
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-014 · 1 240 m</b><span class="text-xs text-tmd08-onvariant">Deep water · North Atlantic</span></article>
<article data-tag="coastal" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-059 · 312 m</b><span class="text-xs text-tmd08-onvariant">Coastal · Irish Shelf</span></article>
<article data-tag="estuary" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-022 · 86 m</b><span class="text-xs text-tmd08-onvariant">Estuary · Baltic</span></article>
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-047 · 2 010 m</b><span class="text-xs text-tmd08-onvariant">Deep water · Norwegian Sea</span></article>
</div>
<p class="text-xs text-tmd08-onvariant">Unchecked shows everything; checking a chip narrows the grid. No script — the grid states its own filter rules.</p>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Assist, suggestion & elevated</span>
<div class="flex flex-wrap gap-2">
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">event</span>Schedule dive</button>
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">near_me</span>Nearest station</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-tmd08-surface border-0 shadow-tmd08-1 text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Elevated chip</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outlinevariant text-sm font-medium text-tmd08-onvariant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Suggestion</button>
<span aria-disabled="true" class="inline-flex items-center min-h-11 px-4 rounded-lg border border-tmd08-outline text-sm font-medium text-tmd08-onsurface opacity-38 pointer-events-none">Disabled</span>
</div>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Input chips — with remove controls</span>
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center gap-1 min-h-11 pl-1.5 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary"><span class="grid place-items-center w-7 h-7 rounded-full bg-tmd08-primary text-white font-tmd08-mono text-[11px]">MK</span>M. Kovač<button type="button" aria-label="Remove M. Kovač" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">depth > 500 m<button type="button" aria-label="Remove depth filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg bg-tmd08-secondarycontainer text-tmd08-onsecondarycontainer text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">QC flagged<button type="button" aria-label="Remove QC filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-current focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
</div>
<p class="text-xs text-tmd08-onvariant">The chip shows a ring when its remove button takes focus — <span class="font-tmd08-mono text-[11px]">has-[button:focus-visible]:ring-2</span>, the wrapper reacting to a descendant.</p>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-icon {
font-feature-settings: 'liga';
}@import "tailwindcss";
@theme {
--color-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-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-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-icon {
font-feature-settings: 'liga';
}@import "tailwindcss";
@theme {
--color-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-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 filter is real, and that is the demo's argument:
has-[#id:checked]:*:data-[tag=x]:blocklets an ancestor's state select matching children, which is a JS filter loop deleted for small sets. - The checkmark's
w-0 → w-4.5width transition is the M3 detail most ports miss; animating opacity alone leaves the chip the wrong width at rest. - Filter chips are checkboxes; assist and suggestion chips are buttons; input chips are neither, because they contain a button. Keeping those three semantics distinct is what the CSS version's uniform
.chipclass hid. has-[button:focus-visible]:ring-2is the wrapper-reacts-to-descendant direction of:has(), and it is the only way to show chip focus without moving the ring off the real control.opacity-38 pointer-events-none aria-disabledtogether are the honest disabled chip — the CSS version's opacity alone left it clickable.- Where to stop: past three facets the class list on the results grid becomes unreadable. The notes say so rather than pretending CSS scales here.
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 Chips with Live Filtering
Source: https://codefronts.com/design-styles/tailwind-material-design-components/tailwind-material-design-chips-live-filtering/
All four M3 chip types — assist, filter, input and suggestion — and then the part the CSS version could only fake: the filter chips actually filter. Checking a chip reveals its matching cards through a has-[#id:checked]: rule on the results grid, so a live filter runs with no JavaScript at all.
## HTML
```html
<section class="tmd-08 w-full min-h-svh bg-tmd08-bg font-tmd08-sans text-tmd08-onsurface px-5 py-9" aria-label="Material Design chips demo">
<div class="w-[min(900px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Chips that actually filter</h2>
<p class="text-sm text-tmd08-onvariant">Four M3 chip types, and a working facet filter built from <code class="font-tmd08-mono text-[12px]">has-[…:checked]:*:data-[tag]:block</code>.</p>
</header>
<div class="group/filter grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Filter chips — live</span>
<div class="flex flex-wrap gap-2">
<input type="checkbox" id="tmd08-f1" class="peer/f1 sr-only">
<label for="tmd08-f1" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f1:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f1:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f1:checked]/filter:border-transparent peer-focus-visible/f1:outline-2 peer-focus-visible/f1:outline-offset-2 peer-focus-visible/f1:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f1:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Deep water</label>
<input type="checkbox" id="tmd08-f2" class="peer/f2 sr-only">
<label for="tmd08-f2" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f2:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f2:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f2:checked]/filter:border-transparent peer-focus-visible/f2:outline-2 peer-focus-visible/f2:outline-offset-2 peer-focus-visible/f2:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f2:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Coastal</label>
<input type="checkbox" id="tmd08-f3" class="peer/f3 sr-only">
<label for="tmd08-f3" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f3:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f3:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f3:checked]/filter:border-transparent peer-focus-visible/f3:outline-2 peer-focus-visible/f3:outline-offset-2 peer-focus-visible/f3:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f3:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Estuary</label>
</div>
<div class="grid gap-2 grid-cols-[repeat(auto-fit,minmax(min(200px,100%),1fr))] group-has-[:checked]/filter:*:hidden group-has-[#tmd08-f1:checked]/filter:*:data-[tag=deep]:block group-has-[#tmd08-f2:checked]/filter:*:data-[tag=coastal]:block group-has-[#tmd08-f3:checked]/filter:*:data-[tag=estuary]:block">
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-014 · 1 240 m</b><span class="text-xs text-tmd08-onvariant">Deep water · North Atlantic</span></article>
<article data-tag="coastal" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-059 · 312 m</b><span class="text-xs text-tmd08-onvariant">Coastal · Irish Shelf</span></article>
<article data-tag="estuary" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-022 · 86 m</b><span class="text-xs text-tmd08-onvariant">Estuary · Baltic</span></article>
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-047 · 2 010 m</b><span class="text-xs text-tmd08-onvariant">Deep water · Norwegian Sea</span></article>
</div>
<p class="text-xs text-tmd08-onvariant">Unchecked shows everything; checking a chip narrows the grid. No script — the grid states its own filter rules.</p>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Assist, suggestion & elevated</span>
<div class="flex flex-wrap gap-2">
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">event</span>Schedule dive</button>
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">near_me</span>Nearest station</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-tmd08-surface border-0 shadow-tmd08-1 text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Elevated chip</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outlinevariant text-sm font-medium text-tmd08-onvariant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Suggestion</button>
<span aria-disabled="true" class="inline-flex items-center min-h-11 px-4 rounded-lg border border-tmd08-outline text-sm font-medium text-tmd08-onsurface opacity-38 pointer-events-none">Disabled</span>
</div>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Input chips — with remove controls</span>
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center gap-1 min-h-11 pl-1.5 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary"><span class="grid place-items-center w-7 h-7 rounded-full bg-tmd08-primary text-white font-tmd08-mono text-[11px]">MK</span>M. Kovač<button type="button" aria-label="Remove M. Kovač" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">depth > 500 m<button type="button" aria-label="Remove depth filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg bg-tmd08-secondarycontainer text-tmd08-onsecondarycontainer text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">QC flagged<button type="button" aria-label="Remove QC filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-current focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
</div>
<p class="text-xs text-tmd08-onvariant">The chip shows a ring when its remove button takes focus — <span class="font-tmd08-mono text-[11px]">has-[button:focus-visible]:ring-2</span>, the wrapper reacting to a descendant.</p>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-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 Chips with Live Filtering
Source: https://codefronts.com/design-styles/tailwind-material-design-components/tailwind-material-design-chips-live-filtering/
All four M3 chip types — assist, filter, input and suggestion — and then the part the CSS version could only fake: the filter chips actually filter. Checking a chip reveals its matching cards through a has-[#id:checked]: rule on the results grid, so a live filter runs with no JavaScript at all.
## HTML
```html
<section class="tmd-08 w-full min-h-svh bg-tmd08-bg font-tmd08-sans text-tmd08-onsurface px-5 py-9" aria-label="Material Design chips demo">
<div class="w-[min(900px,100%)] mx-auto grid gap-6">
<header class="grid gap-1.5">
<h2 class="text-[26px] font-medium tracking-[-0.01em]">Chips that actually filter</h2>
<p class="text-sm text-tmd08-onvariant">Four M3 chip types, and a working facet filter built from <code class="font-tmd08-mono text-[12px]">has-[…:checked]:*:data-[tag]:block</code>.</p>
</header>
<div class="group/filter grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Filter chips — live</span>
<div class="flex flex-wrap gap-2">
<input type="checkbox" id="tmd08-f1" class="peer/f1 sr-only">
<label for="tmd08-f1" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f1:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f1:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f1:checked]/filter:border-transparent peer-focus-visible/f1:outline-2 peer-focus-visible/f1:outline-offset-2 peer-focus-visible/f1:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f1:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Deep water</label>
<input type="checkbox" id="tmd08-f2" class="peer/f2 sr-only">
<label for="tmd08-f2" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f2:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f2:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f2:checked]/filter:border-transparent peer-focus-visible/f2:outline-2 peer-focus-visible/f2:outline-offset-2 peer-focus-visible/f2:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f2:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Coastal</label>
<input type="checkbox" id="tmd08-f3" class="peer/f3 sr-only">
<label for="tmd08-f3" class="relative overflow-hidden inline-flex items-center gap-1.5 min-h-11 px-4 rounded-lg cursor-pointer border border-tmd08-outline text-sm font-medium text-tmd08-onsurface transition-colors duration-200 group-has-[#tmd08-f3:checked]/filter:bg-tmd08-secondarycontainer group-has-[#tmd08-f3:checked]/filter:text-tmd08-onsecondarycontainer group-has-[#tmd08-f3:checked]/filter:border-transparent peer-focus-visible/f3:outline-2 peer-focus-visible/f3:outline-offset-2 peer-focus-visible/f3:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon shrink-0 w-0 overflow-hidden text-[18px] leading-none transition-[width] duration-200 group-has-[#tmd08-f3:checked]/filter:w-4.5 motion-reduce:transition-none" aria-hidden="true">check</span>Estuary</label>
</div>
<div class="grid gap-2 grid-cols-[repeat(auto-fit,minmax(min(200px,100%),1fr))] group-has-[:checked]/filter:*:hidden group-has-[#tmd08-f1:checked]/filter:*:data-[tag=deep]:block group-has-[#tmd08-f2:checked]/filter:*:data-[tag=coastal]:block group-has-[#tmd08-f3:checked]/filter:*:data-[tag=estuary]:block">
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-014 · 1 240 m</b><span class="text-xs text-tmd08-onvariant">Deep water · North Atlantic</span></article>
<article data-tag="coastal" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-059 · 312 m</b><span class="text-xs text-tmd08-onvariant">Coastal · Irish Shelf</span></article>
<article data-tag="estuary" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-022 · 86 m</b><span class="text-xs text-tmd08-onvariant">Estuary · Baltic</span></article>
<article data-tag="deep" class="grid gap-1 p-4 rounded-2xl bg-tmd08-surface2"><b class="text-sm font-medium">ST-047 · 2 010 m</b><span class="text-xs text-tmd08-onvariant">Deep water · Norwegian Sea</span></article>
</div>
<p class="text-xs text-tmd08-onvariant">Unchecked shows everything; checking a chip narrows the grid. No script — the grid states its own filter rules.</p>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Assist, suggestion & elevated</span>
<div class="flex flex-wrap gap-2">
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">event</span>Schedule dive</button>
<button type="button" class="relative overflow-hidden inline-flex items-center gap-2 min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outline text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08] active:before:opacity-[0.12]"><span class="font-tmd08-icon text-[18px] leading-none text-tmd08-primary" aria-hidden="true">near_me</span>Nearest station</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-tmd08-surface border-0 shadow-tmd08-1 text-sm font-medium text-tmd08-onsurface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Elevated chip</button>
<button type="button" class="relative overflow-hidden inline-flex items-center min-h-11 px-4 rounded-lg cursor-pointer bg-transparent border border-tmd08-outlinevariant text-sm font-medium text-tmd08-onvariant focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-tmd08-primary before:absolute before:inset-0 before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]">Suggestion</button>
<span aria-disabled="true" class="inline-flex items-center min-h-11 px-4 rounded-lg border border-tmd08-outline text-sm font-medium text-tmd08-onsurface opacity-38 pointer-events-none">Disabled</span>
</div>
</div>
<div class="grid gap-4 p-5.5 rounded-3xl bg-tmd08-surface shadow-tmd08-1">
<span class="font-tmd08-mono text-[11px] uppercase tracking-[0.1em] text-tmd08-onvariant">Input chips — with remove controls</span>
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center gap-1 min-h-11 pl-1.5 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary"><span class="grid place-items-center w-7 h-7 rounded-full bg-tmd08-primary text-white font-tmd08-mono text-[11px]">MK</span>M. Kovač<button type="button" aria-label="Remove M. Kovač" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg border border-tmd08-outline text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">depth > 500 m<button type="button" aria-label="Remove depth filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-tmd08-onvariant focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
<span class="inline-flex items-center gap-1 min-h-11 pl-3 pr-1 rounded-lg bg-tmd08-secondarycontainer text-tmd08-onsecondarycontainer text-sm has-[button:focus-visible]:ring-2 has-[button:focus-visible]:ring-tmd08-primary">QC flagged<button type="button" aria-label="Remove QC filter" class="relative overflow-hidden grid place-items-center min-w-11 min-h-11 rounded-lg border-0 bg-transparent cursor-pointer text-current focus-visible:outline-none before:absolute before:inset-2 before:rounded-full before:bg-current before:opacity-0 before:transition-opacity hover:before:opacity-[0.08]"><span class="font-tmd08-icon text-[18px] leading-none" aria-hidden="true">close</span></button></span>
</div>
<p class="text-xs text-tmd08-onvariant">The chip shows a ring when its remove button takes focus — <span class="font-tmd08-mono text-[11px]">has-[button:focus-visible]:ring-2</span>, the wrapper reacting to a descendant.</p>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tmd08-bg: #fce4ec;
--color-tmd08-bg: oklch(0.941 0.028 355.4);
--color-tmd08-surface: #ffffff;
--color-tmd08-surface: oklch(1 0 89.9);
--color-tmd08-surface2: #fdf0f4;
--color-tmd08-surface2: oklch(0.966 0.015 356.6);
--color-tmd08-primary: #ad1457;
--color-tmd08-primary: oklch(0.49 0.187 2.5);
--color-tmd08-secondarycontainer: #ffd8e4;
--color-tmd08-secondarycontainer: oklch(0.918 0.046 357.6);
--color-tmd08-onsecondarycontainer: #31111d;
--color-tmd08-onsecondarycontainer: oklch(0.232 0.054 358.6);
--color-tmd08-onsurface: #1f1a1c;
--color-tmd08-onsurface: oklch(0.224 0.009 351.7);
--color-tmd08-onvariant: #5c4650;
--color-tmd08-onvariant: oklch(0.421 0.034 349.6);
--color-tmd08-outline: #85737b;
--color-tmd08-outline: oklch(0.575 0.025 349.3);
--color-tmd08-outlinevariant: #e4c8d2;
--color-tmd08-outlinevariant: oklch(0.859 0.034 354.3);
--font-tmd08-sans: 'Roboto Flex', 'Roboto', system-ui, sans-serif;
--font-tmd08-mono: 'Roboto Mono', ui-monospace, monospace;
--font-tmd08-icon: 'Material Symbols Outlined';
--shadow-tmd08-1: 0 1px 2px 0 rgb(0 0 0 / 0.3), 0 1px 3px 1px rgb(0 0 0 / 0.15);
}
.tmd-08 {
width: 100%;
min-height: 100svh;
}
.tmd-08 .font-tmd08-icon {
font-feature-settings: 'liga';
}
```How this works
A filter chip is a checkbox that grows a tick.
<div class="group/filter">
<input type="checkbox" id="tmd08-f1" class="sr-only">
<label for="tmd08-f1" class="… group-has-[#tmd08-f1:checked]/filter:bg-tmd08-secondarycontainer">
<span class="w-0 overflow-hidden transition-[width]
group-has-[#tmd08-f1:checked]/filter:w-4.5">check</span>
Deep water
</label>The tick's w-0 → w-4.5 width transition is why the M3 chip feels alive; animating opacity alone leaves a gap in the resting state.
The filter really filters. The results grid states, in utilities, which children to show for which chip:
<div class="group/filter"> … chips …
<div class="… group-has-[#tmd08-f1:checked]/filter:*:data-[tag=deep]:block">For a handful of items that is genuinely better than JS: it cannot get out of sync, it survives a re-render, and it costs no script. Past a few dozen items or several simultaneous facets, the combinatorics win and you want real code — the notes say where the line is.
Input chips keep a real button. The remove control is a <button aria-label="Remove …">, not a decorative ×, and the chip shows a ring when that button has focus via has-[button:focus-visible]:.
Make it yours
- Chip height is
min-h-11for the 44px target with the M3 32px look coming frompy-*; keep the target, shrink the paint. - Selected colour:
--color-tmd08-secondarycontainerdrives every chip type's checked state. - Add a facet by copying a chip and adding one
has-[#id:checked]:*:data-[tag=x]:blockutility to the grid. - For elevated chips swap the border for
shadow-tmd08-1; for outlined drop the shadow. - Delete the trailing dismiss button on input chips if the chip is not user-removable — an inert × is worse than none.
Gotchas — read before shipping
- The
*:child variant only reaches direct children, and the state must come from a named group on an ancestor of BOTH the chips and the grid — a barehas-[…:checked]:on the grid matches nothing, because the checkboxes are not inside it. - CSS-only faceted filtering is O(facets × items) in class lists. Two or three facets is fine; five is a stylesheet you cannot read, and that is the moment for JS.
- A chip that removes itself must move focus somewhere before it disappears, or focus falls to
<body>. That is a JS concern, which is why this demo's input chips are static. - Never nest a
<button>inside a<label>that toggles a checkbox — clicking the button also toggles. The input chips here are not labels for that reason. w-0 overflow-hiddenon the tick needsshrink-0too, or flex will negotiate the width back.- Disabled chips need
aria-disabledpluspointer-events-none;opacity-38alone leaves them clickable.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
The live filter needs :has() (Chrome 105, Safari 15.4, Firefox 121). Without it every card stays visible — the chips still select, the filter simply does not apply, which is the right degradation for a filter.