25 Tailwind CSS Button Groups

20 Pure Tailwind5 Tailwind + JSPublished

25 Tailwind CSS button groups for SaaS pricing toggles, rich-text editor toolbars, AI model pickers, Web3 wallet grids, and export-format selectors — spanning iOS segmented controls, split buttons, pill filters, multi-select chips, radial speed dials, hold-to-confirm bars, and cursor-spotlight action rows. Scoped under .tbg-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related25 CSS Button Groups51 Tailwind CSS Buttons28 Tailwind CSS Close Buttons20 Tailwind SVG Stroke Buttons

Premium upgradePartner: Untitled UI

Affiliate link

Building a whole Tailwind app, not just this component?

You just browsed 25 Tailwind button groups. Untitled UI Pro unlocks 5,000+ components and sections and 250+ page examples for React, styled with Tailwind CSS v4. One licence is a one-time $349 with lifetime updates; teams start at $999.

Get Untitled UI Pro

Untitled UI React. We may earn a commission, at no extra cost to you.

01. Tailwind Split Button with Anchor-Positioned Popover Dropdown (No JS) (opens in a new tab)

Published

Pure Tailwind
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.

02. Tailwind Segmented Control with Motion-Path Comet Indicator (opens in a new tab)

Published

Pure Tailwind
A four-way mode switch whose thumb travels an arc instead of a straight line, trailed by a blurred comet tail that lags behind on a slower curve. The motion path is an arbitrary property — [offset-path:path('M43,28C123,6,203,6,283,28')] — and each radio moves it with group-has-[#tbg02-3:checked]/seg:[offset-distance:66.666%]. A live status line under the control reports the active mode, still with no JavaScript.

03. Tailwind Toggle Button Group with View Transitions Morphing Highlight (opens in a new tab)

Published

Tailwind + JS
A density switcher whose highlight morphs from one button to the next with the View Transitions API — the browser snapshots the old and new highlight (both named [view-transition-name:tbg03-thumb]) and tweens between them, no position math. State is pure ARIA: aria-checked:text-white and group-aria-checked:opacity-100 read the attribute a 25-line controller sets, with full arrow-key support.

04. Tailwind Pricing Toggle — Monthly / Yearly Billing Switch (No JS) (opens in a new tab)

Published

Pure Tailwind
The SaaS pricing switch, solved in markup: two radios drive a sliding pill, the prices on every plan card and the savings line beneath them — all through group-has-[#tbg04-y:checked]/card: variants on the elements that change. The inactive price is invisible, not just transparent, so screen readers only ever announce the price you can see. No JavaScript.

05. Tailwind Segmented Control with Sliding Thumb (iOS Style) (opens in a new tab)

Published

Pure Tailwind
The most-searched button-group pattern: an iOS-style segmented control whose thumb springs between options. Radios carry state and keyboard support; the thumb slides with group-has-[#tbg05-3:checked]/seg:translate-x-[200%] on a spring curve written with v4's ease-[linear(…)]. Each segment shows a sub-value, and the whole control is pure Tailwind.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

08. Tailwind Pill Filter Button Group with Sliding Indicator (Anchor Positioning) (opens in a new tab)

Published

Pure Tailwind
Category filter pills of different widths with a single highlight that glides to whichever is checked — and resizes to fit it. The CSS original hard-coded every pill's pixel offset; this port lets CSS Anchor Positioning measure instead: the checked pill gets has-checked:[anchor-name:--tbg08-on] and the indicator sizes itself with w-[anchor-size(width)]. Change a label, add a count, swap the font — it still lines up.

09. Tailwind Toggle Button Group for Text Alignment (Radio Inputs, Live Preview) (opens in a new tab)

Published

Pure Tailwind
A single-select icon toggle group — left, centre, right, justify — on hidden radios, so the arrow keys, form submission and screen-reader semantics are all native. The pressed state uses an inset shadow plus a gold indicator bar (has-checked:after:…), and a sample paragraph re-aligns itself live through group-has-[#tbg09-2:checked]/card:text-center.

10. Tailwind Web3 Connect Wallet Button Grid (Wallet Sign-In Modal) (opens in a new tab)

Published

Pure Tailwind
A dark, glassy wallet picker: a responsive grid-cols-[repeat(auto-fit,minmax(9.5rem,1fr))] of wallet tiles, the recommended one wrapped in a true gradient border ([background:…_padding-box,…_border-box]) with a floating badge, and a hover that lifts the tile and reveals a connect arrow. A close button and a reassuring footer make it a complete modal.

11. Tailwind AI Model Selector Button Group (LLM Model Picker with Badges) (opens in a new tab)

Published

Pure Tailwind
A model picker for AI products: radio cards with a provider badge, capability chips, context window and an animated check. Each row sets one inline variable, --brand, and v4's variable shorthand does the rest — bg-(--brand) on the badge, has-checked:border-(--brand) on the row, and a color-mix() tint behind the selection — so every provider is selected in its own colour.

12. Tailwind Liquid Metal Segmented Control (Animated Chrome Thumb) (opens in a new tab)

Published

Pure Tailwind
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.

13. Tailwind Tab Button Group with Subgrid Underline Indicator (opens in a new tab)

Published

Pure Tailwind
Dashboard tabs with a glowing underline that always matches each tab's exact width — because the underline row shares the tabs' columns through grid-cols-subgrid. The CSS original moved one beam between columns, which can't animate; this port gives every column its own beam segment, so the old one shrinks out while the new one grows in — a smooth hand-off, still with zero JavaScript.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

16. Tailwind Radio Button Group with Conic Fill Animation (Export Format Picker) (opens in a new tab)

Published

Pure Tailwind
Four format tiles — PNG, JPG, WebP, AVIF — where the chosen one fills with its own colour in a clockwise radial sweep. An @property-registered angle drives conic-gradient(from -90deg, var(--c) var(--tbg16-a), transparent 0) on a before: layer, and each tile's colour is one inline variable read by has-checked:border-(--c). Every tile now explains what the format is for.

17. Tailwind Retro LED Pixel Button Group (Dot-Matrix Display Buttons) (opens in a new tab)

Published

Pure Tailwind
A hardware-style transport panel: three keys drawn as dot-matrix LED displays that light up green, amber or red when selected. The dot grid is a tiled radial-gradient on before: (before:bg-size-[7px_7px]), scanlines sit on after:, and each key swaps one variable — has-checked:[--led:#3dff6e] — to re-colour dots, text and glow at once. Labels are set in Doto, a true dot-matrix face, and a status line reports the current mode.

18. Tailwind Hold-to-Confirm Button Group with Gradient Progress (Press and Hold) (opens in a new tab)

Published

Tailwind + JS
A publish bar where the primary action is press-and-hold: holding fills the button left-to-right (an @property width animated on active:after:), letting go early cancels, and completing it flips the button to a green Published state via data-done:. The CSS original drew the fill but nothing ever happened; a 20-line listener on animationend now makes it a real confirmation pattern.

20. Tailwind 3D Vertical Button Group with Hover Depth (Perspective Stack) (opens in a new tab)

Published

Pure Tailwind
A vertical navigation stack tilted into 3D, where the hovered (or keyboard-focused) card lifts toward you while its siblings sink back and dim. It's a showcase for v4's native 3D utilities — perspective-[760px], transform-3d, rotate-x-34, translate-z-[70px] — combined with group-has-[button:hover]/stack:not-hover:, which targets “every card except the one under the pointer” in one variant.

21. Tailwind Floating Action Button with Radial Speed Dial Menu (No JS) (opens in a new tab)

Published

Pure Tailwind
A floating action button that fans four actions out along an arc. A hidden checkbox holds open state; each satellite reads its own offset from inline --x/--y through v4's translate-x-(--x) shorthand, with staggered delay-* for a springy cascade. Closed satellites are now invisible, so they leave the tab order — the CSS original let keyboard users tab into four hidden buttons.

22. Tailwind Glitch Effect Button Group (Cyberpunk RGB-Split Audio Filter) (opens in a new tab)

Published

Pure Tailwind
A cyberpunk audio-filter selector: picking a mode fires a 0.45s RGB-split glitch — cyan and magenta copies of the label (before:content-[attr(data-txt)], after:…) jitter apart while the button slices with stepped clip-path keyframes — then settles into a steady neon state. A live equaliser above the buttons reshapes itself for each filter, so the choice has a visible meaning.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

23. Tailwind Animated Gradient Border Pill Buttons (Rotating Conic Ring) (opens in a new tab)

Published

Pure Tailwind
Plan-selector pills ringed by a rotating conic-gradient border — slow violet for idle pills, a fast solar ring plus blurred halo for the chosen one. The border is the padding-box/border-box trick in a single arbitrary [background:…], spun by an @property angle. The CSS original's halo used z-index:-1 and vanished behind the page background; an isolate wrapper fixes it.

24. Tailwind Button Group with Ripple Click Effect (Radar Shockwave Feedback) (opens in a new tab)

Published

Tailwind + JS
A reactor-control action row with satisfying click feedback: a radar shockwave expands from exactly where you clicked, the primary action breathes with an @property-animated glow, and a console line logs each command. The CSS original only rippled from the centre while the mouse was held (quick clicks barely showed); a tiny script now sets --x/--y and re-arms data-fire:after:animate-tbg24-wave on every press.

25. Tailwind Button Group with Cursor-Following Spotlight Glow (Date Range Presets) (opens in a new tab)

Published

Tailwind + JS
Date-range preset buttons lit by a warm spotlight that follows the cursor across the whole group — one glow spanning every button, because each reads the same pointer coordinates from --mx/--my. Selection is pure radios; the script only writes two variables. Below the presets, the resolved range (Oct 2 – Oct 8, 2026) updates as you choose, so users see exactly which dates they're getting.
FAQ

Frequently asked questions

How does the iOS-style segmented control slide its thumb between options without any JavaScript?
The thumb is one absolutely-positioned element sitting behind a row of visually-hidden radio inputs. Each visible segment is a <label> tied to a radio with for, so clicking a label checks its radio. A :has() rule on the container reads which radio is checked and sets the thumb's translate to that segment's offset — .group:has(input:nth-of-type(2):checked) .thumb{translate:100% 0}. Because the segments are equal width, each step is a clean percentage, and the spring comes from a transition with a slight-overshoot easing. The radios stay in the DOM (clipped with sr-only, never display:none) so they keep focus and arrow-key behaviour. The catch: if your segments are not equal width, a fixed percentage translate lands off-target — switch to the Anchor Positioning approach the pill-filter demo uses.
Why does my sliding highlight land in the wrong place when the buttons have different widths?
Equal-width segments let you move the indicator by a fixed percentage, but filter pills sized to their text — All, Electronics, Home & Garden — break that math, because a 25% step assumes four identical columns you no longer have. The pill-filter demo solves it with CSS Anchor Positioning: give the checked label an anchor-name via :has(), then position the single highlight against it with left:anchor(left) and width:anchor(size width). The indicator now measures the real checked button at whatever width it is, and the browser reflows it on resize for free. Without anchor support you would otherwise read offsetLeft and offsetWidth in JavaScript and write them to custom properties — so treat the anchor version as the enhancement and a measured-JS path as the fallback for older engines.
How do I keep an icon-only button group accessible when there's no visible text label?
Two rules the icon toolbar and alignment-toggle demos both follow. First, every icon button carries an aria-label ("Bold", "Align left"), because the glyph alone means nothing to a screen reader, and the decorative <svg> inside gets aria-hidden. Second, pick the control that matches the behaviour: a single-select group like text alignment or the view switcher is built on radio inputs, so the arrow keys move between options and exactly one stays checked — native radio semantics, no custom key handling. A multi-toggle such as the formatting toolbar uses real <button> elements with aria-pressed flipped per toggle. The CSS-only tooltips use after:content-[attr(data-tip)], but a tooltip is not a label substitute — a tooltip only appears on hover, so keep the aria-label so the control stays named for keyboard and screen-reader users who never hover.
How does the pricing toggle switch every plan's price between monthly and yearly with no script?
The monthly/yearly switch is two radio inputs above the pricing section. Each plan card renders both prices stacked, and a container-level :has() rule decides which shows: .pricing:has(#yearly:checked) .price-monthly{display:none}, with the inverse for the yearly figure. The sliding pill on the switch itself is the same translate-on-checked trick as the segmented control. Because state lives in a checked radio, it survives a form submit and needs no hydration. The common mistake is hiding the inputs with display:none — that removes them from the tab order and breaks keyboard use; clip them with sr-only instead so they stay operable. Also reserve equal height for both price nodes, or the card jumps when you switch — the two figures rarely render at the same height on their own, and an unreserved swap causes a visible layout shift.
How do the split-button dropdowns open their menu, and why won't mine close when I click away?
There are two no-JS bases here. The save-options demo wraps the caret menu in <details>/<summary>: open state is the element's own open attribute and the menu is styled off details[open]. The anchor-positioned demo uses the native popover attribute with a popovertarget trigger, tethered to the button with anchor-name. Your "won't close on outside click" bug is specific to <details> — it has no light-dismiss, so clicking elsewhere leaves it open. The popover API gives you light-dismiss and Escape-to-close for free, which is exactly why it is the better base for a real menu. If you must stay on <details>, you add a small outside-click handler — but that leaves the no-JS path, so prefer the popover version wherever its browser support reaches your users.
When should I use a segmented control versus a dropdown or a radio-card grid?
A segmented control — the iOS switch, alignment toggle, view switcher — earns its horizontal space only for two to five mutually-exclusive options you want visible at once, like billing period or grid-vs-list. Past five it overflows on narrow screens; beyond that, use a <select> or the popover dropdown, which collapse to one line. The AI-model picker and wallet grid are deliberately not segmented: each option carries a badge, a context-window figure or a glassy card, so they use a radio-card grid that wraps with grid-cols-[repeat(auto-fit,minmax(9.5rem,1fr))]. Rule of thumb: if an option needs more than a short label, it is a radio-card grid, not a segmented control. And if the thing is a command rather than a persistent choice — Save, Deploy — reach for a split button; toggle groups represent state, buttons represent actions, and mixing the two confuses users.
How does the View Transitions morphing highlight work, and what happens where it isn't supported?
The density switcher gives its moving highlight a view-transition-name. When the checked option changes, running that state change inside a view transition makes the browser snapshot the highlight's old and new positions and tween between them, so the pill appears to morph from one button to the next rather than teleport. Since state is driven by radios, the :has() rule that repositions the highlight is the real DOM change the transition captures. The design rule is progressive enhancement: in an engine without the View Transitions API the highlight still moves — it just uses the plain CSS transition instead of the cross-element morph, so nothing is gated behind the API and nothing breaks. Honour prefers-reduced-motion:reduce as well; the comet, liquid-metal and glitch demos cut their motion under that query, and the morph should drop to an instant swap too.
The glow, ripple and spotlight demos react to the cursor — how is that built, and does it need JavaScript?
They split by what they track. The cursor-following spotlight updates two custom properties — --x and --y — from a pointermove listener on the group, and a radial-gradient centred on those variables paints the glow; that needs a few lines of JS because CSS cannot read the pointer position. The ripple shockwave reads the exact click coordinates the same way, then plays a scale-and-fade keyframe from that point. By contrast the conic-border pills, the glitch RGB-split and the liquid-metal thumb are pure CSS — they animate on :hover, :checked and an @property-registered angle, with no script at all. All of them must respect prefers-reduced-motion:reduce, and the pointer-driven ones should no-op on touch, where there is no hover — fall back to the static gradient so a tap still reads as a normal, legible button.

Related collections

15 CSS 3D Tilt Hover Cards preview

15 CSS 3D Tilt Hover Cards

15 mouse-aware 3D tilt hover cards — e-commerce product spotlight, glassmorphism parallax team card, interactive pricing tier, holographic NFT collectible, pop-out mascot, dark tech grid with border glow, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and a Pure CSS touch-friendly tilt. Vanilla JS writes --rx/--ry/--mx/--my; all rendering stays in CSS on the GPU.

24 CSS Animated Cards preview

24 CSS Animated Cards

24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

22 CSS Avatars preview

22 CSS Avatars

22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.

Search CodeFronts

Loading…