03 / 47
All 47 in 47 Tailwind CSS Toggles & Switches
- 01Tailwind iOS-Style Toggle Switch with appearance-none
- 02Tailwind Dark Mode Sun / Moon Theme Switcher
- 03Tailwind Neumorphic Soft-UI Pill Toggle
- 04Tailwind Gooey Liquid Morphing Switch with SVG Filter
- 05Tailwind Bouncy Elastic Physics Toggle (Overshoot Cubic-Bezier)
- 06Tailwind ON / OFF Text Switch with Labels Inside the Track
- 07Tailwind SaaS Pricing Toggle — Monthly / Annually Segmented Pill
- 08Tailwind Mechanical Keycap Toggles with Haptic Feedback
- 09Tailwind Waveform Mute Toggle with Animated EQ Bars
- 10Tailwind Timed Auto-Off Toggle with Conic Countdown Ring
- 11Tailwind Press-and-Hold to Confirm Toggle (Fingerprint Scan)
- 12Tailwind Split-Flap Departure Board Toggle (3D Flip)
- 13Tailwind Permission Toggle Group with :has() Live Status Badge
- 14Tailwind Material Design 3 Switch with State Layer
- 15Tailwind Skeuomorphic Wall Light Switch with Room Relight
- 16Tailwind Neon Cyberpunk Toggle with Clipped Corners
- 17Tailwind Brutalist Snap Toggle with Hard Offset Shadow
- 18Tailwind Day / Night Landscape Scene Switch
- 19Tailwind 3D Flip Card Toggle (rotateY Switch)
- 20Tailwind Sliding Pill Toggle with Label Inside the Thumb
- 21Tailwind Glassmorphism Frosted Toggle with backdrop-blur
- 22Tailwind Rotary Knob Toggle with Brushed-Metal Conic Gradient
- 23Tailwind iOS Settings List with Grouped Toggle Rows
- 24Tailwind Minimal Outline Toggle Drawn in currentColor
- 25Tailwind Squash-and-Stretch Pebble Toggle
- 26Tailwind Industrial Rocker I/O Power Switch
- 27Tailwind Accessible role=switch Toggle with forced-colors Support
- 28Tailwind Macro-Pad Toggle Grid with RGB Underglow
- 29Tailwind Radial VU Meter Mute Toggle
- 30Tailwind Segmented Pip Countdown Auto-Off Toggle
- 31Tailwind Slide-to-Confirm Toggle Rail (Drag or Arrow Keys)
- 32Tailwind Odometer Roll ON / OFF Toggle
- 33Tailwind Master Toggle with Indeterminate Tri-State
- 34Tailwind iOS Control Centre Tile Toggles
- 35Tailwind Material 3 Connected Icon Toggle Buttons
- 36Tailwind Two-Gang Wall Plate with Cumulative Room Light
- 37Tailwind Neon Toggle with Animated Conic Border Light
- 38Tailwind Brutalist Rubber-Stamp Toggle
- 39Tailwind Aurora Night Sky Toggle Switch
- 40Tailwind 3D Rotating Cube Toggle
- 41Tailwind Three-Position Off / Auto / On Selector
- 42Tailwind Liquid Glass Toggle with Specular Highlight
- 43Tailwind Three-Detent Rotary Selector Knob
- 44Tailwind Hardware Slide Switch with Knurled Grip
- 45Tailwind Battery Saver Toggle with Live Fill Meter
- 46Tailwind Morphing Sun-to-Moon Icon Toggle (SVG Stroke)
- 47Tailwind Guarded Kill Switch Under a Flip-Up Safety Cover
Tailwind Neumorphic Soft-UI Pill Toggle
Dual-source lighting as utilities: the track is carved INTO the surface with two inset shadows, the knob extrudes out of it with two outset shadows, and active:before:scale-90 presses it physically into the pit before it glides across.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttg-03 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg03-surface font-ttg03-sans" aria-label="Neumorphic pill toggle">
<label for="ttg-03-sw" class="flex items-center gap-9 px-8 py-6 rounded-3xl cursor-pointer bg-ttg03-surface shadow-[9px_9px_20px_var(--color-ttg03-shade),-9px_-9px_20px_var(--color-ttg03-light)] transition-shadow duration-300 hover:shadow-[11px_11px_24px_var(--color-ttg03-shade),-11px_-11px_24px_var(--color-ttg03-light)]">
<span class="flex flex-col gap-0.5">
<span class="text-[15.5px] font-semibold text-ttg03-ink">Focus mode</span>
<span class="text-[12.5px] text-ttg03-mute">Mutes every non-urgent ping</span>
</span>
<input type="checkbox" id="ttg-03-sw" role="switch" class="appearance-none shrink-0 w-20 h-11 rounded-full relative cursor-pointer bg-ttg03-surface transition-shadow duration-300 shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light)] checked:shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light),inset_0_0_14px_2px_color-mix(in_oklab,var(--color-ttg03-acc)_45%,transparent)] before:content-[''] before:absolute before:top-1.5 before:left-1.5 before:w-8 before:h-8 before:rounded-full before:bg-linear-145 before:from-ttg03-knobhi before:to-ttg03-knoblo before:shadow-[4px_4px_9px_var(--color-ttg03-shade),-4px_-4px_9px_var(--color-ttg03-light)] before:transition-[transform,box-shadow,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1.1,0.4,1)] checked:before:translate-x-9 checked:before:from-ttg03-acchi checked:before:to-ttg03-acc active:before:scale-90 checked:active:before:translate-x-9 checked:active:before:scale-90 focus-visible:outline-3 focus-visible:outline-ttg03-acc focus-visible:outline-offset-4 motion-reduce:transition-none motion-reduce:before:transition-none">
</label>
<p class="font-ttg03-mono text-[11px] tracking-[0.14em] uppercase text-ttg03-mute">Press it — it pushes back</p>
</section><section class="ttg-03 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg03-surface font-ttg03-sans" aria-label="Neumorphic pill toggle">
<label for="ttg-03-sw" class="flex items-center gap-9 px-8 py-6 rounded-3xl cursor-pointer bg-ttg03-surface shadow-[9px_9px_20px_var(--color-ttg03-shade),-9px_-9px_20px_var(--color-ttg03-light)] transition-shadow duration-300 hover:shadow-[11px_11px_24px_var(--color-ttg03-shade),-11px_-11px_24px_var(--color-ttg03-light)]">
<span class="flex flex-col gap-0.5">
<span class="text-[15.5px] font-semibold text-ttg03-ink">Focus mode</span>
<span class="text-[12.5px] text-ttg03-mute">Mutes every non-urgent ping</span>
</span>
<input type="checkbox" id="ttg-03-sw" role="switch" class="appearance-none shrink-0 w-20 h-11 rounded-full relative cursor-pointer bg-ttg03-surface transition-shadow duration-300 shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light)] checked:shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light),inset_0_0_14px_2px_color-mix(in_oklab,var(--color-ttg03-acc)_45%,transparent)] before:content-[''] before:absolute before:top-1.5 before:left-1.5 before:w-8 before:h-8 before:rounded-full before:bg-linear-145 before:from-ttg03-knobhi before:to-ttg03-knoblo before:shadow-[4px_4px_9px_var(--color-ttg03-shade),-4px_-4px_9px_var(--color-ttg03-light)] before:transition-[transform,box-shadow,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1.1,0.4,1)] checked:before:translate-x-9 checked:before:from-ttg03-acchi checked:before:to-ttg03-acc active:before:scale-90 checked:active:before:translate-x-9 checked:active:before:scale-90 focus-visible:outline-3 focus-visible:outline-ttg03-acc focus-visible:outline-offset-4 motion-reduce:transition-none motion-reduce:before:transition-none">
</label>
<p class="font-ttg03-mono text-[11px] tracking-[0.14em] uppercase text-ttg03-mute">Press it — it pushes back</p>
</section>@import "tailwindcss";
@theme {
--color-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}@import "tailwindcss";
@theme {
--color-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}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-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}@import "tailwindcss";
@theme {
--color-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}<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=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&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=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The four-shadow light rig stays an arbitrary
shadow-[...]on purpose. It reads as noisy in the class list, but it is one value, it sits on the element it lights, and the alternative — a@utilityfor each — hides the rig from the markup that depends on it. - Referencing
var(--color-ttg03-shade)inside the arbitrary shadow rather than hard-coding hex is what keeps the three-token retheme working;@themecolours are real custom properties at runtime, so this is free. - The compound
checked:active:before:pair is required, not redundant: without restatingtranslate-x-9, pressing a checked switch would snap the knob home for the duration of the press. - UI change from the CSS original: the card now lifts on hover (shadow grows 2px), the knob gained a real gradient in both states instead of only the off state, and the accent moved to
oklch(0.6 0.19 273)so the checked glow is visible against the surface at 50% brightness — the original blue disappeared into the shade colour.
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 Neumorphic Soft-UI Pill Toggle
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-neumorphic-soft-ui-pill-toggle/
Dual-source lighting as utilities: the track is carved INTO the surface with two inset shadows, the knob extrudes out of it with two outset shadows, and active:before:scale-90 presses it physically into the pit before it glides across.
## HTML
```html
<section class="ttg-03 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg03-surface font-ttg03-sans" aria-label="Neumorphic pill toggle">
<label for="ttg-03-sw" class="flex items-center gap-9 px-8 py-6 rounded-3xl cursor-pointer bg-ttg03-surface shadow-[9px_9px_20px_var(--color-ttg03-shade),-9px_-9px_20px_var(--color-ttg03-light)] transition-shadow duration-300 hover:shadow-[11px_11px_24px_var(--color-ttg03-shade),-11px_-11px_24px_var(--color-ttg03-light)]">
<span class="flex flex-col gap-0.5">
<span class="text-[15.5px] font-semibold text-ttg03-ink">Focus mode</span>
<span class="text-[12.5px] text-ttg03-mute">Mutes every non-urgent ping</span>
</span>
<input type="checkbox" id="ttg-03-sw" role="switch" class="appearance-none shrink-0 w-20 h-11 rounded-full relative cursor-pointer bg-ttg03-surface transition-shadow duration-300 shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light)] checked:shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light),inset_0_0_14px_2px_color-mix(in_oklab,var(--color-ttg03-acc)_45%,transparent)] before:content-[''] before:absolute before:top-1.5 before:left-1.5 before:w-8 before:h-8 before:rounded-full before:bg-linear-145 before:from-ttg03-knobhi before:to-ttg03-knoblo before:shadow-[4px_4px_9px_var(--color-ttg03-shade),-4px_-4px_9px_var(--color-ttg03-light)] before:transition-[transform,box-shadow,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1.1,0.4,1)] checked:before:translate-x-9 checked:before:from-ttg03-acchi checked:before:to-ttg03-acc active:before:scale-90 checked:active:before:translate-x-9 checked:active:before:scale-90 focus-visible:outline-3 focus-visible:outline-ttg03-acc focus-visible:outline-offset-4 motion-reduce:transition-none motion-reduce:before:transition-none">
</label>
<p class="font-ttg03-mono text-[11px] tracking-[0.14em] uppercase text-ttg03-mute">Press it — it pushes back</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}
```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 Neumorphic Soft-UI Pill Toggle
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-neumorphic-soft-ui-pill-toggle/
Dual-source lighting as utilities: the track is carved INTO the surface with two inset shadows, the knob extrudes out of it with two outset shadows, and active:before:scale-90 presses it physically into the pit before it glides across.
## HTML
```html
<section class="ttg-03 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg03-surface font-ttg03-sans" aria-label="Neumorphic pill toggle">
<label for="ttg-03-sw" class="flex items-center gap-9 px-8 py-6 rounded-3xl cursor-pointer bg-ttg03-surface shadow-[9px_9px_20px_var(--color-ttg03-shade),-9px_-9px_20px_var(--color-ttg03-light)] transition-shadow duration-300 hover:shadow-[11px_11px_24px_var(--color-ttg03-shade),-11px_-11px_24px_var(--color-ttg03-light)]">
<span class="flex flex-col gap-0.5">
<span class="text-[15.5px] font-semibold text-ttg03-ink">Focus mode</span>
<span class="text-[12.5px] text-ttg03-mute">Mutes every non-urgent ping</span>
</span>
<input type="checkbox" id="ttg-03-sw" role="switch" class="appearance-none shrink-0 w-20 h-11 rounded-full relative cursor-pointer bg-ttg03-surface transition-shadow duration-300 shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light)] checked:shadow-[inset_5px_5px_10px_var(--color-ttg03-shade),inset_-5px_-5px_10px_var(--color-ttg03-light),inset_0_0_14px_2px_color-mix(in_oklab,var(--color-ttg03-acc)_45%,transparent)] before:content-[''] before:absolute before:top-1.5 before:left-1.5 before:w-8 before:h-8 before:rounded-full before:bg-linear-145 before:from-ttg03-knobhi before:to-ttg03-knoblo before:shadow-[4px_4px_9px_var(--color-ttg03-shade),-4px_-4px_9px_var(--color-ttg03-light)] before:transition-[transform,box-shadow,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1.1,0.4,1)] checked:before:translate-x-9 checked:before:from-ttg03-acchi checked:before:to-ttg03-acc active:before:scale-90 checked:active:before:translate-x-9 checked:active:before:scale-90 focus-visible:outline-3 focus-visible:outline-ttg03-acc focus-visible:outline-offset-4 motion-reduce:transition-none motion-reduce:before:transition-none">
</label>
<p class="font-ttg03-mono text-[11px] tracking-[0.14em] uppercase text-ttg03-mute">Press it — it pushes back</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg03-surface: #e2e7ee;
--color-ttg03-surface: oklch(0.92 0.008 255);
--color-ttg03-shade: #b6bdc9;
--color-ttg03-shade: oklch(0.79 0.015 255);
--color-ttg03-light: #ffffff;
--color-ttg03-light: oklch(1 0 0);
--color-ttg03-knobhi: #f2f5fa;
--color-ttg03-knobhi: oklch(0.97 0.005 255);
--color-ttg03-knoblo: #d2d8e2;
--color-ttg03-knoblo: oklch(0.87 0.01 255);
--color-ttg03-acc: #5b6ef5;
--color-ttg03-acc: oklch(0.6 0.19 273);
--color-ttg03-acchi: #97a3ff;
--color-ttg03-acchi: oklch(0.74 0.13 273);
--color-ttg03-ink: #4f5765;
--color-ttg03-ink: oklch(0.44 0.02 260);
--color-ttg03-mute: #8c94a3;
--color-ttg03-mute: oklch(0.65 0.015 260);
--font-ttg03-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-ttg03-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
}
.ttg-03 {
width: 100%;
min-height: 100svh;
}
.ttg-03 input[type="checkbox"] {
-webkit-appearance: none;
}
```How this works
Neumorphism is two shadows, everywhere. Every element carries a dark shadow toward the bottom-right and a light one toward the top-left. The track uses the inset forms and reads as debossed; the knob uses the outset forms and reads as embossed from the same material. In Tailwind both are single arbitrary shadow-[...] values, which is the honest answer — there is no design-scale token here, just a light rig.
The press is scale, the travel is translate, and they compose. active:before:scale-90 alone would reset the thumb to the left when pressed mid-travel, so the checked state restates both: checked:active:before:translate-x-9 checked:active:before:scale-90. Tailwind stacks variants left-to-right, so that compiles to exactly the compound selector the CSS version wrote by hand.
Contrast is the known weakness. Soft UI is low-contrast by construction, so the checked state adds colour (the inset accent glow, a tinted knob) rather than relying on knob position, and the focus ring is a hard 3px outline that ignores the illusion entirely.
Make it yours
- Retheme by moving
--color-ttg03-surface,-shadeand-lighttogether — they must stay one family or the illusion dies. - Deepen the carve: raise the inset blur and offset in the track's
shadow-[...]. - Squircle variant:
rounded-2xlon track and knob instead ofrounded-full. - Change the on-state entirely through
--color-ttg03-acc; the glow, the knob gradient and the focus ring all read it.
Gotchas — read before shipping
- Track, knob and page must share the SAME base surface colour. A card that is even 2% lighter than the page breaks the molded-from-one-material illusion instantly.
- Never animate the press by shrinking width/height —
scaleis compositor-only; size is a relayout every frame. - Soft shadows fail WCAG on their own. Keep the accent state and the focus ring strong, as here, or the control is unusable for low-vision users.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Layered box-shadows and transforms are universal; color-mix() in the checked glow is the only modern requirement and matches Tailwind v4's own floor.