26 / 28
All 28 in 28 Tailwind Hamburger Menus
- 01Tailwind Hamburger Menu with Clip-Path Circle Expansion
- 02Tailwind Keyboard Navigable Hamburger Menu with ARIA Attributes
- 03Tailwind Animated 3-Line to X Hamburger Icon Toggle
- 04Tailwind Slide-In Off-Canvas Drawer with Dimmed Backdrop Overlay
- 05Pure Tailwind Checkbox Hack Hamburger Navigation (No JavaScript)
- 06Tailwind Responsive Hamburger Menu with Integrated Search Bar & Social Links
- 07Tailwind Glassmorphism Backdrop Blur Hamburger Navigation
- 08Tailwind Push Page Navigation Menu (Off-Canvas Content Shift)
- 09Pure Tailwind Hamburger Menu Without JavaScript
- 10Tailwind Hamburger Menu Morphing to Close Icon Animation
- 11Tailwind Full Screen Mobile Navigation Overlay
- 12Tailwind Slide Out Sidebar Navigation / Off Canvas Menu
- 13Tailwind Responsive Navbar Mobile Hamburger Toggle Breakpoints
- 14Tailwind Minimalist Floating Hamburger Button
- 15Tailwind CSS Responsive Hamburger Menu Component
- 16Tailwind Rebuild of the Bootstrap 5 Navbar Hamburger Toggle
- 17Tailwind WCAG Accessible Hamburger Menu Example
- 18Pure Tailwind Dropdown Slide Down Mobile Hamburger Menu
- 19Simple 3-Line Tailwind Hamburger Icon Hover Effect
- 20Tailwind Full Screen Blur Overlay Hamburger Menu
- 21Tailwind Mobile Bottom Navigation Bar with Floating Hamburger Toggle
- 22Pure Tailwind Multi-Level Accordion Hamburger Dropdown Menu
- 23Flexbox Navbar Right-Aligned Tailwind Hamburger Icon
- 24Radial Ripple Expand Tailwind Hamburger Menu
- 25Sticky Tailwind Navbar Shrinking Hamburger Icon on Scroll
- 26Neumorphic Tailwind Hamburger Toggle Button
- 27Brutalist Design Bordered Tailwind Hamburger Menu
- 28Tailwind 3D Flip Canvas Mobile Hamburger Navigation
Neumorphic Tailwind Hamburger Toggle Button
Soft-UI physics done by the rules: the burger is a clay button raised by the canonical dual shadow, and pressing it does not move it — it inverts the shadows to inset, so the button sinks INTO the surface. In utilities both shadow states sit in one class list, which makes the light-source discipline that neumorphism lives by impossible to get wrong by accident.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="thb-26 w-full min-h-svh bg-thb26-clay font-thb26-sans text-thb26-ink px-5 py-9" aria-label="Neumorphic hamburger toggle demo">
<div class="group relative min-h-120 flex flex-col items-center gap-6.5 px-7 py-8.5 rounded-[22px] bg-thb26-clay border border-thb26-edge">
<input type="checkbox" id="thb26-t" class="peer absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">
<div class="flex flex-col items-center gap-3.5">
<label for="thb26-t" class="grid place-items-center w-18.5 h-18.5 rounded-[22px] bg-thb26-clay cursor-pointer select-none shadow-[-7px_-7px_14px_var(--color-thb26-lite),7px_7px_14px_var(--color-thb26-shade)] transition-[box-shadow] duration-300 hover:shadow-[-9px_-9px_18px_var(--color-thb26-lite),9px_9px_18px_var(--color-thb26-shade)] peer-focus-visible:outline-3 peer-focus-visible:outline-thb26-acc peer-focus-visible:outline-offset-3 group-has-checked:shadow-[inset_-5px_-5px_10px_var(--color-thb26-lite),inset_5px_5px_10px_var(--color-thb26-shade)]">
<span class="relative block w-6 h-4" aria-hidden="true">
<i class="absolute left-0 top-0 w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:translate-y-[6.7px] group-has-checked:rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[6.7px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[opacity,scale,background] duration-250 ease-thb26 group-has-checked:opacity-0 group-has-checked:scale-x-40 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[13.4px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:-translate-y-[6.7px] group-has-checked:-rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
</span>
</label>
<span class="relative block h-4 overflow-hidden font-thb26-mono text-[11px] tracking-[0.22em] uppercase text-thb26-mut" aria-hidden="true">
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">rest</b>
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">pressed</b>
</span>
</div>
<nav aria-label="Menu" class="grid gap-3.5 w-[min(280px,100%)] p-1.5">
<a href="#home" style="--i:0" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Home
</a>
<a href="#library" style="--i:1" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Library
</a>
<a href="#stats" style="--i:2" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Statistics
</a>
<a href="#settings" style="--i:3" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Settings
</a>
</nav>
<p class="mt-auto max-w-[34ch] text-center text-xs leading-relaxed text-thb26-mut text-pretty" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code class="font-thb26-mono text-[11px] px-1.5 py-0.5 rounded bg-thb26-code">inset</code>. Note the focus ring stays loud on purpose.</p>
</div>
</section><section class="thb-26 w-full min-h-svh bg-thb26-clay font-thb26-sans text-thb26-ink px-5 py-9" aria-label="Neumorphic hamburger toggle demo">
<div class="group relative min-h-120 flex flex-col items-center gap-6.5 px-7 py-8.5 rounded-[22px] bg-thb26-clay border border-thb26-edge">
<input type="checkbox" id="thb26-t" class="peer absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">
<div class="flex flex-col items-center gap-3.5">
<label for="thb26-t" class="grid place-items-center w-18.5 h-18.5 rounded-[22px] bg-thb26-clay cursor-pointer select-none shadow-[-7px_-7px_14px_var(--color-thb26-lite),7px_7px_14px_var(--color-thb26-shade)] transition-[box-shadow] duration-300 hover:shadow-[-9px_-9px_18px_var(--color-thb26-lite),9px_9px_18px_var(--color-thb26-shade)] peer-focus-visible:outline-3 peer-focus-visible:outline-thb26-acc peer-focus-visible:outline-offset-3 group-has-checked:shadow-[inset_-5px_-5px_10px_var(--color-thb26-lite),inset_5px_5px_10px_var(--color-thb26-shade)]">
<span class="relative block w-6 h-4" aria-hidden="true">
<i class="absolute left-0 top-0 w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:translate-y-[6.7px] group-has-checked:rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[6.7px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[opacity,scale,background] duration-250 ease-thb26 group-has-checked:opacity-0 group-has-checked:scale-x-40 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[13.4px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:-translate-y-[6.7px] group-has-checked:-rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
</span>
</label>
<span class="relative block h-4 overflow-hidden font-thb26-mono text-[11px] tracking-[0.22em] uppercase text-thb26-mut" aria-hidden="true">
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">rest</b>
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">pressed</b>
</span>
</div>
<nav aria-label="Menu" class="grid gap-3.5 w-[min(280px,100%)] p-1.5">
<a href="#home" style="--i:0" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Home
</a>
<a href="#library" style="--i:1" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Library
</a>
<a href="#stats" style="--i:2" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Statistics
</a>
<a href="#settings" style="--i:3" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Settings
</a>
</nav>
<p class="mt-auto max-w-[34ch] text-center text-xs leading-relaxed text-thb26-mut text-pretty" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code class="font-thb26-mono text-[11px] px-1.5 py-0.5 rounded bg-thb26-code">inset</code>. Note the focus ring stays loud on purpose.</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}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-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Both material states live on the element: the outset pair and the
group-has-checked:shadow-[inset_…]twin sit adjacent in one class list, which makes the sign-flip discipline neumorphism depends on visually checkable. - The three-token theme (
clay,lite,shade) becomes@themeentries, and the shadows read them throughvar()inside arbitrary values — so a retheme is still three lines, exactly as in the CSS version. :checked ~ .plate .btnand its five siblings becomegroup-has-checked:utilities, reaching the button, three icon bars, the word swap and four menu pills from one clipped checkbox.- The pressed state's colour change is now typed next to the shadow inversion, which is the point: shadow-only state signalling is the accessibility failure this style is known for.
- The pills' pressed state uses
active:shadow-[inset_…], keeping the inversion grammar identical across controls without a second rule. - The loud focus ring survives as
focus-visible:outline-3 outline-offset-3on every control — verbose, and that verbosity is what stops it being quietly dropped for aesthetics.
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: Neumorphic Tailwind Hamburger Toggle Button
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/neumorphic-tailwind-hamburger-toggle-button/
Soft-UI physics done by the rules: the burger is a clay button raised by the canonical dual shadow, and pressing it does not move it — it inverts the shadows to inset, so the button sinks INTO the surface. In utilities both shadow states sit in one class list, which makes the light-source discipline that neumorphism lives by impossible to get wrong by accident.
## HTML
```html
<section class="thb-26 w-full min-h-svh bg-thb26-clay font-thb26-sans text-thb26-ink px-5 py-9" aria-label="Neumorphic hamburger toggle demo">
<div class="group relative min-h-120 flex flex-col items-center gap-6.5 px-7 py-8.5 rounded-[22px] bg-thb26-clay border border-thb26-edge">
<input type="checkbox" id="thb26-t" class="peer absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">
<div class="flex flex-col items-center gap-3.5">
<label for="thb26-t" class="grid place-items-center w-18.5 h-18.5 rounded-[22px] bg-thb26-clay cursor-pointer select-none shadow-[-7px_-7px_14px_var(--color-thb26-lite),7px_7px_14px_var(--color-thb26-shade)] transition-[box-shadow] duration-300 hover:shadow-[-9px_-9px_18px_var(--color-thb26-lite),9px_9px_18px_var(--color-thb26-shade)] peer-focus-visible:outline-3 peer-focus-visible:outline-thb26-acc peer-focus-visible:outline-offset-3 group-has-checked:shadow-[inset_-5px_-5px_10px_var(--color-thb26-lite),inset_5px_5px_10px_var(--color-thb26-shade)]">
<span class="relative block w-6 h-4" aria-hidden="true">
<i class="absolute left-0 top-0 w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:translate-y-[6.7px] group-has-checked:rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[6.7px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[opacity,scale,background] duration-250 ease-thb26 group-has-checked:opacity-0 group-has-checked:scale-x-40 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[13.4px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:-translate-y-[6.7px] group-has-checked:-rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
</span>
</label>
<span class="relative block h-4 overflow-hidden font-thb26-mono text-[11px] tracking-[0.22em] uppercase text-thb26-mut" aria-hidden="true">
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">rest</b>
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">pressed</b>
</span>
</div>
<nav aria-label="Menu" class="grid gap-3.5 w-[min(280px,100%)] p-1.5">
<a href="#home" style="--i:0" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Home
</a>
<a href="#library" style="--i:1" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Library
</a>
<a href="#stats" style="--i:2" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Statistics
</a>
<a href="#settings" style="--i:3" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Settings
</a>
</nav>
<p class="mt-auto max-w-[34ch] text-center text-xs leading-relaxed text-thb26-mut text-pretty" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code class="font-thb26-mono text-[11px] px-1.5 py-0.5 rounded bg-thb26-code">inset</code>. Note the focus ring stays loud on purpose.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}
```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: Neumorphic Tailwind Hamburger Toggle Button
Source: https://codefronts.com/navigation/tailwind-hamburger-menus/neumorphic-tailwind-hamburger-toggle-button/
Soft-UI physics done by the rules: the burger is a clay button raised by the canonical dual shadow, and pressing it does not move it — it inverts the shadows to inset, so the button sinks INTO the surface. In utilities both shadow states sit in one class list, which makes the light-source discipline that neumorphism lives by impossible to get wrong by accident.
## HTML
```html
<section class="thb-26 w-full min-h-svh bg-thb26-clay font-thb26-sans text-thb26-ink px-5 py-9" aria-label="Neumorphic hamburger toggle demo">
<div class="group relative min-h-120 flex flex-col items-center gap-6.5 px-7 py-8.5 rounded-[22px] bg-thb26-clay border border-thb26-edge">
<input type="checkbox" id="thb26-t" class="peer absolute w-px h-px overflow-hidden whitespace-nowrap [clip-path:inset(50%)]">
<div class="flex flex-col items-center gap-3.5">
<label for="thb26-t" class="grid place-items-center w-18.5 h-18.5 rounded-[22px] bg-thb26-clay cursor-pointer select-none shadow-[-7px_-7px_14px_var(--color-thb26-lite),7px_7px_14px_var(--color-thb26-shade)] transition-[box-shadow] duration-300 hover:shadow-[-9px_-9px_18px_var(--color-thb26-lite),9px_9px_18px_var(--color-thb26-shade)] peer-focus-visible:outline-3 peer-focus-visible:outline-thb26-acc peer-focus-visible:outline-offset-3 group-has-checked:shadow-[inset_-5px_-5px_10px_var(--color-thb26-lite),inset_5px_5px_10px_var(--color-thb26-shade)]">
<span class="relative block w-6 h-4" aria-hidden="true">
<i class="absolute left-0 top-0 w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:translate-y-[6.7px] group-has-checked:rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[6.7px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[opacity,scale,background] duration-250 ease-thb26 group-has-checked:opacity-0 group-has-checked:scale-x-40 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
<i class="absolute left-0 top-[13.4px] w-6 h-[2.6px] rounded-[3px] bg-thb26-ink transition-[translate,rotate,background] duration-300 ease-thb26 group-has-checked:-translate-y-[6.7px] group-has-checked:-rotate-45 group-has-checked:bg-thb26-acc motion-reduce:transition-none"></i>
</span>
</label>
<span class="relative block h-4 overflow-hidden font-thb26-mono text-[11px] tracking-[0.22em] uppercase text-thb26-mut" aria-hidden="true">
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">rest</b>
<b class="block h-4 leading-4 font-bold transition-[translate] duration-350 ease-thb26 group-has-checked:-translate-y-4">pressed</b>
</span>
</div>
<nav aria-label="Menu" class="grid gap-3.5 w-[min(280px,100%)] p-1.5">
<a href="#home" style="--i:0" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Home
</a>
<a href="#library" style="--i:1" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Library
</a>
<a href="#stats" style="--i:2" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Statistics
</a>
<a href="#settings" style="--i:3" class="group/p flex items-center gap-3.25 min-h-13 px-5 rounded-2xl bg-thb26-clay text-thb26-ink text-sm font-bold no-underline shadow-[-6px_-6px_12px_var(--color-thb26-lite),6px_6px_12px_var(--color-thb26-shade)] opacity-0 translate-y-3 pointer-events-none transition-[box-shadow,opacity,translate,color] duration-350 ease-thb26 hover:text-thb26-acc focus-visible:outline-3 focus-visible:outline-thb26-acc focus-visible:outline-offset-2 active:shadow-[inset_-4px_-4px_8px_var(--color-thb26-lite),inset_4px_4px_8px_var(--color-thb26-shade)] group-has-checked:opacity-100 group-has-checked:translate-y-0 group-has-checked:pointer-events-auto group-has-checked:[transition-delay:calc(80ms+var(--i)*50ms)] motion-reduce:translate-y-0 motion-reduce:duration-150">
<i class="w-2 h-2 rounded-full bg-thb26-clay shadow-[inset_-2px_-2px_4px_var(--color-thb26-lite),inset_2px_2px_4px_var(--color-thb26-shade)] transition-[background,box-shadow] duration-200 group-hover/p:bg-thb26-acc group-hover/p:shadow-none group-focus-visible/p:bg-thb26-acc group-focus-visible/p:shadow-none" aria-hidden="true"></i>Settings
</a>
</nav>
<p class="mt-auto max-w-[34ch] text-center text-xs leading-relaxed text-thb26-mut text-pretty" aria-hidden="true">same clay, two shadows — pressed = the pair goes <code class="font-thb26-mono text-[11px] px-1.5 py-0.5 rounded bg-thb26-code">inset</code>. Note the focus ring stays loud on purpose.</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-thb26-clay: #e6e8ef;
--color-thb26-clay: oklch(0.92 0.012 260);
--color-thb26-lite: #fbfcff;
--color-thb26-lite: oklch(0.99 0.005 260);
--color-thb26-shade: #bcc0cd;
--color-thb26-shade: oklch(0.78 0.02 260);
--color-thb26-edge: #d5d8e2;
--color-thb26-edge: oklch(0.87 0.014 260);
--color-thb26-code: #d9dce6;
--color-thb26-code: oklch(0.88 0.014 260);
--color-thb26-ink: #454a5c;
--color-thb26-ink: oklch(0.32 0.02 262);
--color-thb26-mut: #6f7488;
--color-thb26-mut: oklch(0.52 0.018 262);
--color-thb26-acc: #3fa8b4;
--color-thb26-acc: oklch(0.65 0.11 190);
--font-thb26-sans: 'Space Grotesk', system-ui, sans-serif;
--font-thb26-mono: 'JetBrains Mono', ui-monospace, monospace;
--ease-thb26: cubic-bezier(0.2, 0.7, 0.2, 1);
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh. It scopes the demo, and it holds the stage at full height in the
moment before the browser build has compiled its utilities. */
.thb-26 {
width: 100%;
min-height: 100svh;
}
```How this works
One material rule, applied consistently. Every element is the SAME colour as the background, shaped purely by two opposing shadows from one imaginary light:
<label class="bg-thb26-clay
shadow-[-7px_-7px_14px_var(--color-thb26-lite),7px_7px_14px_var(--color-thb26-shade)]
group-has-checked:shadow-[inset_-5px_-5px_10px_var(--color-thb26-lite),inset_5px_5px_10px_var(--color-thb26-shade)]">Raised is the outset pair; pressed is the SAME pair inset. That inversion is the entire interaction language — no translate, no scale; the light does the talking. Having both states adjacent in the class list is what makes the port worthwhile here: the #1 neumorphic bug is copy-pasted shadows with flipped signs, and side-by-side they are trivially checkable.
Radii and depth follow the material. Soft clay wants generous rounding, and the menu pills raise with a wider, softer shadow because farther from the surface means softer.
The accessibility warning, printed because this style is famous for failing audits. Neumorphism is intrinsically low-contrast: the button's edge against the surface is often under 1.4:1, far below the 3:1 WCAG 1.4.11 wants for control boundaries. This build compensates where it counts — ink-dark lines at 7.8:1, a real focus ring, and a pressed state that changes colour as well as shadow. Treat soft UI as a skin over compliant bones, not a substitute for them.
Make it yours
- The whole theme is three tokens:
--color-thb26-clay,-lite,-shade. Warm clay at hue 60, cool porcelain at 260 as shipped. Keep lightness within ±0.10 of the surface or the shadows go muddy. - Depth: 7px offset with 14px blur is standard, 4/8 is a subtle plaque, 10/24 is a pillow. Offset and blur scale together, roughly 1:2.
- The menu pills flatten on press with the same inversion — restyle only the shadow recipe to retheme every control.
- Add one saturated accent (the teal here) sparingly. Pure monochrome neumorphism reads as decoration rather than UI.
- Stagger tempo for the pills is the
50msmultiplier in their[transition-delay:calc(…)].
Gotchas — read before shipping
- Neumorphism only exists on midtone surfaces. On white the light shadow vanishes, on near-black the dark one does — if your brand background is either, this is the wrong pattern.
- Keep the light source fixed forever. One element lit from elsewhere shatters the illusion, and with utilities that means checking the sign order every time you copy a shadow.
- Soft shadows are paint-heavy. Fifty neumorphic elements measurably slow low-end scroll; this is a hero-element style, not a system.
- Never remove the focus outline because it “ruins the softness”. That instinct is how soft-UI fails WCAG 2.4.7 — ring it anyway, offset outside the clay.
- Do not signal state with shadow alone. The pressed state here also recolours the icon bars, because a shadow inversion is invisible to a low-vision user.
shadow-[…]with multiple layers must be typed closed-up with underscores; a space anywhere in the arbitrary value silently drops the whole utility.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Dual box-shadows and inset transitions run in every engine of the last decade; the floor quoted here is Tailwind v4's own generated sheet. This is also the easiest demo to port back to hex colours.