25 Tailwind CSS Button Groups
12 / 25

All 25 in 25 Tailwind CSS Button Groups

  1. 01Tailwind Split Button with Anchor-Positioned Popover Dropdown (No JS)
  2. 02Tailwind Segmented Control with Motion-Path Comet Indicator
  3. 03Tailwind Toggle Button Group with View Transitions Morphing Highlight
  4. 04Tailwind Pricing Toggle — Monthly / Yearly Billing Switch (No JS)
  5. 05Tailwind Segmented Control with Sliding Thumb (iOS Style)
  6. 06Tailwind Split Button Dropdown with Native Details Menu (Save Options)
  7. 07Tailwind Icon Button Group Toolbar with Tooltips (Rich Text Editor)
  8. 08Tailwind Pill Filter Button Group with Sliding Indicator (Anchor Positioning)
  9. 09Tailwind Toggle Button Group for Text Alignment (Radio Inputs, Live Preview)
  10. 10Tailwind Web3 Connect Wallet Button Grid (Wallet Sign-In Modal)
  11. 11Tailwind AI Model Selector Button Group (LLM Model Picker with Badges)
  12. 12Tailwind Liquid Metal Segmented Control (Animated Chrome Thumb)
  13. 13Tailwind Tab Button Group with Subgrid Underline Indicator
  14. 14Tailwind Multi-Select Chip Buttons (Tag Filter Checkboxes with Count)
  15. 15Tailwind Animated Split Button Deploy Menu (Staggered Dropdown Reveal)
  16. 16Tailwind Radio Button Group with Conic Fill Animation (Export Format Picker)
  17. 17Tailwind Retro LED Pixel Button Group (Dot-Matrix Display Buttons)
  18. 18Tailwind Hold-to-Confirm Button Group with Gradient Progress (Press and Hold)
  19. 19Tailwind Holographic Icon Segmented Control (Grid / List View Switcher)
  20. 20Tailwind 3D Vertical Button Group with Hover Depth (Perspective Stack)
  21. 21Tailwind Floating Action Button with Radial Speed Dial Menu (No JS)
  22. 22Tailwind Glitch Effect Button Group (Cyberpunk RGB-Split Audio Filter)
  23. 23Tailwind Animated Gradient Border Pill Buttons (Rotating Conic Ring)
  24. 24Tailwind Button Group with Ripple Click Effect (Radar Shockwave Feedback)
  25. 25Tailwind Button Group with Cursor-Following Spotlight Glow (Date Range Presets)
Pure TailwindMIT licensed

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.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time

Untitled UI React · Affiliate link

Get Untitled UI Pro

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>
@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.

1Load Tailwind
<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.

2Add the stylesheetRequiredthe @theme block is what makes the custom utilities exist
@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;
}
3Fonts used by this demooptional — skip if you have your own type stack
<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-labelledby and the group is a real radiogroup; segments grew to 48px.
  • UI upgrade: muted labels rose from #8b93a7 to oklch(0.74) (7.5:1 on the well).
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
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 &mdash; 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-glint and slow the sheen to 10s.
  • Four options: grid-cols-4, thumb w-[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-sq1 if 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

ChromeSafariFirefoxEdge
111+16.4+128+111+

@property is Baseline 2024 (Firefox 128). Without it the gradient just stays still.

Techniques used in this demo

Plain CSSno Tailwind surface

Search CodeFronts

Loading…