25 CSS Button Groups
15 / 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 Split Button with Quantum Chevron Reveal (Animated Menu Materialize)

A split button where the dropdown doesn't just appear — it materializes: on opening, the caret splits into a quantum shimmer and menu items phase in one after another with a clip-path wipe and blur-in. A cinematic 'CSS split button quantum chevron reveal' built on native <details>, so the drama is pure CSS and the semantics stay honest.

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-15" aria-label="Quantum chevron split button demo">
  <div class="btn-15__split">
    <button type="button" class="btn-15__primary">Deploy</button>
    <details class="btn-15__more">
      <summary class="btn-15__caret" aria-label="More deploy options"></summary>
      <div class="btn-15__menu" role="menu">
        <button type="button" class="btn-15__item" role="menuitem">Deploy to preview</button>
        <button type="button" class="btn-15__item" role="menuitem">Deploy to staging</button>
        <button type="button" class="btn-15__item" role="menuitem">Deploy to production</button>
        <button type="button" class="btn-15__item" role="menuitem">Roll back last</button>
      </div>
    </details>
  </div>
</section>
.btn-15,
.btn-15 *,
.btn-15 *::before,
.btn-15 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-15 {
  --a1: oklch(0.8 0.15 200);
  --a2: oklch(0.62 0.2 300);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: radial-gradient(120% 100% at 50% 0%,#0e1626,#060810);
  padding: 120px 20px;
}

.btn-15__split {
  position: relative;
  display: inline-flex;
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(120,200,255,.2),0 20px 50px -24px rgba(30,120,255,.6);
}

.btn-15__primary {
  border: none;
  background: linear-gradient(120deg,var(--a1),var(--a2));
  color: #07101c;
  font-size: 15.5px;
  font-weight: 800;
  padding: 0 24px;
  height: 52px;
  border-radius: 14px 0 0 14px;
  cursor: pointer;
  transition: filter .15s;
}

.btn-15__primary:hover {
  filter: brightness(1.1);
}

.btn-15__more {
  position: relative;
}

.btn-15__caret {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 52px;
  background: rgba(255,255,255,.06);
  border-radius: 0 14px 14px 0;
  cursor: pointer;
  position: relative;
  border-left: 1px solid rgba(150,210,255,.25);
  backdrop-filter: blur(4px);
}

.btn-15__caret::-webkit-details-marker {
  display: none;
}

.btn-15__caret::marker {
  content: "";
}

.btn-15__caret::after,
.btn-15__caret::before {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  border-right: 2.5px solid var(--a1);
  border-bottom: 2.5px solid var(--a1);
  transform: translateY(-2px) rotate(45deg);
  transition: transform .3s,opacity .3s;
}

.btn-15__caret::before {
  border-color: var(--a2);
  opacity: 0;
}

.btn-15__more[open] .btn-15__caret::after {
  transform: translateY(2px) rotate(-135deg) translateX(2px);
}

.btn-15__more[open] .btn-15__caret::before {
  transform: translateY(0) rotate(-135deg) translateX(-3px);
  opacity: .8;
}

.btn-15__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 230px;
  background: rgba(14,22,40,.92);
  border: 1px solid rgba(120,200,255,.22);
  border-radius: 14px;
  padding: 7px;
  z-index: 20;
  box-shadow: 0 30px 60px -24px rgba(20,80,220,.7);
  display: flex;
  flex-direction: column;
  gap: 2px;
  backdrop-filter: blur(14px);
}

.btn-15__more[open] .btn-15__menu {
  animation: btn-15-wipe .35s cubic-bezier(.2,.7,.3,1) both;
}

@keyframes btn-15-wipe {
  from {
    clip-path: inset(0 0 100% 0 round 14px);
    opacity: 0;
  }

  to {
    clip-path: inset(0 0 0 0 round 14px);
    opacity: 1;
  }
}

.btn-15__item {
  text-align: left;
  border: none;
  background: none;
  font-size: 14.5px;
  font-weight: 650;
  color: #cfe2f5;
  padding: 11px 13px;
  border-radius: 9px;
  cursor: pointer;
  transition: background .12s;
}

.btn-15__more[open] .btn-15__item {
  animation: btn-15-phase .4s both;
}

.btn-15__more[open] .btn-15__item:nth-child(1) {
  animation-delay: .05s;
}

.btn-15__more[open] .btn-15__item:nth-child(2) {
  animation-delay: .11s;
}

.btn-15__more[open] .btn-15__item:nth-child(3) {
  animation-delay: .17s;
}

.btn-15__more[open] .btn-15__item:nth-child(4) {
  animation-delay: .23s;
}

@keyframes btn-15-phase {
  from {
    opacity: 0;
    filter: blur(6px);
    translate: 0 -6px;
  }

  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

.btn-15__item:hover {
  background: rgba(120,200,255,.12);
}

.btn-15__primary:focus-visible,
.btn-15__caret:focus-visible,
.btn-15__item:focus-visible {
  outline: 3px solid var(--a1);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .btn-15__more[open] .btn-15__menu,
    .btn-15__more[open] .btn-15__item {
    animation: none;
  }

  .btn-15__caret::after,
    .btn-15__caret::before {
    transition: 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 Split Button with Quantum Chevron Reveal (Animated Menu Materialize)
Source: https://codefronts.com/components/css-button-groups/quantum-split/

A split button where the dropdown doesn't just appear — it materializes: on opening, the caret splits into a quantum shimmer and menu items phase in one after another with a clip-path wipe and blur-in. A cinematic 'CSS split button quantum chevron reveal' built on native <details>, so the drama is pure CSS and the semantics stay honest.
## HTML
```html
<section class="btn-15" aria-label="Quantum chevron split button demo">
  <div class="btn-15__split">
    <button type="button" class="btn-15__primary">Deploy</button>
    <details class="btn-15__more">
      <summary class="btn-15__caret" aria-label="More deploy options"></summary>
      <div class="btn-15__menu" role="menu">
        <button type="button" class="btn-15__item" role="menuitem">Deploy to preview</button>
        <button type="button" class="btn-15__item" role="menuitem">Deploy to staging</button>
        <button type="button" class="btn-15__item" role="menuitem">Deploy to production</button>
        <button type="button" class="btn-15__item" role="menuitem">Roll back last</button>
      </div>
    </details>
  </div>
</section>
```
## CSS
```css
.btn-15,
.btn-15 *,
.btn-15 *::before,
.btn-15 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-15 {
  --a1: oklch(0.8 0.15 200);
  --a2: oklch(0.62 0.2 300);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  background: radial-gradient(120% 100% at 50% 0%,#0e1626,#060810);
  padding: 120px 20px;
}

.btn-15__split {
  position: relative;
  display: inline-flex;
  border-radius: 14px;
  box-shadow: 0 0 0 1px rgba(120,200,255,.2),0 20px 50px -24px rgba(30,120,255,.6);
}

.btn-15__primary {
  border: none;
  background: linear-gradient(120deg,var(--a1),var(--a2));
  color: #07101c;
  font-size: 15.5px;
  font-weight: 800;
  padding: 0 24px;
  height: 52px;
  border-radius: 14px 0 0 14px;
  cursor: pointer;
  transition: filter .15s;
}

.btn-15__primary:hover {
  filter: brightness(1.1);
}

.btn-15__more {
  position: relative;
}

.btn-15__caret {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 52px;
  background: rgba(255,255,255,.06);
  border-radius: 0 14px 14px 0;
  cursor: pointer;
  position: relative;
  border-left: 1px solid rgba(150,210,255,.25);
  backdrop-filter: blur(4px);
}

.btn-15__caret::-webkit-details-marker {
  display: none;
}

.btn-15__caret::marker {
  content: "";
}

.btn-15__caret::after,
.btn-15__caret::before {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  border-right: 2.5px solid var(--a1);
  border-bottom: 2.5px solid var(--a1);
  transform: translateY(-2px) rotate(45deg);
  transition: transform .3s,opacity .3s;
}

.btn-15__caret::before {
  border-color: var(--a2);
  opacity: 0;
}

.btn-15__more[open] .btn-15__caret::after {
  transform: translateY(2px) rotate(-135deg) translateX(2px);
}

.btn-15__more[open] .btn-15__caret::before {
  transform: translateY(0) rotate(-135deg) translateX(-3px);
  opacity: .8;
}

.btn-15__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 230px;
  background: rgba(14,22,40,.92);
  border: 1px solid rgba(120,200,255,.22);
  border-radius: 14px;
  padding: 7px;
  z-index: 20;
  box-shadow: 0 30px 60px -24px rgba(20,80,220,.7);
  display: flex;
  flex-direction: column;
  gap: 2px;
  backdrop-filter: blur(14px);
}

.btn-15__more[open] .btn-15__menu {
  animation: btn-15-wipe .35s cubic-bezier(.2,.7,.3,1) both;
}

@keyframes btn-15-wipe {
  from {
    clip-path: inset(0 0 100% 0 round 14px);
    opacity: 0;
  }

  to {
    clip-path: inset(0 0 0 0 round 14px);
    opacity: 1;
  }
}

.btn-15__item {
  text-align: left;
  border: none;
  background: none;
  font-size: 14.5px;
  font-weight: 650;
  color: #cfe2f5;
  padding: 11px 13px;
  border-radius: 9px;
  cursor: pointer;
  transition: background .12s;
}

.btn-15__more[open] .btn-15__item {
  animation: btn-15-phase .4s both;
}

.btn-15__more[open] .btn-15__item:nth-child(1) {
  animation-delay: .05s;
}

.btn-15__more[open] .btn-15__item:nth-child(2) {
  animation-delay: .11s;
}

.btn-15__more[open] .btn-15__item:nth-child(3) {
  animation-delay: .17s;
}

.btn-15__more[open] .btn-15__item:nth-child(4) {
  animation-delay: .23s;
}

@keyframes btn-15-phase {
  from {
    opacity: 0;
    filter: blur(6px);
    translate: 0 -6px;
  }

  to {
    opacity: 1;
    filter: blur(0);
    translate: 0 0;
  }
}

.btn-15__item:hover {
  background: rgba(120,200,255,.12);
}

.btn-15__primary:focus-visible,
.btn-15__caret:focus-visible,
.btn-15__item:focus-visible {
  outline: 3px solid var(--a1);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .btn-15__more[open] .btn-15__menu,
    .btn-15__more[open] .btn-15__item {
    animation: none;
  }

  .btn-15__caret::after,
    .btn-15__caret::before {
    transition: none;
  }
}
```

How this works

Structurally it's demo 03 — a fused primary + <summary> caret inside <details>. The theatre is in the [open] reveal: the menu uses a clip-path that wipes open from the caret, and each item carries a staggered animation-delay so they phase in sequentially with a blur-to-sharp filter transition — the "quantum materialize" effect.

The caret itself doubles: a real chevron plus a ghost copy that offsets and fades on open (an RGB-adjacent split) to suggest the item is de-cohering into the menu. All transform/opacity/filter, GPU-friendly, and it collapses instantly and accessibly because <details> owns the state.

.item{ animation:btn-15-phase .4s both }
.item:nth-child(2){ animation-delay:.06s } /* stagger */

Techniques used: native details/summary state · clip-path wipe reveal from the caret · staggered per-item phase-in with blur→sharp filter · ghost-chevron split on open · GPU-only transform/opacity/filter

Make it yours

  • Twin accent = --a1/--a2 (cyan→violet quantum pair).
  • Stagger speed = the per-item animation-delay step.
  • Wipe direction = the menu's opening clip-path geometry.
  • Tone down the ghost split for a subtler reveal.

Gotchas — read before shipping

  • Entrance animations must use animation (not transition) because the menu goes from display:none — transitions can't cross that.
  • Keep the primary action a separate <button> so its click never toggles the menu.
  • Staggered reveals should stay under ~250ms total or the menu feels laggy to power users.
  • Native <details> is disclosure semantics, not a strict ARIA menu — layer roles if you need the full menu pattern.

Browser support

ChromeSafariFirefoxEdge
111+15.4+113+111+

Floor set by oklch(), backdrop-filter as this demo is written. The core technique itself goes back further — Chrome all.

details/summary, clip-path and CSS animation are all Baseline. The effect degrades gracefully to an instant menu if animations are reduced.

Techniques used in this demo

Search CodeFronts

Loading…