12 / 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 Split-Flap Departure Board Toggle (3D Flip)
An airport departure-board cell that flips between OFF and ON: the top half hinges down over the bottom in real 3D, revealing the new word exactly like a mechanical split-flap. Four half-height layers, one rotate-x-180, zero JavaScript.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttg-12 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg12-bg font-ttg12-mono" aria-label="Split-flap board toggle">
<label for="ttg-12-sw" class="flex flex-col items-center gap-4 cursor-pointer">
<input type="checkbox" id="ttg-12-sw" role="switch" aria-label="Departures feed" class="peer sr-only">
<span aria-hidden="true" class="relative w-[150px] h-24 perspective-[500px] drop-shadow-[0_14px_24px_oklch(0_0_0/0.6)] peer-checked:[&_.ttg12-flap]:-rotate-x-180 peer-focus-visible:outline-3 peer-focus-visible:outline-ttg12-glyph peer-focus-visible:outline-offset-[5px] peer-focus-visible:rounded-[10px]">
<b class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">ON</i></b>
<b class="absolute top-12 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">OFF</i></b>
<b class="ttg12-flap absolute top-0 left-0 w-[150px] h-12 transform-3d origin-bottom transition-transform duration-[600ms] ease-[cubic-bezier(0.4,0,0.6,1)] motion-reduce:duration-[10ms]">
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg backface-hidden bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">OFF</i></span>
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg backface-hidden rotate-x-180 bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">ON</i></span>
</b>
<em class="absolute top-[47px] left-0 right-0 h-0.5 z-[5] bg-ttg12-hinge before:content-[''] before:absolute before:-top-1 before:-left-0.5 before:w-[5px] before:h-2.5 before:rounded-sm before:bg-ttg12-hinge after:content-[''] after:absolute after:-top-1 after:-right-0.5 after:w-[5px] after:h-2.5 after:rounded-sm after:bg-ttg12-hinge"></em>
</span>
<span class="text-[13px] tracking-[0.16em] uppercase text-ttg12-label">Departures feed</span>
</label>
<p class="font-ttg12-sans text-[11px] tracking-[0.14em] uppercase text-ttg12-mute">Mechanical split-flap · click to flip</p>
</section><section class="ttg-12 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg12-bg font-ttg12-mono" aria-label="Split-flap board toggle">
<label for="ttg-12-sw" class="flex flex-col items-center gap-4 cursor-pointer">
<input type="checkbox" id="ttg-12-sw" role="switch" aria-label="Departures feed" class="peer sr-only">
<span aria-hidden="true" class="relative w-[150px] h-24 perspective-[500px] drop-shadow-[0_14px_24px_oklch(0_0_0/0.6)] peer-checked:[&_.ttg12-flap]:-rotate-x-180 peer-focus-visible:outline-3 peer-focus-visible:outline-ttg12-glyph peer-focus-visible:outline-offset-[5px] peer-focus-visible:rounded-[10px]">
<b class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">ON</i></b>
<b class="absolute top-12 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">OFF</i></b>
<b class="ttg12-flap absolute top-0 left-0 w-[150px] h-12 transform-3d origin-bottom transition-transform duration-[600ms] ease-[cubic-bezier(0.4,0,0.6,1)] motion-reduce:duration-[10ms]">
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg backface-hidden bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">OFF</i></span>
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg backface-hidden rotate-x-180 bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">ON</i></span>
</b>
<em class="absolute top-[47px] left-0 right-0 h-0.5 z-[5] bg-ttg12-hinge before:content-[''] before:absolute before:-top-1 before:-left-0.5 before:w-[5px] before:h-2.5 before:rounded-sm before:bg-ttg12-hinge after:content-[''] after:absolute after:-top-1 after:-right-0.5 after:w-[5px] after:h-2.5 after:rounded-sm after:bg-ttg12-hinge"></em>
</span>
<span class="text-[13px] tracking-[0.16em] uppercase text-ttg12-label">Departures feed</span>
</label>
<p class="font-ttg12-sans text-[11px] tracking-[0.14em] uppercase text-ttg12-mute">Mechanical split-flap · click to flip</p>
</section>@import "tailwindcss";
@theme {
--color-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
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-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
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=JetBrains+Mono:wght@400;500;700&family=Plus+Jakarta+Sans: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=JetBrains+Mono:wght@400;500;700&family=Plus+Jakarta+Sans:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- v4 ships
transform-3d,backface-hidden,perspective-*,rotate-x-*andorigin-bottomas real utilities, so this demo — the most 3D-heavy in the collection — needed zero arbitrary properties. In v3 every one of those was a plugin or a hand-written rule. - The half-glyph trick survives the port untouched:
h-12 overflow-hiddenwithleading-[96px], and-translate-y-12on the bottom halves. It is the only way to show half a letter without duplicating the font metrics in two places. motion-reduce:duration-[10ms]rather thantransition-none, deliberately: the flap must still end in the right position, and a 10ms transition is an instant swap that cannot leave the element mid-rotation.- UI change from the CSS original: the glyph amber moved to
oklch(0.89 0.12 92)(13:1 on the card, from 9:1), the label and caption inks were separated into two tones so the board reads as the primary object, and the caption dropped to a sans face so it stops competing with the board's monospace.
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 Split-Flap Departure Board Toggle (3D Flip)
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-split-flap-departure-board-toggle-3d-flip/
An airport departure-board cell that flips between OFF and ON: the top half hinges down over the bottom in real 3D, revealing the new word exactly like a mechanical split-flap. Four half-height layers, one rotate-x-180, zero JavaScript.
## HTML
```html
<section class="ttg-12 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg12-bg font-ttg12-mono" aria-label="Split-flap board toggle">
<label for="ttg-12-sw" class="flex flex-col items-center gap-4 cursor-pointer">
<input type="checkbox" id="ttg-12-sw" role="switch" aria-label="Departures feed" class="peer sr-only">
<span aria-hidden="true" class="relative w-[150px] h-24 perspective-[500px] drop-shadow-[0_14px_24px_oklch(0_0_0/0.6)] peer-checked:[&_.ttg12-flap]:-rotate-x-180 peer-focus-visible:outline-3 peer-focus-visible:outline-ttg12-glyph peer-focus-visible:outline-offset-[5px] peer-focus-visible:rounded-[10px]">
<b class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">ON</i></b>
<b class="absolute top-12 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">OFF</i></b>
<b class="ttg12-flap absolute top-0 left-0 w-[150px] h-12 transform-3d origin-bottom transition-transform duration-[600ms] ease-[cubic-bezier(0.4,0,0.6,1)] motion-reduce:duration-[10ms]">
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg backface-hidden bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">OFF</i></span>
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg backface-hidden rotate-x-180 bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">ON</i></span>
</b>
<em class="absolute top-[47px] left-0 right-0 h-0.5 z-[5] bg-ttg12-hinge before:content-[''] before:absolute before:-top-1 before:-left-0.5 before:w-[5px] before:h-2.5 before:rounded-sm before:bg-ttg12-hinge after:content-[''] after:absolute after:-top-1 after:-right-0.5 after:w-[5px] after:h-2.5 after:rounded-sm after:bg-ttg12-hinge"></em>
</span>
<span class="text-[13px] tracking-[0.16em] uppercase text-ttg12-label">Departures feed</span>
</label>
<p class="font-ttg12-sans text-[11px] tracking-[0.14em] uppercase text-ttg12-mute">Mechanical split-flap · click to flip</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
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: Tailwind Split-Flap Departure Board Toggle (3D Flip)
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-split-flap-departure-board-toggle-3d-flip/
An airport departure-board cell that flips between OFF and ON: the top half hinges down over the bottom in real 3D, revealing the new word exactly like a mechanical split-flap. Four half-height layers, one rotate-x-180, zero JavaScript.
## HTML
```html
<section class="ttg-12 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg12-bg font-ttg12-mono" aria-label="Split-flap board toggle">
<label for="ttg-12-sw" class="flex flex-col items-center gap-4 cursor-pointer">
<input type="checkbox" id="ttg-12-sw" role="switch" aria-label="Departures feed" class="peer sr-only">
<span aria-hidden="true" class="relative w-[150px] h-24 perspective-[500px] drop-shadow-[0_14px_24px_oklch(0_0_0/0.6)] peer-checked:[&_.ttg12-flap]:-rotate-x-180 peer-focus-visible:outline-3 peer-focus-visible:outline-ttg12-glyph peer-focus-visible:outline-offset-[5px] peer-focus-visible:rounded-[10px]">
<b class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">ON</i></b>
<b class="absolute top-12 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">OFF</i></b>
<b class="ttg12-flap absolute top-0 left-0 w-[150px] h-12 transform-3d origin-bottom transition-transform duration-[600ms] ease-[cubic-bezier(0.4,0,0.6,1)] motion-reduce:duration-[10ms]">
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-t-lg backface-hidden bg-ttg12-cardhi text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px]">OFF</i></span>
<span class="absolute top-0 left-0 w-[150px] h-12 overflow-hidden rounded-b-lg backface-hidden rotate-x-180 bg-ttg12-cardlo text-ttg12-glyph text-[44px] font-bold tracking-[0.04em] text-center"><i class="not-italic block leading-[96px] -translate-y-12">ON</i></span>
</b>
<em class="absolute top-[47px] left-0 right-0 h-0.5 z-[5] bg-ttg12-hinge before:content-[''] before:absolute before:-top-1 before:-left-0.5 before:w-[5px] before:h-2.5 before:rounded-sm before:bg-ttg12-hinge after:content-[''] after:absolute after:-top-1 after:-right-0.5 after:w-[5px] after:h-2.5 after:rounded-sm after:bg-ttg12-hinge"></em>
</span>
<span class="text-[13px] tracking-[0.16em] uppercase text-ttg12-label">Departures feed</span>
</label>
<p class="font-ttg12-sans text-[11px] tracking-[0.14em] uppercase text-ttg12-mute">Mechanical split-flap · click to flip</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg12-bg: #14130f;
--color-ttg12-bg: oklch(0.19 0.008 90);
--color-ttg12-cardhi: #262320;
--color-ttg12-cardhi: oklch(0.27 0.008 75);
--color-ttg12-cardlo: #1e1c19;
--color-ttg12-cardlo: oklch(0.23 0.008 75);
--color-ttg12-glyph: #f7dc84;
--color-ttg12-glyph: oklch(0.89 0.12 92);
--color-ttg12-hinge: #0d0c0a;
--color-ttg12-hinge: oklch(0.15 0.006 90);
--color-ttg12-label: #a49b81;
--color-ttg12-label: oklch(0.7 0.03 92);
--color-ttg12-mute: #6f684f;
--color-ttg12-mute: oklch(0.52 0.03 92);
--font-ttg12-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;
--font-ttg12-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-12 {
width: 100%;
min-height: 100svh;
}
```How this works
Four layers, two words. A static top half shows the NEW word's upper half; a static bottom half shows the OLD word's lower half; between them a hinged flap carries two faces — front is the old word's top, back (pre-rotated 180°) is the new word's bottom. Each half is a 48px overflow-hidden box with 96px line-height inside; the bottom halves shift their glyph up by exactly 48px.
The flip is one transform. Checking rotates the flap -rotate-x-180 around its bottom edge inside a perspective-[500px] parent. The front face turns away, backface-hidden removes it, and the pre-rotated back face lands as the new bottom half. Unchecking plays the same hinge in reverse for free, which is why this is a transition and not a keyframe animation.
The mechanism sells it. A 2px hinge line and two side pins, all absolute spans — without them the eye reads a card flip rather than a flap.
Make it yours
- Change the words, keeping them the same length: OPEN/SHUT, LIVE/DARK, YES/NO.
- Ripple a whole board by stacking cells with staggered
delay-[...]utilities. - Classic airport palette: amber glyphs on near-black, which is already most of the way there.
- Slow the flap to
duration-[800ms]and add an overshoot curve for a mechanical bounce.
Gotchas — read before shipping
- The back face must be pre-rotated INSIDE a
transform-3dflap, or it renders mirrored. - All four text layers need identical font metrics and line-height, or the halves misalign at the hinge line — this is why the demo uses one monospace family and one size.
perspective-*belongs on the parent. On the flap itself the fold flattens into a scale.- Under
motion-reduce:this becomes an instant swap, not a slow flip: a 3D rotation is exactly the motion that triggers vestibular discomfort.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
3D transforms and backface-visibility are universal in modern browsers; without 3D support the cell swaps instantly and stays fully usable.