25 Tailwind CSS Button Groups
01 / 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 Split Button with Anchor-Positioned Popover Dropdown (No JS)

A primary action fused to a caret that opens a real popover menu, tethered with CSS Anchor Positioning written as arbitrary properties — [anchor-name:--tbg01-anc] on the caret, [position-anchor:--tbg01-anc] top-[anchor(bottom)] on the menu. The menu lives in the top layer, so no overflow-hidden parent can clip it, and v4's open: + starting: variants animate it in and out. Zero JavaScript.

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-01 w-full min-h-svh grid justify-items-center content-start bg-tbg01-bg bg-[radial-gradient(110%_80%_at_50%_0%,var(--color-tbg01-glow),transparent_70%)] px-5 pt-32 pb-16 font-tbg01-sans text-tbg01-ink" aria-label="Anchor-positioned popover split button demo">
  <div class="grid justify-items-center gap-10">
    <div class="group/split relative inline-flex rounded-2xl shadow-tbg01-lift">
      <button type="button" class="inline-flex h-12 cursor-pointer items-center gap-2.5 rounded-l-2xl bg-tbg01-acc pr-5 pl-4.5 text-[15px] font-semibold text-white transition hover:brightness-110 active:brightness-95 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg01-acc-d">
        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v11M7 10l5 5 5-5M5 20h14"/></svg>
        Export CSV
      </button>
      <button type="button" popovertarget="tbg01-menu" aria-label="More export formats" class="[anchor-name:--tbg01-anc] grid h-12 w-12 cursor-pointer place-items-center rounded-r-2xl border-l border-black/20 bg-tbg01-acc-d text-white transition hover:brightness-110 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg01-acc-d">
        <svg class="transition-transform duration-300 group-has-[:popover-open]/split:rotate-180 motion-reduce:transition-none" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
      </button>
      <div id="tbg01-menu" popover role="menu" aria-label="Export formats" class="m-0 mt-2 bottom-auto left-auto top-[anchor(bottom)] right-[anchor(right)] [position-anchor:--tbg01-anc] [position-try-fallbacks:flip-block] w-64 rounded-2xl border border-tbg01-line bg-white p-1.5 font-tbg01-sans text-tbg01-ink shadow-tbg01-menu opacity-0 -translate-y-1.5 transition-[opacity,translate,display,overlay] duration-200 transition-discrete open:translate-y-0 open:opacity-100 starting:open:-translate-y-1.5 starting:open:opacity-0 motion-reduce:translate-y-0 not-supports-[anchor-name:--x]:inset-0 not-supports-[anchor-name:--x]:m-auto not-supports-[anchor-name:--x]:h-fit">
        <p class="px-3 pt-2 pb-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-tbg01-mut">Export as</p>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">JSON <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.json</span></button>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">Excel workbook <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.xlsx</span></button>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">PDF report <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.pdf</span></button>
        <div role="separator" class="mx-2 my-1.5 h-px bg-tbg01-line"></div>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">Copy to clipboard <kbd class="ml-auto rounded-md border border-tbg01-line px-1.5 py-0.5 font-tbg01-mono text-[11px] text-tbg01-mut">⌘C</kbd></button>
      </div>
    </div>
    <p aria-hidden="true" class="rounded-full border border-tbg01-ink/10 bg-white/60 px-3.5 py-2 font-tbg01-mono text-[11.5px] text-tbg01-mut">popover · [anchor-name] · open: + starting:</p>
  </div>
</section>
@import "tailwindcss";

@theme {
  --color-tbg01-bg: #eceef3;
  --color-tbg01-bg: oklch(0.95 0.006 265);
  --color-tbg01-glow: #ffffff;
  --color-tbg01-ink: #2b1a15;
  --color-tbg01-ink: oklch(0.25 0.03 35);
  --color-tbg01-mut: #6e5d57;
  --color-tbg01-mut: oklch(0.5 0.025 35);
  --color-tbg01-line: #efe3df;
  --color-tbg01-line: oklch(0.92 0.015 35);
  --color-tbg01-tint: #fcefeb;
  --color-tbg01-tint: oklch(0.96 0.02 35);
  --color-tbg01-acc: #e5452f;
  --color-tbg01-acc: oklch(0.6 0.2 30);
  --color-tbg01-acc-d: #c4321f;
  --color-tbg01-acc-d: oklch(0.52 0.19 30);
  --shadow-tbg01-lift: 0 14px 34px -16px rgb(120 30 10 / 0.55);
  --shadow-tbg01-menu: 0 26px 54px -22px rgb(120 40 20 / 0.45), 0 2px 6px rgb(0 0 0 / 0.04);
  --font-tbg01-sans: 'Geist', system-ui, sans-serif;
  --font-tbg01-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */

.tbg-01 {
  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";

@theme {
  --color-tbg01-bg: #eceef3;
  --color-tbg01-bg: oklch(0.95 0.006 265);
  --color-tbg01-glow: #ffffff;
  --color-tbg01-ink: #2b1a15;
  --color-tbg01-ink: oklch(0.25 0.03 35);
  --color-tbg01-mut: #6e5d57;
  --color-tbg01-mut: oklch(0.5 0.025 35);
  --color-tbg01-line: #efe3df;
  --color-tbg01-line: oklch(0.92 0.015 35);
  --color-tbg01-tint: #fcefeb;
  --color-tbg01-tint: oklch(0.96 0.02 35);
  --color-tbg01-acc: #e5452f;
  --color-tbg01-acc: oklch(0.6 0.2 30);
  --color-tbg01-acc-d: #c4321f;
  --color-tbg01-acc-d: oklch(0.52 0.19 30);
  --shadow-tbg01-lift: 0 14px 34px -16px rgb(120 30 10 / 0.55);
  --shadow-tbg01-menu: 0 26px 54px -22px rgb(120 40 20 / 0.45), 0 2px 6px rgb(0 0 0 / 0.04);
  --font-tbg01-sans: 'Geist', system-ui, sans-serif;
  --font-tbg01-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */

.tbg-01 {
  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=Geist:wght@400..800&family=Geist+Mono:wght@400..600&display=swap">

What changed in the port

Which parts became utilities, which stayed CSS, and why.

  • Every .btn-01__* rule became utilities on the element it styles; the @starting-style block became starting:open: and allow-discrete became transition-discrete.
  • UI upgrade: each menu item carries popovertargetaction="hide", so choosing a format closes the menu — the CSS original stayed open after a click.
  • UI upgrade: the caret now rotates while the menu is open (the original's rotate rule never fired), the primary action gained a download icon, and every row shows its file extension or shortcut.
  • UI upgrade: the anchor fallback is now a centred, fully usable menu instead of one pinned to the viewport's bottom-right corner.
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 Split Button with Anchor-Positioned Popover Dropdown (No JS)
Source: https://codefronts.com/components/tailwind-css-button-groups/tailwind-split-button-anchor-popover-dropdown/

A primary action fused to a caret that opens a real popover menu, tethered with CSS Anchor Positioning written as arbitrary properties — [anchor-name:--tbg01-anc] on the caret, [position-anchor:--tbg01-anc] top-[anchor(bottom)] on the menu. The menu lives in the top layer, so no overflow-hidden parent can clip it, and v4's open: + starting: variants animate it in and out. Zero JavaScript.
## HTML
```html
<section class="tbg-01 w-full min-h-svh grid justify-items-center content-start bg-tbg01-bg bg-[radial-gradient(110%_80%_at_50%_0%,var(--color-tbg01-glow),transparent_70%)] px-5 pt-32 pb-16 font-tbg01-sans text-tbg01-ink" aria-label="Anchor-positioned popover split button demo">
  <div class="grid justify-items-center gap-10">
    <div class="group/split relative inline-flex rounded-2xl shadow-tbg01-lift">
      <button type="button" class="inline-flex h-12 cursor-pointer items-center gap-2.5 rounded-l-2xl bg-tbg01-acc pr-5 pl-4.5 text-[15px] font-semibold text-white transition hover:brightness-110 active:brightness-95 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg01-acc-d">
        <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 4v11M7 10l5 5 5-5M5 20h14"/></svg>
        Export CSV
      </button>
      <button type="button" popovertarget="tbg01-menu" aria-label="More export formats" class="[anchor-name:--tbg01-anc] grid h-12 w-12 cursor-pointer place-items-center rounded-r-2xl border-l border-black/20 bg-tbg01-acc-d text-white transition hover:brightness-110 focus-visible:outline-3 focus-visible:outline-offset-2 focus-visible:outline-tbg01-acc-d">
        <svg class="transition-transform duration-300 group-has-[:popover-open]/split:rotate-180 motion-reduce:transition-none" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
      </button>
      <div id="tbg01-menu" popover role="menu" aria-label="Export formats" class="m-0 mt-2 bottom-auto left-auto top-[anchor(bottom)] right-[anchor(right)] [position-anchor:--tbg01-anc] [position-try-fallbacks:flip-block] w-64 rounded-2xl border border-tbg01-line bg-white p-1.5 font-tbg01-sans text-tbg01-ink shadow-tbg01-menu opacity-0 -translate-y-1.5 transition-[opacity,translate,display,overlay] duration-200 transition-discrete open:translate-y-0 open:opacity-100 starting:open:-translate-y-1.5 starting:open:opacity-0 motion-reduce:translate-y-0 not-supports-[anchor-name:--x]:inset-0 not-supports-[anchor-name:--x]:m-auto not-supports-[anchor-name:--x]:h-fit">
        <p class="px-3 pt-2 pb-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-tbg01-mut">Export as</p>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">JSON <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.json</span></button>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">Excel workbook <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.xlsx</span></button>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">PDF report <span class="ml-auto font-tbg01-mono text-xs text-tbg01-mut">.pdf</span></button>
        <div role="separator" class="mx-2 my-1.5 h-px bg-tbg01-line"></div>
        <button type="button" role="menuitem" popovertarget="tbg01-menu" popovertargetaction="hide" class="flex min-h-11 w-full cursor-pointer items-center gap-3 rounded-xl px-3 text-left text-[14.5px] font-medium hover:bg-tbg01-tint focus-visible:bg-tbg01-tint focus-visible:outline-2 focus-visible:outline-tbg01-acc">Copy to clipboard <kbd class="ml-auto rounded-md border border-tbg01-line px-1.5 py-0.5 font-tbg01-mono text-[11px] text-tbg01-mut">⌘C</kbd></button>
      </div>
    </div>
    <p aria-hidden="true" class="rounded-full border border-tbg01-ink/10 bg-white/60 px-3.5 py-2 font-tbg01-mono text-[11.5px] text-tbg01-mut">popover · [anchor-name] · open: + starting:</p>
  </div>
</section>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tbg01-bg: #eceef3;
  --color-tbg01-bg: oklch(0.95 0.006 265);
  --color-tbg01-glow: #ffffff;
  --color-tbg01-ink: #2b1a15;
  --color-tbg01-ink: oklch(0.25 0.03 35);
  --color-tbg01-mut: #6e5d57;
  --color-tbg01-mut: oklch(0.5 0.025 35);
  --color-tbg01-line: #efe3df;
  --color-tbg01-line: oklch(0.92 0.015 35);
  --color-tbg01-tint: #fcefeb;
  --color-tbg01-tint: oklch(0.96 0.02 35);
  --color-tbg01-acc: #e5452f;
  --color-tbg01-acc: oklch(0.6 0.2 30);
  --color-tbg01-acc-d: #c4321f;
  --color-tbg01-acc-d: oklch(0.52 0.19 30);
  --shadow-tbg01-lift: 0 14px 34px -16px rgb(120 30 10 / 0.55);
  --shadow-tbg01-menu: 0 26px 54px -22px rgb(120 40 20 / 0.45), 0 2px 6px rgb(0 0 0 / 0.04);
  --font-tbg01-sans: 'Geist', system-ui, sans-serif;
  --font-tbg01-mono: 'Geist Mono', ui-monospace, monospace;
}
/* Root rule: the full-bleed contract, restated so the stage is sized before the browser build compiles. */

.tbg-01 {
  width: 100%;
  min-height: 100svh;
}
```

How this works

The browser does the plumbing. The caret is a plain <button popovertarget="tbg01-menu">; the menu is a popover. Light-dismiss, Escape and focus return come with the platform.

<button popovertarget="tbg01-menu" class="[anchor-name:--tbg01-anc]">
<div id="tbg01-menu" popover class="[position-anchor:--tbg01-anc]
  top-[anchor(bottom)] right-[anchor(right)] [position-try-fallbacks:flip-block]
  opacity-0 open:opacity-100 starting:open:opacity-0 transition-discrete">

Why bottom-auto left-auto m-0. The UA stylesheet centres every popover with inset:0; margin:auto. Releasing two edges and the margin hands placement to the anchor.

Fallback. not-supports-[anchor-name:--x]: restores a centred dialog-style menu, so older engines still get a usable, on-screen list.

Make it yours

  • Open upwards by default: swap top-[anchor(bottom)] for bottom-[anchor(top)] and mt-2 for mb-2.
  • Left-align the menu with the primary button: use left-[anchor(left)] and move anchor-name to the wrapper.
  • Re-theme: edit --color-tbg01-acc and --color-tbg01-acc-d in @theme; the caret, focus rings and hover tint follow.

Gotchas — read before shipping

  • Anchor names are document-global — give every split button its own --name (or use anchor-scope).
  • The Popover API is a disclosure, not a full ARIA menu: arrow-key navigation between items needs script if you require it.
  • Keep the primary action a separate <button> so a click on the label never opens the menu.

Browser support

ChromeSafariFirefoxEdge
125+26+147+125+

Popover API is Baseline 2024. Anchor Positioning is newer — not-supports-[anchor-name:--x]: keeps a centred fallback menu everywhere else. Tailwind v4's own sheet needs Chrome 111 / Safari 16.4 / Firefox 128.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Search CodeFronts

Loading…