25 CSS Button Groups
25 / 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)
CSS + JSMIT licensed

CSS Date-Range Preset Button Group with Cursor-Following Glow (Spotlight Hover)

A date-range preset group — Today / 7d / 30d / Quarter / Year — lit by a solar-flare spotlight that tracks your cursor across the whole group, with the hovered preset flaring brightest and one preset selected. The polished 'CSS cursor-following glow button group' spotlight effect for analytics dashboards and reporting filters.

Live Demo
Plain CSSTailwind v4
Try it

Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects

ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link

10% off at checkout with code
Check ZTM Course

The code

<section class="btn-25" aria-label="Cursor-following glow date range presets demo">
  <div class="btn-25__wrap">
    <p class="btn-25__label">Date range</p>
    <div class="btn-25__group" id="btn-25-group" role="radiogroup" aria-label="Date range preset" style="--mx:50%;--my:50%">
      <input type="radio" name="btn-25" id="btn-25-1" class="btn-25__in">
      <label for="btn-25-1" class="btn-25__btn">Today</label>
      <input type="radio" name="btn-25" id="btn-25-2" class="btn-25__in" checked>
      <label for="btn-25-2" class="btn-25__btn">Last 7 days</label>
      <input type="radio" name="btn-25" id="btn-25-3" class="btn-25__in">
      <label for="btn-25-3" class="btn-25__btn">Last 30 days</label>
      <input type="radio" name="btn-25" id="btn-25-4" class="btn-25__in">
      <label for="btn-25-4" class="btn-25__btn">Quarter</label>
      <input type="radio" name="btn-25" id="btn-25-5" class="btn-25__in">
      <label for="btn-25-5" class="btn-25__btn">Year</label>
    </div>
  </div>
</section>
.btn-25,
.btn-25 *,
.btn-25 *::before,
.btn-25 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-25 {
  --accent: oklch(0.78 0.16 65);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(130% 120% at 50% 0%,#1e130a,#0b0705);
  padding: 44px 20px;
}

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

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

.btn-25__group {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.btn-25__in {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.btn-25__btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  padding: 0 22px;
  font-size: 15px;
  font-weight: 700;
  color: #c8b49a;
  background: #1b120b;
  border: 1.5px solid #362619;
  border-radius: 13px;
  cursor: pointer;
  transition: color .2s,border-color .2s;
}

.btn-25__btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(220px circle at var(--mx) var(--my),color-mix(in oklab,var(--accent) 55%,transparent),transparent 60%);
  opacity: 0;
  transition: opacity .3s;
}

.btn-25__group:hover .btn-25__btn::before {
  opacity: 1;
}

.btn-25__btn:hover {
  color: #fff2e2;
  border-color: var(--accent);
}

.btn-25__in:checked + .btn-25__btn {
  color: #1a0f06;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 8px 24px -10px var(--accent);
}

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

@media (prefers-reduced-motion: reduce) {
  .btn-25__btn,
    .btn-25__btn::before {
    transition: none;
  }
}
/* One small enhancement: track the pointer and write --mx/--my so a single solar-flare spotlight glides across the whole group. Selection stays pure-CSS via radios, so it works without JS (falling back to a centered hover glow). */
(function(){
  var g = document.getElementById('btn-25-group');
  if(!g) return;
  function move(e){
    var r = g.getBoundingClientRect();
    var btns = g.querySelectorAll('.btn-25__btn');
    btns.forEach(function(b){
      var br = b.getBoundingClientRect();
      b.style.setProperty('--mx', (e.clientX - br.left) + 'px');
      b.style.setProperty('--my', (e.clientY - br.top) + 'px');
    });
  }
  g.addEventListener('pointermove', move, {passive:true});
})();
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 Date-Range Preset Button Group with Cursor-Following Glow (Spotlight Hover)
Source: https://codefronts.com/components/css-button-groups/solar-flare/

A date-range preset group — Today / 7d / 30d / Quarter / Year — lit by a solar-flare spotlight that tracks your cursor across the whole group, with the hovered preset flaring brightest and one preset selected. The polished 'CSS cursor-following glow button group' spotlight effect for analytics dashboards and reporting filters.
## HTML
```html
<section class="btn-25" aria-label="Cursor-following glow date range presets demo">
  <div class="btn-25__wrap">
    <p class="btn-25__label">Date range</p>
    <div class="btn-25__group" id="btn-25-group" role="radiogroup" aria-label="Date range preset" style="--mx:50%;--my:50%">
      <input type="radio" name="btn-25" id="btn-25-1" class="btn-25__in">
      <label for="btn-25-1" class="btn-25__btn">Today</label>
      <input type="radio" name="btn-25" id="btn-25-2" class="btn-25__in" checked>
      <label for="btn-25-2" class="btn-25__btn">Last 7 days</label>
      <input type="radio" name="btn-25" id="btn-25-3" class="btn-25__in">
      <label for="btn-25-3" class="btn-25__btn">Last 30 days</label>
      <input type="radio" name="btn-25" id="btn-25-4" class="btn-25__in">
      <label for="btn-25-4" class="btn-25__btn">Quarter</label>
      <input type="radio" name="btn-25" id="btn-25-5" class="btn-25__in">
      <label for="btn-25-5" class="btn-25__btn">Year</label>
    </div>
  </div>
</section>
```
## CSS
```css
.btn-25,
.btn-25 *,
.btn-25 *::before,
.btn-25 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.btn-25 {
  --accent: oklch(0.78 0.16 65);
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(130% 120% at 50% 0%,#1e130a,#0b0705);
  padding: 44px 20px;
}

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

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

.btn-25__group {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.btn-25__in {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.btn-25__btn {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 50px;
  padding: 0 22px;
  font-size: 15px;
  font-weight: 700;
  color: #c8b49a;
  background: #1b120b;
  border: 1.5px solid #362619;
  border-radius: 13px;
  cursor: pointer;
  transition: color .2s,border-color .2s;
}

.btn-25__btn::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(220px circle at var(--mx) var(--my),color-mix(in oklab,var(--accent) 55%,transparent),transparent 60%);
  opacity: 0;
  transition: opacity .3s;
}

.btn-25__group:hover .btn-25__btn::before {
  opacity: 1;
}

.btn-25__btn:hover {
  color: #fff2e2;
  border-color: var(--accent);
}

.btn-25__in:checked + .btn-25__btn {
  color: #1a0f06;
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 8px 24px -10px var(--accent);
}

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

@media (prefers-reduced-motion: reduce) {
  .btn-25__btn,
    .btn-25__btn::before {
    transition: none;
  }
}
```

## JavaScript
```js
/* One small enhancement: track the pointer and write --mx/--my so a single solar-flare spotlight glides across the whole group. Selection stays pure-CSS via radios, so it works without JS (falling back to a centered hover glow). */
(function(){
  var g = document.getElementById('btn-25-group');
  if(!g) return;
  function move(e){
    var r = g.getBoundingClientRect();
    var btns = g.querySelectorAll('.btn-25__btn');
    btns.forEach(function(b){
      var br = b.getBoundingClientRect();
      b.style.setProperty('--mx', (e.clientX - br.left) + 'px');
      b.style.setProperty('--my', (e.clientY - br.top) + 'px');
    });
  }
  g.addEventListener('pointermove', move, {passive:true});
})();
```

How this works

A pointer listener writes the cursor's position into two CSS variables (--mx/--my) on the group. Each button paints a radial-gradient centered on those coordinates, so a single spotlight appears to glide continuously across all buttons — a group-wide flare rather than per-button hovers. This is the one demo that needs a few lines of JS, because CSS alone can't read pointer coordinates.

Selection stays pure CSS: real radios drive a persistent "active" preset with a warm border and fill, independent of the roaming glow. The spotlight uses color-mix for a warm solar tint and fades out when the pointer leaves. Everything degrades to a normal hover glow if JS is off.

el.addEventListener('pointermove', e => {
  const r = group.getBoundingClientRect();
  group.style.setProperty('--mx', (e.clientX-r.left)+'px');
  group.style.setProperty('--my', (e.clientY-r.top)+'px'); });

Techniques used: pointer-driven --mx/--my CSS variables · group-wide radial-gradient spotlight · color-mix solar tint · radio-driven persistent selection (independent of glow) · graceful no-JS hover fallback · reduced-motion safe

Make it yours

  • Flare hue/size = the radial-gradient color and radius.
  • --accent is the selected-preset color.
  • Spotlight softness = the gradient stop positions.
  • Add presets freely; the shared spotlight covers them all.

Gotchas — read before shipping

  • This effect needs the small pointermove listener — scope it to the group and use pointermove (covers mouse + pen); keep it passive for scroll perf.
  • Provide a sensible default --mx/--my and a plain :hover glow so it works before/without JS.
  • Selection must not depend on the glow — keep the radios' active state clearly separate for keyboard users.
  • Throttle nothing heavy in the handler; setting two custom properties is cheap, but avoid layout reads beyond one getBoundingClientRect.

Browser support

ChromeSafariFirefoxEdge
111+ (color-mix)16.2+113+111+ (color-mix)

color-mix() Baseline 2023; custom properties + pointer events are universal. The spotlight needs JS; without it a static :hover glow remains.

Techniques used in this demo

Search CodeFronts

Loading…