25 CSS Button Groups
25 CSS button groups for SaaS pricing toggles, app toolbars and icon actions, Web3 wallet-connect and AI model pickers, filter chips, split buttons, and date-range presets — with iOS-style segmented controls, sliding and morphing thumbs, floating action fans, and 3D hover stacks. Scoped under .btn-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.
Related51 CSS Buttons24 CSS Radio Buttons32 CSS Tabs20 CSS SVG Stroke Buttons51 Tailwind CSS Buttons25 Tailwind CSS Button Groups
Course pickPartner: Zero to Mastery
Affiliate linkWant to build production-ready CSS layouts, animations and UI from scratch?
You just browsed 25 button groups. Learn the Flexbox, Grid and animation thinking behind them, so the next component starts in your head, not in a copy-paste.
Every ZTM course in one membership: $25/month, paid yearly at $299 · 30‑day money-back guarantee
The CSS Bootcamp. We may earn a commission, at no extra cost to you.
02. CSS Segmented Control with Motion-Path Comet Thumb (offset-path Arc Travel) (opens in a new tab)
03. CSS View Transitions Toggle Button Group with Morphing Thumb (Shared-Element FLIP) (opens in a new tab)
04. CSS Pricing Toggle with Monthly / Yearly Switch (Pure CSS, No JavaScript) (opens in a new tab)
05. CSS Segmented Control with Sliding Thumb Indicator (iOS-Style, Pure CSS) (opens in a new tab)
06. CSS Split Button Dropdown with Native <details> (Primary Action + Menu) (opens in a new tab)
<details>/<summary> so it opens with zero JavaScript, closes on outside interaction, and keeps full keyboard and screen-reader disclosure semantics. The definitive 'CSS split button dropdown' recipe.07. CSS Icon Button Group for Toolbar Actions (Accessible Tooltips, Pure CSS) (opens in a new tab)
08. CSS Pill Radio Button Group with Sliding Indicator (Filter Pills, Pure CSS) (opens in a new tab)
09. CSS Toggle Button Group with Radio Inputs (Single-Select Alignment Group) (opens in a new tab)
10. CSS Wallet Connect Button Grid for Web3 Sign-In (MetaMask, WalletConnect, Coinbase) (opens in a new tab)
11. CSS AI Model Selector Button Group with Provider Badges (LLM Model Picker) (opens in a new tab)
12. CSS Segmented Control with Liquid Mercury Thumb (Metallic Stretch Animation) (opens in a new tab)
13. CSS Button Group with Subgrid Underline Beam (Perfectly Aligned Tab Indicator) (opens in a new tab)
14. CSS Multi-Select Chip Buttons with Acid Glow (Toggleable Tag Filters) (opens in a new tab)
15. CSS Split Button with Quantum Chevron Reveal (Animated Menu Materialize) (opens in a new tab)
<details>, so the drama is pure CSS and the semantics stay honest.16. CSS Format Toggle Button Group with Conic Fill (Radial Sweep Select) (opens in a new tab)
17. CSS Pixel Matrix Button Group with LED Segment Glow (Retro Display Buttons) (opens in a new tab)
18. CSS Multi-Action Button Group with Gradient Flow Progress (Animated CTA Bar) (opens in a new tab)
19. CSS Icon Segmented Control with Holographic Sheen (Iridescent View Switcher) (opens in a new tab)
20. CSS Vertical Button Stack with 3D Hover Depth (Tilting Card Buttons) (opens in a new tab)
21. CSS Floating Action Button with Radial Sub-Button Menu (Arc Fan-Out, Pure CSS) (opens in a new tab)
22. CSS Filter Button Group with RGB-Split Glitch Active State (Cyberpunk Toggle) (opens in a new tab)
23. CSS Pill Button Group with Rotating Conic Plasma Border (Animated Gradient Ring) (opens in a new tab)
24. CSS Action Button Row with Radar Shockwave Click Feedback (Ripple Pulse) (opens in a new tab)
25. CSS Date-Range Preset Button Group with Cursor-Following Glow (Spotlight Hover) (opens in a new tab)
Build your own
Tweak the exact look in our visual generators — no signup, instant copy-paste.
Frequently asked questions
What is a CSS button group and when should you use one instead of separate buttons?
How do these 25 button groups map to real-world use cases and industry verticals?
How do these compare to Radix UI, shadcn/ui, Headless UI, Aceternity, and Tailwind — why not just use one of those?
display:grid + grid-template-columns:repeat(3,1fr) maps to grid grid-cols-3; border-radius:999px maps to rounded-full; backdrop-filter:blur() maps to backdrop-blur. Named @keyframes and @property registrations belong in tailwind.config.js under extend.keyframes + extend.animation, or a small @layer utilities block.How do you build a CSS split button dropdown without JavaScript — using the Popover API or details/summary?
<details> / <summary> (Demo 06): the primary action is a <button>; the dropdown trigger is a <summary> inside a <details> element. When the user clicks the summary, the browser toggles the details open state natively — zero JavaScript for open/close mechanics. Screen readers announce it as a disclosure widget (semantic and correct). The menu itself is styled inside the details element, revealed on [open] selector. Works in every browser since 2020. This is the simplest, most-supported production pattern. Approach 2 — the modern Popover API (Demo 01): the primary action is a <button>; the dropdown trigger is a <button popovertarget="menu-01">. The menu is a <div id="menu-01" popover>. The browser handles show / hide / dismiss on Escape / dismiss on backdrop click natively. Anchor Positioning API (anchor-name + position-anchor + anchor()) positions the menu relative to the trigger with collision-aware behaviour, eliminating the JS floating-ui library entirely. Baseline support in Chrome 114+, Safari 17+, Firefox 125+. For older browsers, a 15-line polyfill or a @supports fallback to Approach 1 keeps it working. Both approaches ship real accessibility: aria-haspopup="menu" on the trigger, aria-expanded flipping true / false, focus management moves into the menu on open and back to the trigger on close, arrow keys navigate menu items, Enter / Space activate. This collection's Demo 01 and Demo 06 ship both patterns so teams can pick based on browser support requirements — Popover + Anchor for cutting-edge products, native <details> for maximum compatibility. What NOT to do: never use a plain <div onclick> with a JS-toggled class. That fails keyboard access, fails screen reader announcement, and requires an outside-click handler that competes with every other event listener on the page. The native primitives handle it all correctly.How does the sliding thumb indicator work in a segmented control — with pure CSS and no JavaScript?
calc(100% / N) where N is the number of options. When a radio is :checked, a general-sibling selector on the parent sets the thumb's translateX to move it to the correct position (0, 100%, 200%, 300%, ...). Transition on transform only — compositor-friendly, GPU-accelerated, no layout thrash. This is the pattern iOS 26 ships in UISegmentedControl, and it's what Stripe, Linear, and every modern SaaS product uses for their segmented pickers. Zero JavaScript. Technique 2 — motion-path comet thumb (Demo 02, the offset-path arc travel): the thumb rides a curved offset-path: path('M ...') as it moves between options, tracing a subtle arc rather than a straight line. The path is a Bézier curve; offset-distance tweens from 0% to 100% based on which radio is checked. This is a modern polish move that reads as premium (the eye tracks curved motion as more organic than linear). Technique 3 — View Transitions with morphing thumb (Demo 03, shared-element FLIP): uses the CSS View Transitions API (view-transition-name) so the thumb is treated as the same DOM element across states. When the selection changes, the browser natively animates the thumb from its old position to its new one — no manual FLIP math, no requestAnimationFrame loop, no imperative animation code. Baseline support arrived in 2024 (Chrome 111+, Safari 18+). For older browsers, degrades gracefully to Technique 1's transform-tween pattern. Common pitfall: never animate left or margin-left or width to move the thumb — those trigger the layout + paint pipeline on every frame and tank Interaction to Next Paint on real mobile hardware. Always animate transform: translateX() so the animation runs on the compositor. Every demo in this collection is INP-safe.How do you make CSS button groups accessible for keyboard, screen readers, and touch — WCAG 2.2 AA compliance?
<button>, <input type="radio">, <input type="checkbox">, or <summary>. Never a <div onclick>. Real elements come with real keyboard support, real screen reader announcements, real focus management, real form participation — none of which you have to write yourself. ARIA where semantic HTML falls short — aria-pressed on toggle buttons (multi-select toolbar buttons), aria-current="page" or aria-current="true" on active items in single-select navigation groups, aria-expanded on split-button and dropdown triggers, aria-haspopup="menu" on split-button dropdowns, aria-label on every icon-only button so screen readers announce the action, role="group" with aria-label on the button group container. Keyboard navigation matches the widget type — radio groups use arrow keys to move between options (roving tabindex handled natively by the radio input semantics), toolbars use arrow keys with Home / End to jump to first / last, split buttons open menus on Enter / Space / Down Arrow. Tab enters the widget, tabs out to the next widget — it doesn't tab through every button inside a group (that would create tab-traps on toolbar-heavy screens). Focus indication survives every design — a :focus-visible ring is applied to every interactive element, using a high-contrast double outline (dark + light) so it reads against any button background. Rings never disappear inside a filled active state. In @media (forced-colors: active), rings switch to outline: 2px solid Highlight so Windows High Contrast mode shows the focus indicator using system palette. Touch targets — every button meets WCAG 2.5.5 44×44px minimum. Icon buttons that look smaller ship padding that expands the hit area to the required size. Motion respects prefers-reduced-motion — every animation is guarded. Transparency respects prefers-reduced-transparency where glass effects are used. Color contrast — every active state clears WCAG 1.4.3 AA (4.5:1 for normal text, 3:1 for large-text and non-text UI). The compliance floor is the same one healthcare / government / regulated fintech / insurance / legal products need. Ship these to a Section 508 audit and pass.How does the multi-select filter chip / toggle button group work — for ecommerce filters, NFT marketplaces, or editor toolbars?
<label> wrapping a hidden <input type="checkbox"> plus a visible span. When the checkbox is :checked, a general-sibling selector styles the label with a glow ring, brighter fill, and a checkmark. Multiple chips can be checked simultaneously. Form submission works natively (browsers include every checked checkbox in the form data). Zero JavaScript required. Screen readers announce each chip as “checkbox, checked / unchecked”. This is the pattern OpenSea uses for NFT trait filters, Amazon uses for product category filters, and Shopify uses for merchant-facing product tag pickers. Pattern 2 — <button aria-pressed> for stateful toolbar buttons (Demo 07, the icon toolbar): each toolbar button is a real <button type="button"> with aria-pressed="true" or aria-pressed="false". The pressed state is styled via [aria-pressed="true"] selector. Screen readers announce “toggle button, pressed / not pressed”. This is the pattern Google Docs, Notion, Linear, and every text-editor toolbar uses for the Bold / Italic / Underline pattern. Requires a small amount of JS to flip the aria-pressed attribute on click (or a :has()-based state machine for pure CSS). Both patterns support keyboard navigation, focus management, and screen reader announcements out of the box. Common failure mode: never use a <div class="active"> pattern for multi-select. That loses the semantic role, breaks form submission, and requires you to write the accessibility layer yourself. The native primitives handle everything correctly. Style at your leisure; leave the semantics alone.How do you build a Web3 wallet-connect button grid or an AI model selector — high-CPM patterns for crypto / LLM products?
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)) — so it reflows from a 5-across grid on desktop to a 2-across grid on mobile without duplicate breakpoint markup. Each provider tile is a <button> with the wallet logo (inline SVG for performance), the provider name, and a chevron or status indicator. Hover / focus states reveal a subtle glow. The active state carries aria-pressed="true". Real Web3 products then wire the click handler to window.ethereum.request({ method: 'eth_requestAccounts' }) or the equivalent WalletConnect / Coinbase SDK call. The demo ships the UI layer; the SDK integration is one line of JavaScript in your app. AI model selector (Demo 11): the pattern every LLM playground and AI product ships to let users switch between GPT-4, Claude 4.5 Sonnet, Gemini 2.0 Pro, Mistral Large, Llama 3.3, o1, etc. OpenAI Playground, Anthropic Console, Perplexity, Cursor's model picker, GitHub Copilot's model dropdown, and every AI product's model-selection UI ship variants of this. The demo ships as a segmented button group with provider badges (small pill next to the model name showing the provider), context-window indicator (128K, 200K, 1M), pricing tier badge (Free / Pro / Team). Hidden radios drive single-select state; the visible label styling is pure CSS. Real AI products then wire the click handler to their API's model parameter. Both demos are copy-paste-ready UI layers — you ship them with your Web3 SDK or LLM API integration. The accessibility floor is the same as every other button group in the collection: real interactive elements, ARIA announcements, keyboard navigation, focus indication, 44px touch targets, prefers-reduced-motion guards. This collection is the first free CSS button-group library to specifically address the Web3 wallet-connect and AI model selector verticals — both are $50-150 CPM ad tiers, so the traffic these patterns attract is disproportionately valuable.Are these button groups free to use commercially, and do they work in React, Vue, Svelte, Next.js, Astro, and SvelteKit?
className instead of class), a Vue single-file component's template, a Svelte component, an Astro island, or a SvelteKit page — the CSS ships as a scoped module, an inline <style> block, or a Tailwind arbitrary-value paste. React — use className, wrap the state in useState if you need it, but the CSS-driven state machines (hidden radios, :has(), native <details>) work without React state. React StrictMode double-mount is safe. Server-side rendering works without hydration flash because the initial state is CSS-driven, not JS-driven. Vue 3 — use v-model to bind the selected value to the hidden radio inputs. Svelte / SvelteKit — bind:group works with the radio pattern out of the box. Astro islands — wrap as client:load or client:visible only if the demo actually needs client-side state (most don't; they render on the server and the CSS handles interaction). Angular, Blazor, HTMX, Turbo, Rails ERB, Django, Laravel Blade, WordPress themes, and static HTML all work — the code is framework-agnostic. Content-Security-Policy compatibility: every demo works under a strict CSP (script-src 'self' without 'unsafe-inline') because no demo uses inline onclick handlers; interactions come from CSS selectors on hidden form inputs. Tailwind users: the CSS translates cleanly. display:grid + grid-template-columns:repeat(auto-fit,minmax(160px,1fr)) maps to grid grid-cols-[repeat(auto-fit,minmax(160px,1fr))]. border-radius:999px maps to rounded-full. backdrop-filter:blur() maps to backdrop-blur. @property registrations and named @keyframes belong in tailwind.config.js under extend.keyframes + extend.animation, or a small @layer utilities block. Every demo has been mentally tested at 320px / 600px / 1200px viewport widths per this site's responsive-first contract — the code you copy is the code that ships to your users at every width.Related collections
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
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
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.



















