17 / 25
All 25 in 25 Tailwind CSS Button Groups
- 01Tailwind Split Button with Anchor-Positioned Popover Dropdown (No JS)
- 02Tailwind Segmented Control with Motion-Path Comet Indicator
- 03Tailwind Toggle Button Group with View Transitions Morphing Highlight
- 04Tailwind Pricing Toggle — Monthly / Yearly Billing Switch (No JS)
- 05Tailwind Segmented Control with Sliding Thumb (iOS Style)
- 06Tailwind Split Button Dropdown with Native Details Menu (Save Options)
- 07Tailwind Icon Button Group Toolbar with Tooltips (Rich Text Editor)
- 08Tailwind Pill Filter Button Group with Sliding Indicator (Anchor Positioning)
- 09Tailwind Toggle Button Group for Text Alignment (Radio Inputs, Live Preview)
- 10Tailwind Web3 Connect Wallet Button Grid (Wallet Sign-In Modal)
- 11Tailwind AI Model Selector Button Group (LLM Model Picker with Badges)
- 12Tailwind Liquid Metal Segmented Control (Animated Chrome Thumb)
- 13Tailwind Tab Button Group with Subgrid Underline Indicator
- 14Tailwind Multi-Select Chip Buttons (Tag Filter Checkboxes with Count)
- 15Tailwind Animated Split Button Deploy Menu (Staggered Dropdown Reveal)
- 16Tailwind Radio Button Group with Conic Fill Animation (Export Format Picker)
- 17Tailwind Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
- 18Tailwind Hold-to-Confirm Button Group with Gradient Progress (Press and Hold)
- 19Tailwind Holographic Icon Segmented Control (Grid / List View Switcher)
- 20Tailwind 3D Vertical Button Group with Hover Depth (Perspective Stack)
- 21Tailwind Floating Action Button with Radial Speed Dial Menu (No JS)
- 22Tailwind Glitch Effect Button Group (Cyberpunk RGB-Split Audio Filter)
- 23Tailwind Animated Gradient Border Pill Buttons (Rotating Conic Ring)
- 24Tailwind Button Group with Ripple Click Effect (Radar Shockwave Feedback)
- 25Tailwind Button Group with Cursor-Following Spotlight Glow (Date Range Presets)
Tailwind Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
A hardware-style transport panel: three keys drawn as dot-matrix LED displays that light up green, amber or red when selected. The dot grid is a tiled radial-gradient on before: (before:bg-size-[7px_7px]), scanlines sit on after:, and each key swaps one variable — has-checked:[--led:#3dff6e] — to re-colour dots, text and glow at once. Labels are set in Doto, a true dot-matrix face, and a status line reports the current mode.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tbg-17 w-full min-h-svh grid place-items-center bg-tbg17-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg17-glow),transparent_70%)] px-5 py-14 font-tbg17-mono text-tbg17-ink" aria-label="Retro LED pixel button group demo">
<div class="group/panel w-full max-w-[33rem] rounded-[1.25rem] border border-tbg17-line bg-linear-to-b from-tbg17-panel to-tbg17-panel2 p-6 shadow-tbg17-panel">
<div class="mb-4 flex items-center justify-between">
<p id="tbg17-label" class="text-[11px] font-semibold uppercase tracking-[0.24em] text-tbg17-mut">Transport</p>
<p class="text-[11px] tracking-[0.12em] text-tbg17-mut">SYS ▸ <span class="hidden text-tbg17-go group-has-[#tbg17-run:checked]/panel:inline">RUNNING</span><span class="hidden text-tbg17-wait group-has-[#tbg17-wait:checked]/panel:inline">ON HOLD</span><span class="hidden text-tbg17-stop group-has-[#tbg17-stop:checked]/panel:inline">HALTED</span></p>
</div>
<div role="radiogroup" aria-labelledby="tbg17-label" class="grid grid-cols-3 gap-3">
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-go-d has-checked:[--led:var(--color-tbg17-go)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-run" class="sr-only" checked><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-run:checked]/panel:text-white group-has-[#tbg17-run:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-go),0_0_16px_var(--color-tbg17-go)]">RUN</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-wait-d has-checked:[--led:var(--color-tbg17-wait)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-wait" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-wait:checked]/panel:text-white group-has-[#tbg17-wait:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-wait),0_0_16px_var(--color-tbg17-wait)]">WAIT</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-stop-d has-checked:[--led:var(--color-tbg17-stop)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-stop" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-stop:checked]/panel:text-white group-has-[#tbg17-stop:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-stop),0_0_16px_var(--color-tbg17-stop)]">STOP</span></label>
</div>
</div>
</section><section class="tbg-17 w-full min-h-svh grid place-items-center bg-tbg17-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg17-glow),transparent_70%)] px-5 py-14 font-tbg17-mono text-tbg17-ink" aria-label="Retro LED pixel button group demo">
<div class="group/panel w-full max-w-[33rem] rounded-[1.25rem] border border-tbg17-line bg-linear-to-b from-tbg17-panel to-tbg17-panel2 p-6 shadow-tbg17-panel">
<div class="mb-4 flex items-center justify-between">
<p id="tbg17-label" class="text-[11px] font-semibold uppercase tracking-[0.24em] text-tbg17-mut">Transport</p>
<p class="text-[11px] tracking-[0.12em] text-tbg17-mut">SYS ▸ <span class="hidden text-tbg17-go group-has-[#tbg17-run:checked]/panel:inline">RUNNING</span><span class="hidden text-tbg17-wait group-has-[#tbg17-wait:checked]/panel:inline">ON HOLD</span><span class="hidden text-tbg17-stop group-has-[#tbg17-stop:checked]/panel:inline">HALTED</span></p>
</div>
<div role="radiogroup" aria-labelledby="tbg17-label" class="grid grid-cols-3 gap-3">
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-go-d has-checked:[--led:var(--color-tbg17-go)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-run" class="sr-only" checked><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-run:checked]/panel:text-white group-has-[#tbg17-run:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-go),0_0_16px_var(--color-tbg17-go)]">RUN</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-wait-d has-checked:[--led:var(--color-tbg17-wait)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-wait" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-wait:checked]/panel:text-white group-has-[#tbg17-wait:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-wait),0_0_16px_var(--color-tbg17-wait)]">WAIT</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-stop-d has-checked:[--led:var(--color-tbg17-stop)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-stop" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-stop:checked]/panel:text-white group-has-[#tbg17-stop:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-stop),0_0_16px_var(--color-tbg17-stop)]">STOP</span></label>
</div>
</div>
</section>@import "tailwindcss";
@theme {
--color-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
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-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
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=Doto:wght@600..900&family=IBM+Plex+Mono:wght@400..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=Doto:wght@600..900&family=IBM+Plex+Mono:wght@400..600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Three
input:checked + .btn-17__key--x{--led:…}rules became onehas-checked:[--led:…]per key; the dot grid and scanlines arebefore:/after:utilities. background-size:7px 7pxbecame v4'sbg-size-[7px_7px].- UI upgrade: labels are set in Doto, a real dot-matrix typeface, and a SYS status readout names the active mode in its LED colour.
- Accessibility fix: removed
aria-labels that contradicted the visible text (“WAIT” was announced as “Pause”); the panel label rose to 6:1.
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 Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-retro-led-pixel-button-group/
A hardware-style transport panel: three keys drawn as dot-matrix LED displays that light up green, amber or red when selected. The dot grid is a tiled radial-gradient on before: (before:bg-size-[7px_7px]), scanlines sit on after:, and each key swaps one variable — has-checked:[--led:#3dff6e] — to re-colour dots, text and glow at once. Labels are set in Doto, a true dot-matrix face, and a status line reports the current mode.
## HTML
```html
<section class="tbg-17 w-full min-h-svh grid place-items-center bg-tbg17-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg17-glow),transparent_70%)] px-5 py-14 font-tbg17-mono text-tbg17-ink" aria-label="Retro LED pixel button group demo">
<div class="group/panel w-full max-w-[33rem] rounded-[1.25rem] border border-tbg17-line bg-linear-to-b from-tbg17-panel to-tbg17-panel2 p-6 shadow-tbg17-panel">
<div class="mb-4 flex items-center justify-between">
<p id="tbg17-label" class="text-[11px] font-semibold uppercase tracking-[0.24em] text-tbg17-mut">Transport</p>
<p class="text-[11px] tracking-[0.12em] text-tbg17-mut">SYS ▸ <span class="hidden text-tbg17-go group-has-[#tbg17-run:checked]/panel:inline">RUNNING</span><span class="hidden text-tbg17-wait group-has-[#tbg17-wait:checked]/panel:inline">ON HOLD</span><span class="hidden text-tbg17-stop group-has-[#tbg17-stop:checked]/panel:inline">HALTED</span></p>
</div>
<div role="radiogroup" aria-labelledby="tbg17-label" class="grid grid-cols-3 gap-3">
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-go-d has-checked:[--led:var(--color-tbg17-go)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-run" class="sr-only" checked><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-run:checked]/panel:text-white group-has-[#tbg17-run:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-go),0_0_16px_var(--color-tbg17-go)]">RUN</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-wait-d has-checked:[--led:var(--color-tbg17-wait)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-wait" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-wait:checked]/panel:text-white group-has-[#tbg17-wait:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-wait),0_0_16px_var(--color-tbg17-wait)]">WAIT</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-stop-d has-checked:[--led:var(--color-tbg17-stop)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-stop" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-stop:checked]/panel:text-white group-has-[#tbg17-stop:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-stop),0_0_16px_var(--color-tbg17-stop)]">STOP</span></label>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
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 Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-retro-led-pixel-button-group/
A hardware-style transport panel: three keys drawn as dot-matrix LED displays that light up green, amber or red when selected. The dot grid is a tiled radial-gradient on before: (before:bg-size-[7px_7px]), scanlines sit on after:, and each key swaps one variable — has-checked:[--led:#3dff6e] — to re-colour dots, text and glow at once. Labels are set in Doto, a true dot-matrix face, and a status line reports the current mode.
## HTML
```html
<section class="tbg-17 w-full min-h-svh grid place-items-center bg-tbg17-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg17-glow),transparent_70%)] px-5 py-14 font-tbg17-mono text-tbg17-ink" aria-label="Retro LED pixel button group demo">
<div class="group/panel w-full max-w-[33rem] rounded-[1.25rem] border border-tbg17-line bg-linear-to-b from-tbg17-panel to-tbg17-panel2 p-6 shadow-tbg17-panel">
<div class="mb-4 flex items-center justify-between">
<p id="tbg17-label" class="text-[11px] font-semibold uppercase tracking-[0.24em] text-tbg17-mut">Transport</p>
<p class="text-[11px] tracking-[0.12em] text-tbg17-mut">SYS ▸ <span class="hidden text-tbg17-go group-has-[#tbg17-run:checked]/panel:inline">RUNNING</span><span class="hidden text-tbg17-wait group-has-[#tbg17-wait:checked]/panel:inline">ON HOLD</span><span class="hidden text-tbg17-stop group-has-[#tbg17-stop:checked]/panel:inline">HALTED</span></p>
</div>
<div role="radiogroup" aria-labelledby="tbg17-label" class="grid grid-cols-3 gap-3">
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-go-d has-checked:[--led:var(--color-tbg17-go)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-run" class="sr-only" checked><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-run:checked]/panel:text-white group-has-[#tbg17-run:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-go),0_0_16px_var(--color-tbg17-go)]">RUN</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-wait-d has-checked:[--led:var(--color-tbg17-wait)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-wait" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-wait:checked]/panel:text-white group-has-[#tbg17-wait:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-wait),0_0_16px_var(--color-tbg17-wait)]">WAIT</span></label>
<label class="relative isolate grid min-h-22 cursor-pointer place-items-center overflow-hidden rounded-xl border-[1.5px] border-tbg17-key-line bg-tbg17-key shadow-[inset_0_2px_6px_rgb(0_0_0/0.7)] [--led:rgb(120_120_120/0.22)] before:absolute before:inset-0 before:-z-1 before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)] before:bg-size-[7px_7px] before:transition-[filter] before:duration-250 before:content-[''] after:pointer-events-none after:absolute after:inset-0 after:bg-[repeating-linear-gradient(0deg,rgb(255_255_255/0.04)_0_1px,transparent_1px_3px)] after:content-[''] hover:border-tbg17-hover has-checked:border-tbg17-stop-d has-checked:[--led:var(--color-tbg17-stop)] has-checked:shadow-[inset_0_2px_6px_rgb(0_0_0/0.7),0_0_22px_-6px_var(--led)] has-checked:before:drop-shadow-[0_0_5px_var(--led)] has-focus-visible:outline-3 has-focus-visible:outline-offset-2 has-focus-visible:outline-white"><input type="radio" name="tbg17" id="tbg17-stop" class="sr-only"><span class="font-tbg17-dot text-[26px] font-black tracking-[0.08em] text-(--led) transition-[color,text-shadow] duration-250 group-has-[#tbg17-stop:checked]/panel:text-white group-has-[#tbg17-stop:checked]/panel:[text-shadow:0_0_8px_var(--color-tbg17-stop),0_0_16px_var(--color-tbg17-stop)]">STOP</span></label>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbg17-bg: #050505;
--color-tbg17-glow: #181818;
--color-tbg17-panel: #1c1c1c;
--color-tbg17-panel2: #0e0e0e;
--color-tbg17-line: #2c2c2c;
--color-tbg17-key: #0a0a0a;
--color-tbg17-key-line: #262626;
--color-tbg17-hover: #3a3a3a;
--color-tbg17-ink: #e8e8e8;
--color-tbg17-mut: #8f8f8f;
--color-tbg17-go: #3dff6e;
--color-tbg17-go-d: #1c7a3a;
--color-tbg17-wait: #ffb000;
--color-tbg17-wait-d: #8a6000;
--color-tbg17-stop: #ff4242;
--color-tbg17-stop-d: #8a2020;
--shadow-tbg17-panel: inset 0 2px 10px rgb(0 0 0 / 0.8), 0 30px 70px -40px #000;
--font-tbg17-mono: 'IBM Plex Mono', ui-monospace, monospace;
--font-tbg17-dot: 'Doto', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-17 {
width: 100%;
min-height: 100svh;
}
```How this works
Everything reads --led. The key sets a dim default; the checked state overrides it, and every layer that uses var(--led) lights up together:
<label class="[--led:rgb(120_120_120/0.22)] has-checked:[--led:#3dff6e]
before:bg-[radial-gradient(circle,var(--led)_30%,transparent_34%)]
before:bg-size-[7px_7px]">The status readout is three spans, each shown by group-has-[#tbg17-run:checked]/panel:inline — the panel narrates its own state without script.
Make it yours
- Finer matrix:
before:bg-size-[5px_5px]. - Different LEDs: change each key's
has-checked:[--led:…]. - Remove scanlines: delete the
after:utilities.
Gotchas — read before shipping
- Transitions on
backgroundcan't interpolate the gradient itself — the dots switch colour, the glow fades. - Keep visible labels as the accessible name; don't add an
aria-labelthat differs from the text. - Doto at small sizes is hard to read — keep it to display text.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
All long-standing CSS; Tailwind v4 sets the floor.