12 / 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 Liquid Metal Segmented Control (Animated Chrome Thumb)
A dark segmented control with a polished-chrome thumb: an @property-registered angle spins the metallic gradient, a glint sweeps across it every few seconds, and the thumb squashes and stretches like mercury every time it lands. The CSS original only wobbled on the first move — this port gives each position its own squash keyframe, so the stretch replays on every change.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tbg-12 w-full min-h-svh grid place-items-center bg-tbg12-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg12-glow),transparent_70%)] px-5 py-14 font-tbg12-sans text-tbg12-ink" aria-label="Liquid metal segmented control demo">
<div class="grid w-full max-w-[28rem] gap-3.5">
<p id="tbg12-label" class="ml-1 text-xs font-bold uppercase tracking-[0.16em] text-tbg12-mut">Rendering mode</p>
<div role="radiogroup" aria-labelledby="tbg12-label" class="group/seg relative grid grid-cols-3 rounded-2xl border border-tbg12-line bg-tbg12-well p-1.5 shadow-[inset_0_2px_8px_rgb(0_0_0/0.6)]">
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-1" class="sr-only" checked>Auto</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-2" class="sr-only">Metal</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-3" class="sr-only">Glass</label>
<span aria-hidden="true" class="pointer-events-none absolute top-1.5 bottom-1.5 left-1.5 z-1 w-[calc((100%-0.75rem)/3)] animate-tbg12-sq1 rounded-xl shadow-tbg12-thumb transition-[translate] duration-500 ease-[cubic-bezier(.5,-0.2,.3,1.3)] group-has-[#tbg12-2:checked]/seg:translate-x-full group-has-[#tbg12-2:checked]/seg:animate-tbg12-sq2 group-has-[#tbg12-3:checked]/seg:translate-x-[200%] group-has-[#tbg12-3:checked]/seg:animate-tbg12-sq3 motion-reduce:animate-none! motion-reduce:duration-300">
<span class="absolute inset-0 overflow-hidden rounded-xl bg-[linear-gradient(var(--tbg12-sheen),#f4f6fb_0%,#c3cad9_30%,#7a8398_55%,#dfe4ee_80%,#f4f6fb_100%)] animate-tbg12-sheen after:absolute after:inset-0 after:-translate-x-[120%] after:bg-[linear-gradient(105deg,transparent_35%,rgb(255_255_255/0.85)_50%,transparent_65%)] after:animate-tbg12-glint after:content-[''] motion-reduce:animate-none motion-reduce:after:animate-none"></span>
</span>
</div>
</div>
</section><section class="tbg-12 w-full min-h-svh grid place-items-center bg-tbg12-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg12-glow),transparent_70%)] px-5 py-14 font-tbg12-sans text-tbg12-ink" aria-label="Liquid metal segmented control demo">
<div class="grid w-full max-w-[28rem] gap-3.5">
<p id="tbg12-label" class="ml-1 text-xs font-bold uppercase tracking-[0.16em] text-tbg12-mut">Rendering mode</p>
<div role="radiogroup" aria-labelledby="tbg12-label" class="group/seg relative grid grid-cols-3 rounded-2xl border border-tbg12-line bg-tbg12-well p-1.5 shadow-[inset_0_2px_8px_rgb(0_0_0/0.6)]">
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-1" class="sr-only" checked>Auto</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-2" class="sr-only">Metal</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-3" class="sr-only">Glass</label>
<span aria-hidden="true" class="pointer-events-none absolute top-1.5 bottom-1.5 left-1.5 z-1 w-[calc((100%-0.75rem)/3)] animate-tbg12-sq1 rounded-xl shadow-tbg12-thumb transition-[translate] duration-500 ease-[cubic-bezier(.5,-0.2,.3,1.3)] group-has-[#tbg12-2:checked]/seg:translate-x-full group-has-[#tbg12-2:checked]/seg:animate-tbg12-sq2 group-has-[#tbg12-3:checked]/seg:translate-x-[200%] group-has-[#tbg12-3:checked]/seg:animate-tbg12-sq3 motion-reduce:animate-none! motion-reduce:duration-300">
<span class="absolute inset-0 overflow-hidden rounded-xl bg-[linear-gradient(var(--tbg12-sheen),#f4f6fb_0%,#c3cad9_30%,#7a8398_55%,#dfe4ee_80%,#f4f6fb_100%)] animate-tbg12-sheen after:absolute after:inset-0 after:-translate-x-[120%] after:bg-[linear-gradient(105deg,transparent_35%,rgb(255_255_255/0.85)_50%,transparent_65%)] after:animate-tbg12-glint after:content-[''] motion-reduce:animate-none motion-reduce:after:animate-none"></span>
</span>
</div>
</div>
</section>@import "tailwindcss";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-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";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-12 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-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=Familjen+Grotesk:wght@400..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=Familjen+Grotesk:wght@400..700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The thumb split into two layers: the outer span translates and squashes, the inner span carries the spinning gradient and glint — each animation owns one property.
- Bug fix: in the CSS original positions 2 and 3 shared one animation list, so the mercury squash never replayed between them. Three uniquely-named keyframes make it fire on every change.
- UI upgrade: the label is now tied to the control with
aria-labelledbyand the group is a realradiogroup; segments grew to 48px. - UI upgrade: muted labels rose from
#8b93a7tooklch(0.74)(7.5:1 on the well).
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 Liquid Metal Segmented Control (Animated Chrome Thumb)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-liquid-metal-segmented-control/
A dark segmented control with a polished-chrome thumb: an @property-registered angle spins the metallic gradient, a glint sweeps across it every few seconds, and the thumb squashes and stretches like mercury every time it lands. The CSS original only wobbled on the first move — this port gives each position its own squash keyframe, so the stretch replays on every change.
## HTML
```html
<section class="tbg-12 w-full min-h-svh grid place-items-center bg-tbg12-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg12-glow),transparent_70%)] px-5 py-14 font-tbg12-sans text-tbg12-ink" aria-label="Liquid metal segmented control demo">
<div class="grid w-full max-w-[28rem] gap-3.5">
<p id="tbg12-label" class="ml-1 text-xs font-bold uppercase tracking-[0.16em] text-tbg12-mut">Rendering mode</p>
<div role="radiogroup" aria-labelledby="tbg12-label" class="group/seg relative grid grid-cols-3 rounded-2xl border border-tbg12-line bg-tbg12-well p-1.5 shadow-[inset_0_2px_8px_rgb(0_0_0/0.6)]">
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-1" class="sr-only" checked>Auto</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-2" class="sr-only">Metal</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-3" class="sr-only">Glass</label>
<span aria-hidden="true" class="pointer-events-none absolute top-1.5 bottom-1.5 left-1.5 z-1 w-[calc((100%-0.75rem)/3)] animate-tbg12-sq1 rounded-xl shadow-tbg12-thumb transition-[translate] duration-500 ease-[cubic-bezier(.5,-0.2,.3,1.3)] group-has-[#tbg12-2:checked]/seg:translate-x-full group-has-[#tbg12-2:checked]/seg:animate-tbg12-sq2 group-has-[#tbg12-3:checked]/seg:translate-x-[200%] group-has-[#tbg12-3:checked]/seg:animate-tbg12-sq3 motion-reduce:animate-none! motion-reduce:duration-300">
<span class="absolute inset-0 overflow-hidden rounded-xl bg-[linear-gradient(var(--tbg12-sheen),#f4f6fb_0%,#c3cad9_30%,#7a8398_55%,#dfe4ee_80%,#f4f6fb_100%)] animate-tbg12-sheen after:absolute after:inset-0 after:-translate-x-[120%] after:bg-[linear-gradient(105deg,transparent_35%,rgb(255_255_255/0.85)_50%,transparent_65%)] after:animate-tbg12-glint after:content-[''] motion-reduce:animate-none motion-reduce:after:animate-none"></span>
</span>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-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 Liquid Metal Segmented Control (Animated Chrome Thumb)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-liquid-metal-segmented-control/
A dark segmented control with a polished-chrome thumb: an @property-registered angle spins the metallic gradient, a glint sweeps across it every few seconds, and the thumb squashes and stretches like mercury every time it lands. The CSS original only wobbled on the first move — this port gives each position its own squash keyframe, so the stretch replays on every change.
## HTML
```html
<section class="tbg-12 w-full min-h-svh grid place-items-center bg-tbg12-bg bg-[radial-gradient(120%_120%_at_50%_0%,var(--color-tbg12-glow),transparent_70%)] px-5 py-14 font-tbg12-sans text-tbg12-ink" aria-label="Liquid metal segmented control demo">
<div class="grid w-full max-w-[28rem] gap-3.5">
<p id="tbg12-label" class="ml-1 text-xs font-bold uppercase tracking-[0.16em] text-tbg12-mut">Rendering mode</p>
<div role="radiogroup" aria-labelledby="tbg12-label" class="group/seg relative grid grid-cols-3 rounded-2xl border border-tbg12-line bg-tbg12-well p-1.5 shadow-[inset_0_2px_8px_rgb(0_0_0/0.6)]">
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-1" class="sr-only" checked>Auto</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-2" class="sr-only">Metal</label>
<label class="relative z-2 grid min-h-12 cursor-pointer place-items-center rounded-xl text-[15px] font-semibold text-tbg12-mut transition-colors duration-300 hover:text-tbg12-ink has-checked:text-tbg12-on has-checked:[text-shadow:0_1px_0_rgb(255_255_255/0.45)] has-focus-visible:outline-3 has-focus-visible:outline-offset-3 has-focus-visible:outline-tbg12-focus"><input type="radio" name="tbg12" id="tbg12-3" class="sr-only">Glass</label>
<span aria-hidden="true" class="pointer-events-none absolute top-1.5 bottom-1.5 left-1.5 z-1 w-[calc((100%-0.75rem)/3)] animate-tbg12-sq1 rounded-xl shadow-tbg12-thumb transition-[translate] duration-500 ease-[cubic-bezier(.5,-0.2,.3,1.3)] group-has-[#tbg12-2:checked]/seg:translate-x-full group-has-[#tbg12-2:checked]/seg:animate-tbg12-sq2 group-has-[#tbg12-3:checked]/seg:translate-x-[200%] group-has-[#tbg12-3:checked]/seg:animate-tbg12-sq3 motion-reduce:animate-none! motion-reduce:duration-300">
<span class="absolute inset-0 overflow-hidden rounded-xl bg-[linear-gradient(var(--tbg12-sheen),#f4f6fb_0%,#c3cad9_30%,#7a8398_55%,#dfe4ee_80%,#f4f6fb_100%)] animate-tbg12-sheen after:absolute after:inset-0 after:-translate-x-[120%] after:bg-[linear-gradient(105deg,transparent_35%,rgb(255_255_255/0.85)_50%,transparent_65%)] after:animate-tbg12-glint after:content-[''] motion-reduce:animate-none motion-reduce:after:animate-none"></span>
</span>
</div>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
/* @property is an at-rule with no utility: it makes the gradient angle animatable. */
@property --tbg12-sheen {
syntax: "<angle>";
inherits: true;
initial-value: 0deg;
}
@theme {
--color-tbg12-bg: #0c0e14;
--color-tbg12-glow: #232834;
--color-tbg12-well: #171a22;
--color-tbg12-line: #2a2f3c;
--color-tbg12-ink: #eef1f7;
--color-tbg12-mut: #a2aabd;
--color-tbg12-mut: oklch(0.74 0.025 265);
--color-tbg12-on: #11141c;
--color-tbg12-focus: #9fb0ff;
--shadow-tbg12-thumb: 0 4px 14px -4px rgb(0 0 0 / 0.8), inset 0 1px 1px rgb(255 255 255 / 0.9), inset 0 -2px 4px rgb(0 0 0 / 0.3);
--font-tbg12-sans: 'Familjen Grotesk', system-ui, sans-serif;
--animate-tbg12-sheen: tbg12-sheen 5s linear infinite;
--animate-tbg12-glint: tbg12-glint 5s ease-in-out infinite;
--animate-tbg12-sq1: tbg12-sq1 0.5s ease;
--animate-tbg12-sq2: tbg12-sq2 0.5s ease;
--animate-tbg12-sq3: tbg12-sq3 0.5s ease;
@keyframes tbg12-sheen {
to {
--tbg12-sheen: 360deg;
}
}
@keyframes tbg12-glint {
0%,
60% {
translate: -120% 0;
}
80%,
100% {
translate: 120% 0;
}
}
@keyframes tbg12-sq1 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq2 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
@keyframes tbg12-sq3 {
40% {
scale: 1.16 0.82;
}
70% {
scale: 0.94 1.08;
}
100% {
scale: 1 1;
}
}
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */
.tbg-12 {
width: 100%;
min-height: 100svh;
}
```How this works
Why three keyframes. A CSS animation only restarts when its name changes. If positions 2 and 3 share one squash animation, moving between them never replays it. Giving each position its own name fixes that:
<span class="animate-tbg12-sq1
group-has-[#tbg12-2:checked]/seg:animate-tbg12-sq2
group-has-[#tbg12-3:checked]/seg:animate-tbg12-sq3">What stays CSS. @property is an at-rule with no utility; it lets the browser interpolate --tbg12-sheen as an angle, so the metal actually rotates instead of jumping.
Make it yours
- Gold instead of chrome: change the gradient stops in the inner span to warm tones.
- Calmer: remove
after:animate-tbg12-glintand slow the sheen to 10s. - Four options:
grid-cols-4, thumbw-[calc((100%-0.75rem)/4)], add a fourth squash keyframe.
Gotchas — read before shipping
- The first squash plays on page load (position 1's animation starts immediately) — it reads as a settle; remove
animate-tbg12-sq1if you don't want it. - Infinite animations cost battery; they stop under
motion-reduce:. - Keep checked text dark (
text-tbg12-on) — the metal is light in places and dark in others.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
@property is Baseline 2024 (Firefox 128). Without it the gradient just stays still.