41 / 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 Three-Position Off / Auto / On Selector
Not every setting is binary: a three-detent selector for Off / Auto / On, where the thumb travels to thirds and the card below explains what the chosen mode actually does. Named peers make three positions no harder than two.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttg-41 w-full min-h-svh grid place-items-center px-5 py-14 bg-ttg41-bg font-ttg41-sans" aria-label="Three position selector">
<div class="w-[min(360px,100%)] flex flex-col gap-5 p-7 rounded-[24px] bg-white border border-ttg41-edge shadow-[0_24px_50px_-36px_oklch(0.4_0.04_265/0.6)]">
<span class="flex flex-col gap-0.5">
<b class="text-[16px] font-bold text-ttg41-ink">Cooling</b>
<span class="text-[12.5px] text-ttg41-mute">Server room · rack B</span>
</span>
<div role="radiogroup" aria-label="Cooling mode" class="relative grid grid-cols-3 p-1 rounded-2xl bg-ttg41-track">
<input type="radio" name="ttg-41" id="ttg-41-off" class="peer/off sr-only">
<input type="radio" name="ttg-41" id="ttg-41-auto" checked class="peer/auto sr-only">
<input type="radio" name="ttg-41" id="ttg-41-on" class="peer/on sr-only">
<span aria-hidden="true" class="absolute top-1 left-1 z-0 w-[calc(33.333%-2.67px)] h-[calc(100%-8px)] rounded-xl bg-white shadow-[0_2px_8px_-2px_oklch(0.4_0.03_265/0.45)] transition-transform duration-[320ms] ease-[cubic-bezier(0.3,1,0.35,1.05)] peer-checked/auto:translate-x-full peer-checked/on:translate-x-[200%] motion-reduce:duration-75"></span>
<label for="ttg-41-off" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/off:text-ttg41-ink peer-focus-visible/off:outline-3 peer-focus-visible/off:outline-ttg41-focus peer-focus-visible/off:outline-offset-2">Off</label>
<label for="ttg-41-auto" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/auto:text-ttg41-auto peer-focus-visible/auto:outline-3 peer-focus-visible/auto:outline-ttg41-focus peer-focus-visible/auto:outline-offset-2">Auto</label>
<label for="ttg-41-on" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/on:text-ttg41-on peer-focus-visible/on:outline-3 peer-focus-visible/on:outline-ttg41-focus peer-focus-visible/on:outline-offset-2">On</label>
<span aria-hidden="true" class="ttg41-exp absolute -bottom-13 left-0 right-0 h-10 peer-checked/off:[&_.ttg41-o]:opacity-100 peer-checked/auto:[&_.ttg41-a]:opacity-100 peer-checked/on:[&_.ttg41-n]:opacity-100">
<i class="ttg41-o absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans stay off. Rack may exceed 30°C under load.</i>
<i class="ttg41-a absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans spin up above 24°C and stop when the rack settles.</i>
<i class="ttg41-n absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans run continuously at 60%. Louder, coldest, more power.</i>
</span>
</div>
<span class="h-10"></span>
</div>
</section><section class="ttg-41 w-full min-h-svh grid place-items-center px-5 py-14 bg-ttg41-bg font-ttg41-sans" aria-label="Three position selector">
<div class="w-[min(360px,100%)] flex flex-col gap-5 p-7 rounded-[24px] bg-white border border-ttg41-edge shadow-[0_24px_50px_-36px_oklch(0.4_0.04_265/0.6)]">
<span class="flex flex-col gap-0.5">
<b class="text-[16px] font-bold text-ttg41-ink">Cooling</b>
<span class="text-[12.5px] text-ttg41-mute">Server room · rack B</span>
</span>
<div role="radiogroup" aria-label="Cooling mode" class="relative grid grid-cols-3 p-1 rounded-2xl bg-ttg41-track">
<input type="radio" name="ttg-41" id="ttg-41-off" class="peer/off sr-only">
<input type="radio" name="ttg-41" id="ttg-41-auto" checked class="peer/auto sr-only">
<input type="radio" name="ttg-41" id="ttg-41-on" class="peer/on sr-only">
<span aria-hidden="true" class="absolute top-1 left-1 z-0 w-[calc(33.333%-2.67px)] h-[calc(100%-8px)] rounded-xl bg-white shadow-[0_2px_8px_-2px_oklch(0.4_0.03_265/0.45)] transition-transform duration-[320ms] ease-[cubic-bezier(0.3,1,0.35,1.05)] peer-checked/auto:translate-x-full peer-checked/on:translate-x-[200%] motion-reduce:duration-75"></span>
<label for="ttg-41-off" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/off:text-ttg41-ink peer-focus-visible/off:outline-3 peer-focus-visible/off:outline-ttg41-focus peer-focus-visible/off:outline-offset-2">Off</label>
<label for="ttg-41-auto" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/auto:text-ttg41-auto peer-focus-visible/auto:outline-3 peer-focus-visible/auto:outline-ttg41-focus peer-focus-visible/auto:outline-offset-2">Auto</label>
<label for="ttg-41-on" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/on:text-ttg41-on peer-focus-visible/on:outline-3 peer-focus-visible/on:outline-ttg41-focus peer-focus-visible/on:outline-offset-2">On</label>
<span aria-hidden="true" class="ttg41-exp absolute -bottom-13 left-0 right-0 h-10 peer-checked/off:[&_.ttg41-o]:opacity-100 peer-checked/auto:[&_.ttg41-a]:opacity-100 peer-checked/on:[&_.ttg41-n]:opacity-100">
<i class="ttg41-o absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans stay off. Rack may exceed 30°C under load.</i>
<i class="ttg41-a absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans spin up above 24°C and stop when the rack settles.</i>
<i class="ttg41-n absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans run continuously at 60%. Louder, coldest, more power.</i>
</span>
</div>
<span class="h-10"></span>
</div>
</section>@import "tailwindcss";
@theme {
--color-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
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-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
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=Plus+Jakarta+Sans:wght@400;500;600;700&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&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Three named peers in one scope is the pattern demo 07 introduced with two, and it is the reason a third detent costs one extra input and one extra
translate-x-[200%]rather than a rethink. - Positions are expressed in thumb-widths (
translate-x-full,translate-x-[200%]) rather than pixels, so the control is resolution-independent and a wider card needs no new numbers. - The explanation block is absolutely positioned below the track with a reserved spacer, so three quite different sentences can swap without the card resizing — the same zero-CLS discipline as the pricing swap in demo 07.
- UI upgrade over demo 20: a genuinely non-binary control, each mode explains its own consequence in a full sentence, and all three labels clear 44px with their active ink at 4.5:1 or better.
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 Three-Position Off / Auto / On Selector
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-three-position-off-auto-on-selector/
Not every setting is binary: a three-detent selector for Off / Auto / On, where the thumb travels to thirds and the card below explains what the chosen mode actually does. Named peers make three positions no harder than two.
## HTML
```html
<section class="ttg-41 w-full min-h-svh grid place-items-center px-5 py-14 bg-ttg41-bg font-ttg41-sans" aria-label="Three position selector">
<div class="w-[min(360px,100%)] flex flex-col gap-5 p-7 rounded-[24px] bg-white border border-ttg41-edge shadow-[0_24px_50px_-36px_oklch(0.4_0.04_265/0.6)]">
<span class="flex flex-col gap-0.5">
<b class="text-[16px] font-bold text-ttg41-ink">Cooling</b>
<span class="text-[12.5px] text-ttg41-mute">Server room · rack B</span>
</span>
<div role="radiogroup" aria-label="Cooling mode" class="relative grid grid-cols-3 p-1 rounded-2xl bg-ttg41-track">
<input type="radio" name="ttg-41" id="ttg-41-off" class="peer/off sr-only">
<input type="radio" name="ttg-41" id="ttg-41-auto" checked class="peer/auto sr-only">
<input type="radio" name="ttg-41" id="ttg-41-on" class="peer/on sr-only">
<span aria-hidden="true" class="absolute top-1 left-1 z-0 w-[calc(33.333%-2.67px)] h-[calc(100%-8px)] rounded-xl bg-white shadow-[0_2px_8px_-2px_oklch(0.4_0.03_265/0.45)] transition-transform duration-[320ms] ease-[cubic-bezier(0.3,1,0.35,1.05)] peer-checked/auto:translate-x-full peer-checked/on:translate-x-[200%] motion-reduce:duration-75"></span>
<label for="ttg-41-off" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/off:text-ttg41-ink peer-focus-visible/off:outline-3 peer-focus-visible/off:outline-ttg41-focus peer-focus-visible/off:outline-offset-2">Off</label>
<label for="ttg-41-auto" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/auto:text-ttg41-auto peer-focus-visible/auto:outline-3 peer-focus-visible/auto:outline-ttg41-focus peer-focus-visible/auto:outline-offset-2">Auto</label>
<label for="ttg-41-on" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/on:text-ttg41-on peer-focus-visible/on:outline-3 peer-focus-visible/on:outline-ttg41-focus peer-focus-visible/on:outline-offset-2">On</label>
<span aria-hidden="true" class="ttg41-exp absolute -bottom-13 left-0 right-0 h-10 peer-checked/off:[&_.ttg41-o]:opacity-100 peer-checked/auto:[&_.ttg41-a]:opacity-100 peer-checked/on:[&_.ttg41-n]:opacity-100">
<i class="ttg41-o absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans stay off. Rack may exceed 30°C under load.</i>
<i class="ttg41-a absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans spin up above 24°C and stop when the rack settles.</i>
<i class="ttg41-n absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans run continuously at 60%. Louder, coldest, more power.</i>
</span>
</div>
<span class="h-10"></span>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
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 Three-Position Off / Auto / On Selector
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-three-position-off-auto-on-selector/
Not every setting is binary: a three-detent selector for Off / Auto / On, where the thumb travels to thirds and the card below explains what the chosen mode actually does. Named peers make three positions no harder than two.
## HTML
```html
<section class="ttg-41 w-full min-h-svh grid place-items-center px-5 py-14 bg-ttg41-bg font-ttg41-sans" aria-label="Three position selector">
<div class="w-[min(360px,100%)] flex flex-col gap-5 p-7 rounded-[24px] bg-white border border-ttg41-edge shadow-[0_24px_50px_-36px_oklch(0.4_0.04_265/0.6)]">
<span class="flex flex-col gap-0.5">
<b class="text-[16px] font-bold text-ttg41-ink">Cooling</b>
<span class="text-[12.5px] text-ttg41-mute">Server room · rack B</span>
</span>
<div role="radiogroup" aria-label="Cooling mode" class="relative grid grid-cols-3 p-1 rounded-2xl bg-ttg41-track">
<input type="radio" name="ttg-41" id="ttg-41-off" class="peer/off sr-only">
<input type="radio" name="ttg-41" id="ttg-41-auto" checked class="peer/auto sr-only">
<input type="radio" name="ttg-41" id="ttg-41-on" class="peer/on sr-only">
<span aria-hidden="true" class="absolute top-1 left-1 z-0 w-[calc(33.333%-2.67px)] h-[calc(100%-8px)] rounded-xl bg-white shadow-[0_2px_8px_-2px_oklch(0.4_0.03_265/0.45)] transition-transform duration-[320ms] ease-[cubic-bezier(0.3,1,0.35,1.05)] peer-checked/auto:translate-x-full peer-checked/on:translate-x-[200%] motion-reduce:duration-75"></span>
<label for="ttg-41-off" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/off:text-ttg41-ink peer-focus-visible/off:outline-3 peer-focus-visible/off:outline-ttg41-focus peer-focus-visible/off:outline-offset-2">Off</label>
<label for="ttg-41-auto" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/auto:text-ttg41-auto peer-focus-visible/auto:outline-3 peer-focus-visible/auto:outline-ttg41-focus peer-focus-visible/auto:outline-offset-2">Auto</label>
<label for="ttg-41-on" class="relative z-10 flex items-center justify-center min-h-11 rounded-xl cursor-pointer text-[13.5px] font-bold text-ttg41-mute transition-colors duration-200 peer-checked/on:text-ttg41-on peer-focus-visible/on:outline-3 peer-focus-visible/on:outline-ttg41-focus peer-focus-visible/on:outline-offset-2">On</label>
<span aria-hidden="true" class="ttg41-exp absolute -bottom-13 left-0 right-0 h-10 peer-checked/off:[&_.ttg41-o]:opacity-100 peer-checked/auto:[&_.ttg41-a]:opacity-100 peer-checked/on:[&_.ttg41-n]:opacity-100">
<i class="ttg41-o absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans stay off. Rack may exceed 30°C under load.</i>
<i class="ttg41-a absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans spin up above 24°C and stop when the rack settles.</i>
<i class="ttg41-n absolute inset-0 not-italic flex items-center text-[12.5px] leading-snug text-ttg41-mute opacity-0 transition-opacity duration-200">Fans run continuously at 60%. Louder, coldest, more power.</i>
</span>
</div>
<span class="h-10"></span>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg41-bg: #eef0f5;
--color-ttg41-bg: oklch(0.95 0.006 265);
--color-ttg41-edge: #e6e8f0;
--color-ttg41-edge: oklch(0.93 0.006 265);
--color-ttg41-track: #eceef4;
--color-ttg41-track: oklch(0.94 0.006 265);
--color-ttg41-ink: #1f2532;
--color-ttg41-ink: oklch(0.26 0.015 265);
--color-ttg41-mute: #767e90;
--color-ttg41-mute: oklch(0.58 0.018 265);
--color-ttg41-auto: #2a6fd6;
--color-ttg41-auto: oklch(0.54 0.17 255);
--color-ttg41-on: #2f9e63;
--color-ttg41-on: oklch(0.6 0.15 155);
--color-ttg41-focus: #3f63e0;
--color-ttg41-focus: oklch(0.55 0.19 265);
--font-ttg41-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-41 {
width: 100%;
min-height: 100svh;
}
```How this works
Thirds, not pixels. The thumb is w-1/3 of the track and moves by translate-x-full and translate-x-[200%] — its own width, twice. That makes the control resolution-independent: change the track width and every detent stays correct.
Three radios, three named peers. Unnamed peers are ambiguous the moment there are two inputs in a scope; naming them is what lets one track react differently to each.
Auto is the interesting state. The explanation area holds three absolutely-stacked lines and crossfades between them, so the card never changes height — and Auto's line says what Auto will do, which is the whole reason a three-position switch exists instead of two.
Make it yours
- Four positions:
grid-cols-4,w-1/4thumb, and atranslate-x-[300%]rule. - Per-mode accent: give each radio's checked rule a different thumb colour.
- Vertical: swap the axis to
translate-yfor a fader-style selector. - Add mode icons above the labels if the words are short enough to keep at 44px.
Gotchas — read before shipping
- Use radios, never three checkboxes — the states are mutually exclusive and a radio group gives arrow-key navigation for free.
- Name every peer when more than one input shares a scope; unnamed peers silently match the wrong input.
- Keep the explanation lines absolutely stacked. Swapping text in normal flow reflows the card and makes the thumb appear to jump.
- Three 44px-tall labels is the floor; a three-position control squeezed into 32px is unhittable on touch.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Named peer variants are v4 core; the demo needs no :has(), no @property and no 3D.