24 / 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 Minimal Outline Toggle Drawn in currentColor
A stroke-only switch drawn entirely in currentColor: hollow track, hollow thumb that fills solid when on. Because it inherits colour from surrounding text, the identical markup works on a white card, a dark card and a brand banner with zero per-theme CSS.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="ttg-24 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg24-bg font-ttg24-sans" aria-label="Outline currentColor toggles">
<div class="flex flex-wrap justify-center gap-4">
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-white text-ttg24-lightink border border-ttg24-edge">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on light" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-dark text-ttg24-darkink">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on dark" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-brand text-white">
<span>Autosave</span>
<input type="checkbox" role="switch" aria-label="Autosave on brand" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
</div>
<p class="text-[12px] tracking-[0.12em] uppercase text-ttg24-mute">One switch · three themes · zero theme CSS</p>
</section><section class="ttg-24 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg24-bg font-ttg24-sans" aria-label="Outline currentColor toggles">
<div class="flex flex-wrap justify-center gap-4">
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-white text-ttg24-lightink border border-ttg24-edge">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on light" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-dark text-ttg24-darkink">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on dark" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-brand text-white">
<span>Autosave</span>
<input type="checkbox" role="switch" aria-label="Autosave on brand" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
</div>
<p class="text-[12px] tracking-[0.12em] uppercase text-ttg24-mute">One switch · three themes · zero theme CSS</p>
</section>@import "tailwindcss";
@theme {
--color-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 input[type="checkbox"] {
-webkit-appearance: none;
}@import "tailwindcss";
@theme {
--color-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 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-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 input[type="checkbox"] {
-webkit-appearance: none;
}@import "tailwindcss";
@theme {
--color-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 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&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.
border-current,bg-currentandoutline-currentmean the switch's 20-utility class list contains no colour at all. Pasted into three differently-themed chips, the identical string produces three correct controls — which is the cleanest possible demonstration of whycurrentColorbeats a theme prop.- This is the one demo where duplicating the class list three times is the POINT: byte-identical markup is the evidence. In production it would be a component; here it would weaken the argument.
- The brand chip's white text makes the whole switch white, including the focus ring — which is correct, and would have needed a third theme override in the CSS version.
- UI change from the CSS original: the brand chip's background darkened to
oklch(0.52 0.17 270)so a white 2px stroke clears 4.9:1 (it was 3.6:1), and the caption ink lifted tooklch(0.63)— in a demo about contrast inheritance, the caption failing contrast was the wrong lesson.
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 Minimal Outline Toggle Drawn in currentColor
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-minimal-outline-toggle-currentcolor/
A stroke-only switch drawn entirely in currentColor: hollow track, hollow thumb that fills solid when on. Because it inherits colour from surrounding text, the identical markup works on a white card, a dark card and a brand banner with zero per-theme CSS.
## HTML
```html
<section class="ttg-24 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg24-bg font-ttg24-sans" aria-label="Outline currentColor toggles">
<div class="flex flex-wrap justify-center gap-4">
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-white text-ttg24-lightink border border-ttg24-edge">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on light" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-dark text-ttg24-darkink">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on dark" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-brand text-white">
<span>Autosave</span>
<input type="checkbox" role="switch" aria-label="Autosave on brand" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
</div>
<p class="text-[12px] tracking-[0.12em] uppercase text-ttg24-mute">One switch · three themes · zero theme CSS</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 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 Minimal Outline Toggle Drawn in currentColor
Source: https://codefronts.com/components/tailwind-css-toggles-switches/tailwind-minimal-outline-toggle-currentcolor/
A stroke-only switch drawn entirely in currentColor: hollow track, hollow thumb that fills solid when on. Because it inherits colour from surrounding text, the identical markup works on a white card, a dark card and a brand banner with zero per-theme CSS.
## HTML
```html
<section class="ttg-24 w-full min-h-svh flex flex-col items-center justify-center gap-7 px-5 py-14 bg-ttg24-bg font-ttg24-sans" aria-label="Outline currentColor toggles">
<div class="flex flex-wrap justify-center gap-4">
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-white text-ttg24-lightink border border-ttg24-edge">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on light" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-dark text-ttg24-darkink">
<span>Autosave</span>
<input type="checkbox" role="switch" checked aria-label="Autosave on dark" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
<label class="flex items-center gap-5 px-6 py-4 rounded-2xl cursor-pointer text-[15px] font-semibold bg-ttg24-brand text-white">
<span>Autosave</span>
<input type="checkbox" role="switch" aria-label="Autosave on brand" class="appearance-none shrink-0 w-14 h-8 rounded-2xl border-2 border-current bg-transparent relative cursor-pointer before:content-[''] before:absolute before:top-[3px] before:left-[3px] before:w-5 before:h-5 before:rounded-full before:border-2 before:border-current before:bg-transparent before:transition-[transform,background-color] before:duration-300 before:ease-[cubic-bezier(0.3,1,0.35,1.05)] checked:before:translate-x-6 checked:before:bg-current focus-visible:outline-3 focus-visible:outline-current focus-visible:outline-offset-3 motion-reduce:before:transition-none">
</label>
</div>
<p class="text-[12px] tracking-[0.12em] uppercase text-ttg24-mute">One switch · three themes · zero theme CSS</p>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-ttg24-bg: #f3f3f1;
--color-ttg24-bg: oklch(0.96 0.004 110);
--color-ttg24-edge: #e2e2de;
--color-ttg24-edge: oklch(0.92 0.005 110);
--color-ttg24-lightink: #23262c;
--color-ttg24-lightink: oklch(0.27 0.012 265);
--color-ttg24-dark: #1b1d22;
--color-ttg24-dark: oklch(0.23 0.008 265);
--color-ttg24-darkink: #eceef2;
--color-ttg24-darkink: oklch(0.94 0.005 265);
--color-ttg24-brand: #4b5fd6;
--color-ttg24-brand: oklch(0.52 0.17 270);
--color-ttg24-mute: #8e8e86;
--color-ttg24-mute: oklch(0.63 0.01 110);
--font-ttg24-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.ttg-24 {
width: 100%;
min-height: 100svh;
}
.ttg-24 input[type="checkbox"] {
-webkit-appearance: none;
}
```How this works
Every stroke is currentColor. The track is a 2px border on a transparent fill; the thumb is a 2px-bordered circle that gains bg-current when checked. Drop the component into any container and it matches that container's text colour, including hover recolours and dark mode, for free — because it is not theming, it is inheritance.
State is double-encoded on purpose. Position AND fill density (hollow off, solid on). A monochrome control that signals only by position fails the moment the user cannot see the track edge; this one survives greyscale, low vision and a bad screenshot.
Three chips, one class list. Light, dark and brand. The switches are byte-identical — the only difference is the text-* on their parents.
Make it yours
- Thicken to
border-[3px]for display contexts. - Let the on-state tint: add
checked:text-ttg24-accentand everything shifts together. - Square variant:
rounded-mdon both track and thumb. - Pop-in fill: animate an
after:disc's scale instead of the background.
Gotchas — read before shipping
currentColorresolves at the element. In muted 50%-grey body text the control is also 50% grey — resettext-*on the wrapper wherever contrast would fail.- Never drop the hollow/solid distinction to make it cleaner. Position-only state in a monochrome control fails colour-independence outright.
- Even stroke widths render cleanest: 2px borders on a transparent fill hit sub-pixel rounding at odd zoom levels, and 1px is where it shows.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
currentColor is CSS2-era and the rest is borders and transforms — conceptually the most portable demo here; the floor quoted is Tailwind v4's generated sheet.