36 Tailwind CSS Button Hover Effects
32 / 36

All 36 in 36 Tailwind CSS Button Hover Effects

  1. 01Tailwind Magnetic Ripple Attraction Button Hover Effect
  2. 02Tailwind Cyberpunk Holographic Glitch Grid Button
  3. 03Tailwind Frosted Glassmorphism Shimmer Glow Button
  4. 04Tailwind Organic Blob Morph Button Hover Effect
  5. 05Tailwind Dual-Line Intersection Border Draw Button
  6. 06Tailwind Isometric Tactile 3D Depress Button
  7. 07Tailwind Infinite Sliding Kinetic Marquee Button
  8. 08Tailwind Chroma Conic Gradient Border Spin Button
  9. 09Tailwind Minimalist Border & Outline Drawing Hover Effects
  10. 10Tailwind Modern 3D Skeuomorphic Button Press Effects
  11. 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
  12. 12Tailwind Sliding Background Color Wipe Button Effects
  13. 13Tailwind Liquid Fill & Organic Morphing Button Effects
  14. 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
  15. 15Tailwind Liquid Wave Fill Button Hover Effect
  16. 16Tailwind Magnetic Border Drift Button
  17. 17Tailwind Glitch Slice Button Hover Effect
  18. 18Tailwind Neon Pulse Glow Button Hover Effect
  19. 19Tailwind Split Reveal Curtain Button Effect
  20. 20Tailwind Diagonal Shutter Sweep Button Effect
  21. 21Tailwind Elastic Lift Spring Button Hover Effect
  22. 22Tailwind Center Underline Draw Link Button Effects
  23. 23Tailwind 3D Flip Card Button Hover Effect
  24. 24Tailwind Particle Burst Confetti Button Effect
  25. 25Tailwind Slide Doors Closing Panel Button Effect
  26. 26Tailwind Typewriter Retype Terminal Button Effect
  27. 27Tailwind Hard Shadow Shift Flat Button Effect
  28. 28Tailwind Material Ripple Wave Button Effect
  29. 29Tailwind Rainbow Gradient Border Spin Button
  30. 30Tailwind Morph Shape Silhouette Button Effects
  31. 31Tailwind Icon Slide In Directional Button Effect
  32. 32Tailwind Cursor Spotlight Glow Button
  33. 33Tailwind Stagger Fill Equaliser Button Effect
  34. 34Tailwind Sunburst Expand Radiant Button Effect
  35. 35Tailwind 3D Press Plunger Key Button Effect
  36. 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Tailwind + JSMIT licensed

Tailwind Cursor Spotlight Glow Button

A sophisticated dark CTA with a soft circle of light that follows the cursor across its surface — the button appears lit by a moving spotlight. Design B applies the same tracked gradient to the BORDER via a mask-composite ring, which is the part worth stealing.

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

<div class="tbh-32-group w-full min-h-svh flex flex-wrap">
<section class="tbh-32a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh32-sans bg-tbh32a-stage" aria-label="Cursor spotlight button">
  <button id="tbh-32a-btn" type="button" class="relative isolate overflow-hidden cursor-pointer rounded-xl border border-white/15 px-[42px] py-[18px] text-base font-bold text-tbh32a-ink bg-tbh32a-base transition-colors duration-300 hover:border-white/30 focus-visible:border-white/30 focus-visible:outline-3 focus-visible:outline-tbh32a-light focus-visible:outline-offset-4 before:content-[''] before:absolute before:inset-0 before:z-0 before:opacity-[var(--lit,0)] before:bg-[radial-gradient(160px_160px_at_var(--mx,50%)_var(--my,50%),color-mix(in_oklab,var(--color-tbh32a-light)_55%,transparent),transparent_60%)] before:transition-opacity before:duration-300 motion-reduce:before:transition-none">
    <span class="relative z-[1]">Explore features</span>
  </button>
</section>
<section class="tbh-32b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh32-sans bg-tbh32b-stage" aria-label="Cursor glow border button">
  <button id="tbh-32b-btn" type="button" class="relative isolate cursor-pointer rounded-xl border border-white/12 px-[42px] py-[18px] text-base font-bold text-tbh32b-ink bg-tbh32b-base focus-visible:outline-3 focus-visible:outline-tbh32b-glow focus-visible:outline-offset-4 before:content-[''] before:absolute before:-inset-px before:z-0 before:rounded-[13px] before:p-px before:opacity-[var(--lit,0)] before:bg-[radial-gradient(120px_120px_at_var(--mx,50%)_var(--my,50%),var(--color-tbh32b-glow),transparent_70%)] before:[mask:linear-gradient(#000_0_0)_content-box,linear-gradient(#000_0_0)] before:[mask-composite:exclude] before:transition-opacity before:duration-300 motion-reduce:before:transition-none">
    <span class="relative z-[1]">View pricing</span>
  </button>
</section>
</div>
@import "tailwindcss";

@theme {
  --color-tbh32a-base: #14131c;
  --color-tbh32a-base: oklch(0.19 0.02 285);
  --color-tbh32a-stage: #08070d;
  --color-tbh32a-stage: oklch(0.11 0.01 285);
  --color-tbh32a-ink: #f4f3ff;
  --color-tbh32a-ink: oklch(0.96 0.01 285);
  --color-tbh32a-light: #8c78ff;
  --color-tbh32a-light: oklch(0.68 0.19 285);
  --color-tbh32b-base: #0f1a1c;
  --color-tbh32b-base: oklch(0.19 0.02 195);
  --color-tbh32b-stage: #060d0e;
  --color-tbh32b-stage: oklch(0.11 0.012 195);
  --color-tbh32b-ink: #eafffb;
  --color-tbh32b-ink: oklch(0.97 0.02 175);
  --color-tbh32b-glow: #5fdde8;
  --color-tbh32b-glow: oklch(0.75 0.15 190);
  --font-tbh32-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Stays CSS: the -webkit- mask pair for design B's border ring. The prefixed form needs the
   'xor' keyword where the standard form takes 'exclude', so it cannot be one utility. */

.tbh-32b button::before {
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
}

.tbh-32-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-32a,
.tbh-32b {
  min-height: 100svh;
}
(() => {
  const light = id => {
    const btn = document.querySelector(id); if (!btn) return;
    const set = (mx, my, lit) => {
      btn.style.setProperty('--mx', mx);
      btn.style.setProperty('--my', my);
      btn.style.setProperty('--lit', lit);
    };
    btn.addEventListener('pointermove', e => {
      const r = btn.getBoundingClientRect();
      set((e.clientX - r.left) + 'px', (e.clientY - r.top) + 'px', '1');
    });
    btn.addEventListener('pointerleave', () => btn.style.setProperty('--lit', '0'));
    btn.addEventListener('focus', () => set('50%', '50%', '1'));
    btn.addEventListener('blur', () => btn.style.setProperty('--lit', '0'));
  };
  light('#tbh-32a-btn');
  light('#tbh-32b-btn');
})();

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-tbh32a-base: #14131c;
  --color-tbh32a-base: oklch(0.19 0.02 285);
  --color-tbh32a-stage: #08070d;
  --color-tbh32a-stage: oklch(0.11 0.01 285);
  --color-tbh32a-ink: #f4f3ff;
  --color-tbh32a-ink: oklch(0.96 0.01 285);
  --color-tbh32a-light: #8c78ff;
  --color-tbh32a-light: oklch(0.68 0.19 285);
  --color-tbh32b-base: #0f1a1c;
  --color-tbh32b-base: oklch(0.19 0.02 195);
  --color-tbh32b-stage: #060d0e;
  --color-tbh32b-stage: oklch(0.11 0.012 195);
  --color-tbh32b-ink: #eafffb;
  --color-tbh32b-ink: oklch(0.97 0.02 175);
  --color-tbh32b-glow: #5fdde8;
  --color-tbh32b-glow: oklch(0.75 0.15 190);
  --font-tbh32-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Stays CSS: the -webkit- mask pair for design B's border ring. The prefixed form needs the
   'xor' keyword where the standard form takes 'exclude', so it cannot be one utility. */

.tbh-32b button::before {
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
}

.tbh-32-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-32a,
.tbh-32b {
  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=Space+Grotesk:wght@400;500;600;700&display=swap">

What changed in the port

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

  • before:opacity-[var(--lit,0)] is the port's discovery: an arbitrary utility value can be a bare var(), so a JS-written number drives the utility directly and the fade stays a CSS transition rather than becoming a second animation in script.
  • Both controllers collapsed into one function — the designs differ only in which gradient the markup declares, so there is nothing left for a second IIFE to say.
  • UI change from the CSS original: design A's border now brightens on focus-visible: as well as hover, and both buttons light from the centre on keyboard focus (the original wired focus but left design A's border change hover-only).
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 Cursor Spotlight Glow Button
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-cursor-spotlight-glow-button/

A sophisticated dark CTA with a soft circle of light that follows the cursor across its surface &mdash; the button appears lit by a moving spotlight. Design B applies the same tracked gradient to the BORDER via a mask-composite ring, which is the part worth stealing.
## HTML
```html
<div class="tbh-32-group w-full min-h-svh flex flex-wrap">
<section class="tbh-32a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh32-sans bg-tbh32a-stage" aria-label="Cursor spotlight button">
  <button id="tbh-32a-btn" type="button" class="relative isolate overflow-hidden cursor-pointer rounded-xl border border-white/15 px-[42px] py-[18px] text-base font-bold text-tbh32a-ink bg-tbh32a-base transition-colors duration-300 hover:border-white/30 focus-visible:border-white/30 focus-visible:outline-3 focus-visible:outline-tbh32a-light focus-visible:outline-offset-4 before:content-[''] before:absolute before:inset-0 before:z-0 before:opacity-[var(--lit,0)] before:bg-[radial-gradient(160px_160px_at_var(--mx,50%)_var(--my,50%),color-mix(in_oklab,var(--color-tbh32a-light)_55%,transparent),transparent_60%)] before:transition-opacity before:duration-300 motion-reduce:before:transition-none">
    <span class="relative z-[1]">Explore features</span>
  </button>
</section>
<section class="tbh-32b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh32-sans bg-tbh32b-stage" aria-label="Cursor glow border button">
  <button id="tbh-32b-btn" type="button" class="relative isolate cursor-pointer rounded-xl border border-white/12 px-[42px] py-[18px] text-base font-bold text-tbh32b-ink bg-tbh32b-base focus-visible:outline-3 focus-visible:outline-tbh32b-glow focus-visible:outline-offset-4 before:content-[''] before:absolute before:-inset-px before:z-0 before:rounded-[13px] before:p-px before:opacity-[var(--lit,0)] before:bg-[radial-gradient(120px_120px_at_var(--mx,50%)_var(--my,50%),var(--color-tbh32b-glow),transparent_70%)] before:[mask:linear-gradient(#000_0_0)_content-box,linear-gradient(#000_0_0)] before:[mask-composite:exclude] before:transition-opacity before:duration-300 motion-reduce:before:transition-none">
    <span class="relative z-[1]">View pricing</span>
  </button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";

@theme {
  --color-tbh32a-base: #14131c;
  --color-tbh32a-base: oklch(0.19 0.02 285);
  --color-tbh32a-stage: #08070d;
  --color-tbh32a-stage: oklch(0.11 0.01 285);
  --color-tbh32a-ink: #f4f3ff;
  --color-tbh32a-ink: oklch(0.96 0.01 285);
  --color-tbh32a-light: #8c78ff;
  --color-tbh32a-light: oklch(0.68 0.19 285);
  --color-tbh32b-base: #0f1a1c;
  --color-tbh32b-base: oklch(0.19 0.02 195);
  --color-tbh32b-stage: #060d0e;
  --color-tbh32b-stage: oklch(0.11 0.012 195);
  --color-tbh32b-ink: #eafffb;
  --color-tbh32b-ink: oklch(0.97 0.02 175);
  --color-tbh32b-glow: #5fdde8;
  --color-tbh32b-glow: oklch(0.75 0.15 190);
  --font-tbh32-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Stays CSS: the -webkit- mask pair for design B's border ring. The prefixed form needs the
   'xor' keyword where the standard form takes 'exclude', so it cannot be one utility. */

.tbh-32b button::before {
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
}

.tbh-32-group {
  width: 100%;
  min-height: 100svh;
}

.tbh-32a,
.tbh-32b {
  min-height: 100svh;
}
```

## JavaScript
```js
(() => {
  const light = id => {
    const btn = document.querySelector(id); if (!btn) return;
    const set = (mx, my, lit) => {
      btn.style.setProperty('--mx', mx);
      btn.style.setProperty('--my', my);
      btn.style.setProperty('--lit', lit);
    };
    btn.addEventListener('pointermove', e => {
      const r = btn.getBoundingClientRect();
      set((e.clientX - r.left) + 'px', (e.clientY - r.top) + 'px', '1');
    });
    btn.addEventListener('pointerleave', () => btn.style.setProperty('--lit', '0'));
    btn.addEventListener('focus', () => set('50%', '50%', '1'));
    btn.addEventListener('blur', () => btn.style.setProperty('--lit', '0'));
  };
  light('#tbh-32a-btn');
  light('#tbh-32b-btn');
})();
```

How this works

Three properties do everything. --mx and --my place the gradient; --lit fades it. The handler writes nothing else and touches no classes, which means the light's size, colour and falloff are all editable in the markup without reading the script.

Reading a custom property as an opacity. opacity-[var(--lit,0)] is the neat trick here: an arbitrary value can BE a variable reference, so a JS-written number drives a utility directly. The fallback matters — before the first event, 0 keeps the layer hidden.

The border version needs a mask. A gradient cannot paint a border, so design B paints a full-bleed gradient into a 1px-padded box and excludes the content area with mask-composite. What remains is a 1px ring lit only where the cursor is — three arbitrary properties, no extra elements.

Make it yours

  • Light size: the 160px 160px in the gradient (120px on design B's rim).
  • Falloff: the 60% / 70% transparent stop — further out is softer and more diffuse.
  • Colour: --color-tbh32a-light / --color-tbh32b-glow; both are read by the gradient and the focus ring.
  • Fade speed: before:transition-opacity before:duration-300 — the light should fade slower than it moves.

Gotchas — read before shipping

  • Write ONLY custom properties in the pointermove handler. Setting classes or styles that affect layout on every move is how this effect ruins INP.
  • Reset --lit to 0 on pointerleave and blur, or the button stays lit after the cursor has gone.
  • The label must clear 4.5:1 against the UNLIT base, not the lit state — the spotlight only ever adds light.
  • Design B's mask needs both the -webkit- and unprefixed forms; the -webkit- pair stays in CSS because the composite keyword differs (xor vs exclude).

Browser support

ChromeSafariFirefoxEdge
111+16.4+128+111+

Custom properties and pointer events are universal; the mask-composite ring needs Chrome 120 / Safari 15.4 / Firefox 53 and degrades to an unlit border. With JS disabled both buttons are clean dark CTAs.

Techniques used in this demo

Utilitiesfirst-class utility or variant

Theme tokensset in @theme, used by a utility

Arbitraryneeds bracket syntax

HTML & JSno Tailwind involved

Search CodeFronts

Loading…