25 CSS Button Groups
03 / 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 View Transitions Toggle Button Group with Morphing Thumb (Shared-Element FLIP)

A toggle button group where the active highlight doesn't slide — it morphs. Powered by the View Transitions API, the highlight is a shared element that the browser FLIP-animates from the old button to the new one, smoothly stretching across size and position changes. The forward-looking 'CSS View Transitions button group morphing highlight' pattern for 2027 UIs.

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-03" aria-label="View transitions morphing toggle group demo">
  <div class="btn-03__wrap">
    <p class="btn-03__label">Workspace theme</p>
    <div class="btn-03__group" id="btn-03-group" role="radiogroup" aria-label="Workspace theme">
      <button type="button" class="btn-03__btn" role="radio" aria-checked="true" tabindex="0"><span class="btn-03__hl"></span><span class="btn-03__t">Focus</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Comfortable</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Compact</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Zen</span></button>
    </div>
  </div>
</section>
.btn-03,
.btn-03 *,
.btn-03 *::before,
.btn-03 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-03 {
  --accent: oklch(0.5 0.22 265);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg,#f4f5fa,#e7e9f3);
  padding: 44px 20px;
}

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

.btn-03__label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 14px 4px;
}

.btn-03__group {
  display: inline-flex;
  gap: 6px;
  padding: 6px;
  background: #fff;
  border: 1px solid #e2e4f0;
  border-radius: 16px;
  box-shadow: 0 16px 40px -26px rgba(40,30,120,.5);
}

.btn-03__btn {
  position: relative;
  border: none;
  background: none;
  min-height: 46px;
  padding: 0 22px;
  border-radius: 11px;
  font-size: 15px;
  font-weight: 700;
  color: #5a5f80;
  cursor: pointer;
  overflow: hidden;
  transition: color .2s;
}

.btn-03__hl {
  position: absolute;
  inset: 0;
  border-radius: 11px;
  background: var(--accent);
  opacity: 0;
}

.btn-03__t {
  position: relative;
  z-index: 1;
}

.btn-03__btn[aria-checked=true] {
  color: #fff;
}

.btn-03__btn[aria-checked=true] .btn-03__hl {
  opacity: 1;
  view-transition-name: btn-03-thumb;
  box-shadow: 0 6px 16px -6px var(--accent);
}

.btn-03__btn:not([aria-checked=true]):hover {
  color: #2a2e50;
}

.btn-03__btn:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

::view-transition-group(btn-03-thumb) {
  animation-duration: .45s;
  animation-timing-function: cubic-bezier(.34,1.25,.4,1);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(btn-03-thumb) {
    animation-duration: 1ms;
  }
}
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 View Transitions Toggle Button Group with Morphing Thumb (Shared-Element FLIP)
Source: https://codefronts.com/components/css-button-groups/css-view-transitions-toggle-button-group-with-morphing-thumb-shared-element-flip/

A toggle button group where the active highlight doesn't slide — it morphs. Powered by the View Transitions API, the highlight is a shared element that the browser FLIP-animates from the old button to the new one, smoothly stretching across size and position changes. The forward-looking 'CSS View Transitions button group morphing highlight' pattern for 2027 UIs.
## HTML
```html
<section class="btn-03" aria-label="View transitions morphing toggle group demo">
  <div class="btn-03__wrap">
    <p class="btn-03__label">Workspace theme</p>
    <div class="btn-03__group" id="btn-03-group" role="radiogroup" aria-label="Workspace theme">
      <button type="button" class="btn-03__btn" role="radio" aria-checked="true" tabindex="0"><span class="btn-03__hl"></span><span class="btn-03__t">Focus</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Comfortable</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Compact</span></button>
      <button type="button" class="btn-03__btn" role="radio" aria-checked="false" tabindex="-1"><span class="btn-03__hl"></span><span class="btn-03__t">Zen</span></button>
    </div>
  </div>
</section>
```
## CSS
```css
.btn-03,
.btn-03 *,
.btn-03 *::before,
.btn-03 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-03 {
  --accent: oklch(0.5 0.22 265);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(160deg,#f4f5fa,#e7e9f3);
  padding: 44px 20px;
}

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

.btn-03__label {
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 14px 4px;
}

.btn-03__group {
  display: inline-flex;
  gap: 6px;
  padding: 6px;
  background: #fff;
  border: 1px solid #e2e4f0;
  border-radius: 16px;
  box-shadow: 0 16px 40px -26px rgba(40,30,120,.5);
}

.btn-03__btn {
  position: relative;
  border: none;
  background: none;
  min-height: 46px;
  padding: 0 22px;
  border-radius: 11px;
  font-size: 15px;
  font-weight: 700;
  color: #5a5f80;
  cursor: pointer;
  overflow: hidden;
  transition: color .2s;
}

.btn-03__hl {
  position: absolute;
  inset: 0;
  border-radius: 11px;
  background: var(--accent);
  opacity: 0;
}

.btn-03__t {
  position: relative;
  z-index: 1;
}

.btn-03__btn[aria-checked=true] {
  color: #fff;
}

.btn-03__btn[aria-checked=true] .btn-03__hl {
  opacity: 1;
  view-transition-name: btn-03-thumb;
  box-shadow: 0 6px 16px -6px var(--accent);
}

.btn-03__btn:not([aria-checked=true]):hover {
  color: #2a2e50;
}

.btn-03__btn:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

::view-transition-group(btn-03-thumb) {
  animation-duration: .45s;
  animation-timing-function: cubic-bezier(.34,1.25,.4,1);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(btn-03-thumb) {
    animation-duration: 1ms;
  }
}
```

How this works

Each button owns a highlight layer, but only the active one carries view-transition-name:btn-03-thumb. When selection changes, a ~20-line accessible radiogroup helper wraps the state update in document.startViewTransition(). The browser snapshots the old highlight and the new one, and because they share a transition name it treats them as the same element — morphing position AND size (a real shared-element transition, not a CSS translate).

The result reads like the highlight liquid-morphs between buttons of different widths, something a fixed-width sliding thumb can't do. Keyboard arrow keys and roving tabindex keep it a proper radiogroup; without the API it degrades to an instant, still-correct toggle.

.btn[aria-checked=true] .hl{ view-transition-name:btn-03-thumb }
document.startViewTransition(() => setActive(btn));  // browser FLIP-morphs the highlight

Techniques used: View Transitions API shared-element morph (position + size FLIP) · single view-transition-name on the active highlight only · accessible radiogroup with roving tabindex + arrow keys · ::view-transition-group custom easing · reduced-motion instant fallback · progressive enhancement (works with no API)

Make it yours

  • Morph feel = the ::view-transition-group(btn-03-thumb) animation duration/easing.
  • Highlight color = --accent.
  • Add options freely — variable widths are exactly what the morph shows off.
  • Swap to a pill or underline highlight; the shared-element morph adapts.

Gotchas — read before shipping

  • A view-transition-name must be unique per snapshot — keep it ONLY on the active highlight, never on all of them.
  • The View Transitions API is Chrome/Safari; guard with if (document.startViewTransition) and fall back to an instant update (done here).
  • Keep it a real radiogroup: roving tabindex + arrow keys, one Tab stop — don't lose keyboard operability for the effect.
  • Neutralize the transition under prefers-reduced-motion by zeroing the ::view-transition-group animation.

Browser support

ChromeSafariFirefoxEdge
111+18+no111+

Same-document View Transitions ship in Chrome 111+ and Safari 18+; Firefox not yet. It's a progressive enhancement — the group works everywhere and only gains the morph where supported.

Techniques used in this demo

Search CodeFronts

Loading…