25 CSS Button Groups
12 / 25

All 25 in 25 CSS Button Groups

  1. 01CSS Split Button with Anchor-Positioned Popover Menu (Popover API, No JS)
  2. 02CSS Segmented Control with Motion-Path Comet Thumb (offset-path Arc Travel)
  3. 03CSS View Transitions Toggle Button Group with Morphing Thumb (Shared-Element FLIP)
  4. 04CSS Pricing Toggle with Monthly / Yearly Switch (Pure CSS, No JavaScript)
  5. 05CSS Segmented Control with Sliding Thumb Indicator (iOS-Style, Pure CSS)
  6. 06CSS Split Button Dropdown with Native <details> (Primary Action + Menu)
  7. 07CSS Icon Button Group for Toolbar Actions (Accessible Tooltips, Pure CSS)
  8. 08CSS Pill Radio Button Group with Sliding Indicator (Filter Pills, Pure CSS)
  9. 09CSS Toggle Button Group with Radio Inputs (Single-Select Alignment Group)
  10. 10CSS Wallet Connect Button Grid for Web3 Sign-In (MetaMask, WalletConnect, Coinbase)
  11. 11CSS AI Model Selector Button Group with Provider Badges (LLM Model Picker)
  12. 12CSS Segmented Control with Liquid Mercury Thumb (Metallic Stretch Animation)
  13. 13CSS Button Group with Subgrid Underline Beam (Perfectly Aligned Tab Indicator)
  14. 14CSS Multi-Select Chip Buttons with Acid Glow (Toggleable Tag Filters)
  15. 15CSS Split Button with Quantum Chevron Reveal (Animated Menu Materialize)
  16. 16CSS Format Toggle Button Group with Conic Fill (Radial Sweep Select)
  17. 17CSS Pixel Matrix Button Group with LED Segment Glow (Retro Display Buttons)
  18. 18CSS Multi-Action Button Group with Gradient Flow Progress (Animated CTA Bar)
  19. 19CSS Icon Segmented Control with Holographic Sheen (Iridescent View Switcher)
  20. 20CSS Vertical Button Stack with 3D Hover Depth (Tilting Card Buttons)
  21. 21CSS Floating Action Button with Radial Sub-Button Menu (Arc Fan-Out, Pure CSS)
  22. 22CSS Filter Button Group with RGB-Split Glitch Active State (Cyberpunk Toggle)
  23. 23CSS Pill Button Group with Rotating Conic Plasma Border (Animated Gradient Ring)
  24. 24CSS Action Button Row with Radar Shockwave Click Feedback (Ripple Pulse)
  25. 25CSS Date-Range Preset Button Group with Cursor-Following Glow (Spotlight Hover)
Pure CSSMIT licensed

CSS Segmented Control with Liquid Mercury Thumb (Metallic Stretch Animation)

A segmented control whose thumb behaves like a bead of liquid mercury — a chromed, specular-highlit pill that stretches and squashes as it travels between options, then settles with a metallic shimmer. A show-stopping 'CSS liquid mercury segmented control' with real radios underneath, so it stays keyboard-operable while looking impossibly fluid.

Live Demo
Plain CSSTailwind v4
Try it

Build UI like this from scratch: Flexbox, Grid, animations & responsive CSS

ZTM · The CSS Bootcamp · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<section class="btn-12" aria-label="Liquid mercury segmented control demo">
  <div class="btn-12__wrap">
    <p class="btn-12__label">Rendering mode</p>
    <div class="btn-12__seg" role="group" aria-label="Rendering mode">
      <input type="radio" name="btn-12" id="btn-12-1" class="btn-12__in" checked>
      <label for="btn-12-1" class="btn-12__opt">Auto</label>
      <input type="radio" name="btn-12" id="btn-12-2" class="btn-12__in">
      <label for="btn-12-2" class="btn-12__opt">Metal</label>
      <input type="radio" name="btn-12" id="btn-12-3" class="btn-12__in">
      <label for="btn-12-3" class="btn-12__opt">Glass</label>
      <span class="btn-12__thumb" aria-hidden="true"></span>
    </div>
  </div>
</section>
@property --btn-12-sheen {
  syntax: "<angle>";
  inherits: true;
  initial-value: 0deg;
}

.btn-12,
.btn-12 *,
.btn-12 *::before,
.btn-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-12 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(120% 120% at 50% 0%,#20242e,#0c0e14);
  padding: 44px 20px;
}

.btn-12__wrap {
  width: min(460px,100%);
}

.btn-12__label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #aeb6c9;
  margin: 0 0 14px 4px;
}

.btn-12__seg {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3,1fr);
  padding: 6px;
  background: #171a22;
  border: 1px solid #2a2f3c;
  border-radius: 16px;
  box-shadow: inset 0 2px 8px rgba(0,0,0,.6);
}

.btn-12__in {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.btn-12__opt {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-size: 15px;
  font-weight: 700;
  color: #8b93a7;
  cursor: pointer;
  transition: color .3s;
}

.btn-12__thumb {
  position: absolute;
  z-index: 1;
  top: 6px;
  left: 6px;
  width: calc((100% - 12px)/3);
  height: calc(100% - 12px);
  border-radius: 11px;
  background: linear-gradient(var(--btn-12-sheen),#f4f6fb 0%,#c3cad9 30%,#7a8398 55%,#dfe4ee 80%,#f4f6fb 100%);
  box-shadow: 0 4px 14px -4px rgba(0,0,0,.8),inset 0 1px 1px rgba(255,255,255,.9),inset 0 -2px 4px rgba(0,0,0,.3);
  animation: btn-12-sheen 5s linear infinite;
  overflow: hidden;
  transition: translate .5s cubic-bezier(.5,-0.2,.3,1.3);
}

.btn-12__thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,transparent 35%,rgba(255,255,255,.85) 50%,transparent 65%);
  translate: -120% 0;
  animation: btn-12-glint 5s ease-in-out infinite;
}

@keyframes btn-12-sheen {
  to {
    --btn-12-sheen: 360deg;
  }
}

@keyframes btn-12-glint {
  0%,
    60% {
    translate: -120% 0;
  }

  80%,
    100% {
    translate: 120% 0;
  }
}

.btn-12__seg:has(#btn-12-2:checked) .btn-12__thumb {
  translate: 100% 0;
  animation: btn-12-sheen 5s linear infinite,btn-12-squash .5s ease;
}

.btn-12__seg:has(#btn-12-3:checked) .btn-12__thumb {
  translate: 200% 0;
  animation: btn-12-sheen 5s linear infinite,btn-12-squash .5s ease;
}

@keyframes btn-12-squash {
  40% {
    scale: 1.16 .82;
  }

  70% {
    scale: .94 1.08;
  }

  100% {
    scale: 1 1;
  }
}

.btn-12__in:checked + .btn-12__opt {
  color: #11141c;
  text-shadow: 0 1px 0 rgba(255,255,255,.4);
}

.btn-12__in:focus-visible + .btn-12__opt {
  outline: 3px solid #9fb0ff;
  outline-offset: 3px;
  border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .btn-12__thumb,
    .btn-12__thumb::after {
    animation: none;
    transition: translate .3s;
  }

  .btn-12__seg:has(.btn-12__in:checked) .btn-12__thumb {
    animation: none;
  }
}
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 Button Group 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: CSS Segmented Control with Liquid Mercury Thumb (Metallic Stretch Animation)
Source: https://codefronts.com/components/css-button-groups/liquid-mercury/

A segmented control whose thumb behaves like a bead of liquid mercury — a chromed, specular-highlit pill that stretches and squashes as it travels between options, then settles with a metallic shimmer. A show-stopping 'CSS liquid mercury segmented control' with real radios underneath, so it stays keyboard-operable while looking impossibly fluid.
## HTML
```html
<section class="btn-12" aria-label="Liquid mercury segmented control demo">
  <div class="btn-12__wrap">
    <p class="btn-12__label">Rendering mode</p>
    <div class="btn-12__seg" role="group" aria-label="Rendering mode">
      <input type="radio" name="btn-12" id="btn-12-1" class="btn-12__in" checked>
      <label for="btn-12-1" class="btn-12__opt">Auto</label>
      <input type="radio" name="btn-12" id="btn-12-2" class="btn-12__in">
      <label for="btn-12-2" class="btn-12__opt">Metal</label>
      <input type="radio" name="btn-12" id="btn-12-3" class="btn-12__in">
      <label for="btn-12-3" class="btn-12__opt">Glass</label>
      <span class="btn-12__thumb" aria-hidden="true"></span>
    </div>
  </div>
</section>
```
## CSS
```css
@property --btn-12-sheen {
  syntax: "<angle>";
  inherits: true;
  initial-value: 0deg;
}

.btn-12,
.btn-12 *,
.btn-12 *::before,
.btn-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-12 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(120% 120% at 50% 0%,#20242e,#0c0e14);
  padding: 44px 20px;
}

.btn-12__wrap {
  width: min(460px,100%);
}

.btn-12__label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #aeb6c9;
  margin: 0 0 14px 4px;
}

.btn-12__seg {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3,1fr);
  padding: 6px;
  background: #171a22;
  border: 1px solid #2a2f3c;
  border-radius: 16px;
  box-shadow: inset 0 2px 8px rgba(0,0,0,.6);
}

.btn-12__in {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.btn-12__opt {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-size: 15px;
  font-weight: 700;
  color: #8b93a7;
  cursor: pointer;
  transition: color .3s;
}

.btn-12__thumb {
  position: absolute;
  z-index: 1;
  top: 6px;
  left: 6px;
  width: calc((100% - 12px)/3);
  height: calc(100% - 12px);
  border-radius: 11px;
  background: linear-gradient(var(--btn-12-sheen),#f4f6fb 0%,#c3cad9 30%,#7a8398 55%,#dfe4ee 80%,#f4f6fb 100%);
  box-shadow: 0 4px 14px -4px rgba(0,0,0,.8),inset 0 1px 1px rgba(255,255,255,.9),inset 0 -2px 4px rgba(0,0,0,.3);
  animation: btn-12-sheen 5s linear infinite;
  overflow: hidden;
  transition: translate .5s cubic-bezier(.5,-0.2,.3,1.3);
}

.btn-12__thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg,transparent 35%,rgba(255,255,255,.85) 50%,transparent 65%);
  translate: -120% 0;
  animation: btn-12-glint 5s ease-in-out infinite;
}

@keyframes btn-12-sheen {
  to {
    --btn-12-sheen: 360deg;
  }
}

@keyframes btn-12-glint {
  0%,
    60% {
    translate: -120% 0;
  }

  80%,
    100% {
    translate: 120% 0;
  }
}

.btn-12__seg:has(#btn-12-2:checked) .btn-12__thumb {
  translate: 100% 0;
  animation: btn-12-sheen 5s linear infinite,btn-12-squash .5s ease;
}

.btn-12__seg:has(#btn-12-3:checked) .btn-12__thumb {
  translate: 200% 0;
  animation: btn-12-sheen 5s linear infinite,btn-12-squash .5s ease;
}

@keyframes btn-12-squash {
  40% {
    scale: 1.16 .82;
  }

  70% {
    scale: .94 1.08;
  }

  100% {
    scale: 1 1;
  }
}

.btn-12__in:checked + .btn-12__opt {
  color: #11141c;
  text-shadow: 0 1px 0 rgba(255,255,255,.4);
}

.btn-12__in:focus-visible + .btn-12__opt {
  outline: 3px solid #9fb0ff;
  outline-offset: 3px;
  border-radius: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .btn-12__thumb,
    .btn-12__thumb::after {
    animation: none;
    transition: translate .3s;
  }

  .btn-12__seg:has(.btn-12__in:checked) .btn-12__thumb {
    animation: none;
  }
}
```

How this works

The base is the same accessible pattern as demo 02 — hidden radios, a proportional thumb, :has() to translate by index. The liquid feel comes from three layers on the thumb: a metallic gradient (light→dark→light) for the chrome body, a moving specular highlight pseudo-element, and a scaleX squash-and-stretch that runs during travel via a keyframe triggered on the transition.

As the thumb moves, scale:1.18 0.86 mid-travel then springs back to 1 1, reading as surface tension. An @property-animated angle rotates the specular sheen so the chrome looks alive even at rest. Everything is transform/opacity only, so it stays 60fps.

@property --sheen{ syntax:'<angle>'; inherits:true; initial-value:0deg }
.thumb{ background:linear-gradient(var(--sheen),#f4f6fb,#8b93a7,#eef1f7) }

Techniques used: @property-animated specular angle · squash-and-stretch scaleX during travel · metallic multi-stop gradient · moving highlight pseudo-element · proportional :has() thumb translate · reduced-motion fallback to a plain slide

Make it yours

  • Chrome tint = the gradient stops on .thumb; go gold or copper by shifting them warm.
  • Squash intensity = the mid-keyframe scale values.
  • Sheen speed = the --sheen animation duration.
  • Drop the squash entirely for a calmer metallic slide.

Gotchas — read before shipping

  • Squash-and-stretch must return exactly to scale:1 1 or the thumb ends misaligned.
  • Register --sheen with @property so the angle interpolates — a plain custom prop won't animate.
  • Metallic gradients can drop text contrast; keep active-label text pure white with a subtle shadow.
  • Honor prefers-reduced-motion: kill the squash and sheen, keep the slide.

Browser support

ChromeSafariFirefoxEdge
105+16.4+128+105+

Floor set by :has(), @property as this demo is written. The core technique itself goes back further — Chrome 85+ (@property).

@property is Baseline 2024. Without it the sheen sits static and the thumb still slides + squashes — no loss of function.

Techniques used in this demo

Search CodeFronts

Loading…