18 / 36
All 36 in 36 Tailwind CSS Button Hover Effects
- 01Tailwind Magnetic Ripple Attraction Button Hover Effect
- 02Tailwind Cyberpunk Holographic Glitch Grid Button
- 03Tailwind Frosted Glassmorphism Shimmer Glow Button
- 04Tailwind Organic Blob Morph Button Hover Effect
- 05Tailwind Dual-Line Intersection Border Draw Button
- 06Tailwind Isometric Tactile 3D Depress Button
- 07Tailwind Infinite Sliding Kinetic Marquee Button
- 08Tailwind Chroma Conic Gradient Border Spin Button
- 09Tailwind Minimalist Border & Outline Drawing Hover Effects
- 10Tailwind Modern 3D Skeuomorphic Button Press Effects
- 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
- 12Tailwind Sliding Background Color Wipe Button Effects
- 13Tailwind Liquid Fill & Organic Morphing Button Effects
- 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
- 15Tailwind Liquid Wave Fill Button Hover Effect
- 16Tailwind Magnetic Border Drift Button
- 17Tailwind Glitch Slice Button Hover Effect
- 18Tailwind Neon Pulse Glow Button Hover Effect
- 19Tailwind Split Reveal Curtain Button Effect
- 20Tailwind Diagonal Shutter Sweep Button Effect
- 21Tailwind Elastic Lift Spring Button Hover Effect
- 22Tailwind Center Underline Draw Link Button Effects
- 23Tailwind 3D Flip Card Button Hover Effect
- 24Tailwind Particle Burst Confetti Button Effect
- 25Tailwind Slide Doors Closing Panel Button Effect
- 26Tailwind Typewriter Retype Terminal Button Effect
- 27Tailwind Hard Shadow Shift Flat Button Effect
- 28Tailwind Material Ripple Wave Button Effect
- 29Tailwind Rainbow Gradient Border Spin Button
- 30Tailwind Morph Shape Silhouette Button Effects
- 31Tailwind Icon Slide In Directional Button Effect
- 32Tailwind Cursor Spotlight Glow Button
- 33Tailwind Stagger Fill Equaliser Button Effect
- 34Tailwind Sunburst Expand Radiant Button Effect
- 35Tailwind 3D Press Plunger Key Button Effect
- 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Tailwind Neon Pulse Glow Button Hover Effect
A glowing CTA whose neon halo breathes: on hover a heavy blurred glow scales gently in and out on a loop, so the button looks like a live light source — the soft looping counterpart to entry 11's hard snap. The reduced-motion fallback here is the interesting part: a steady mid-glow, not an absence.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-18-group w-full min-h-svh flex flex-wrap">
<section class="tbh-18a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18a-stage" aria-label="Neon pulse button">
<button type="button" class="cursor-pointer rounded-full border-2 border-tbh18a-neon px-10 py-4 text-base font-extrabold uppercase tracking-[0.05em] text-white bg-[color-mix(in_oklab,var(--color-tbh18a-neon)_24%,var(--color-tbh18a-stage))] shadow-[0_0_14px_-2px_color-mix(in_oklab,var(--color-tbh18a-neon)_70%,transparent)] transition-colors duration-200 hover:animate-tbh18a-pulse focus-visible:animate-tbh18a-pulse focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)] motion-reduce:focus-visible:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)]">
Go live
</button>
</section>
<section class="tbh-18b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18b-stage" aria-label="Neon flicker button">
<button type="button" class="cursor-pointer rounded-lg border-2 border-tbh18b-neon px-[42px] py-4 text-base font-extrabold uppercase tracking-[0.08em] text-tbh18b-neon bg-transparent [text-shadow:0_0_8px_var(--color-tbh18b-neon)] shadow-[0_0_10px_-2px_var(--color-tbh18b-neon),inset_0_0_10px_-4px_var(--color-tbh18b-neon)] transition-colors duration-200 hover:animate-tbh18b-flick focus-visible:animate-tbh18b-flick focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_22px_-2px_var(--color-tbh18b-neon),inset_0_0_14px_-4px_var(--color-tbh18b-neon)]">
Open bar
</button>
</section>
</div><div class="tbh-18-group w-full min-h-svh flex flex-wrap">
<section class="tbh-18a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18a-stage" aria-label="Neon pulse button">
<button type="button" class="cursor-pointer rounded-full border-2 border-tbh18a-neon px-10 py-4 text-base font-extrabold uppercase tracking-[0.05em] text-white bg-[color-mix(in_oklab,var(--color-tbh18a-neon)_24%,var(--color-tbh18a-stage))] shadow-[0_0_14px_-2px_color-mix(in_oklab,var(--color-tbh18a-neon)_70%,transparent)] transition-colors duration-200 hover:animate-tbh18a-pulse focus-visible:animate-tbh18a-pulse focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)] motion-reduce:focus-visible:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)]">
Go live
</button>
</section>
<section class="tbh-18b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18b-stage" aria-label="Neon flicker button">
<button type="button" class="cursor-pointer rounded-lg border-2 border-tbh18b-neon px-[42px] py-4 text-base font-extrabold uppercase tracking-[0.08em] text-tbh18b-neon bg-transparent [text-shadow:0_0_8px_var(--color-tbh18b-neon)] shadow-[0_0_10px_-2px_var(--color-tbh18b-neon),inset_0_0_10px_-4px_var(--color-tbh18b-neon)] transition-colors duration-200 hover:animate-tbh18b-flick focus-visible:animate-tbh18b-flick focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_22px_-2px_var(--color-tbh18b-neon),inset_0_0_14px_-4px_var(--color-tbh18b-neon)]">
Open bar
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<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.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}<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">
<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.
color-mix()works inside@keyframesas well as inside arbitrary values, which is what lets the whole glow derive from one neon token in both places — the CSS original repeated the colour six times.- This is the collection's clearest example of a
motion-reduce:fallback that is a design decision rather than a deletion: the glow lands at its midpoint and holds, so hover still says 'yes, this is interactive'. - UI change from the CSS original: both pulses now fire on
focus-visible:as well as hover, and design B's reduced-motion state keeps the full static glow instead of the dimmer resting one.
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 Neon Pulse Glow Button Hover Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-neon-pulse-glow-button-hover-effect/
A glowing CTA whose neon halo breathes: on hover a heavy blurred glow scales gently in and out on a loop, so the button looks like a live light source — the soft looping counterpart to entry 11's hard snap. The reduced-motion fallback here is the interesting part: a steady mid-glow, not an absence.
## HTML
```html
<div class="tbh-18-group w-full min-h-svh flex flex-wrap">
<section class="tbh-18a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18a-stage" aria-label="Neon pulse button">
<button type="button" class="cursor-pointer rounded-full border-2 border-tbh18a-neon px-10 py-4 text-base font-extrabold uppercase tracking-[0.05em] text-white bg-[color-mix(in_oklab,var(--color-tbh18a-neon)_24%,var(--color-tbh18a-stage))] shadow-[0_0_14px_-2px_color-mix(in_oklab,var(--color-tbh18a-neon)_70%,transparent)] transition-colors duration-200 hover:animate-tbh18a-pulse focus-visible:animate-tbh18a-pulse focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)] motion-reduce:focus-visible:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)]">
Go live
</button>
</section>
<section class="tbh-18b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18b-stage" aria-label="Neon flicker button">
<button type="button" class="cursor-pointer rounded-lg border-2 border-tbh18b-neon px-[42px] py-4 text-base font-extrabold uppercase tracking-[0.08em] text-tbh18b-neon bg-transparent [text-shadow:0_0_8px_var(--color-tbh18b-neon)] shadow-[0_0_10px_-2px_var(--color-tbh18b-neon),inset_0_0_10px_-4px_var(--color-tbh18b-neon)] transition-colors duration-200 hover:animate-tbh18b-flick focus-visible:animate-tbh18b-flick focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_22px_-2px_var(--color-tbh18b-neon),inset_0_0_14px_-4px_var(--color-tbh18b-neon)]">
Open bar
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}
```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 Neon Pulse Glow Button Hover Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-neon-pulse-glow-button-hover-effect/
A glowing CTA whose neon halo breathes: on hover a heavy blurred glow scales gently in and out on a loop, so the button looks like a live light source — the soft looping counterpart to entry 11's hard snap. The reduced-motion fallback here is the interesting part: a steady mid-glow, not an absence.
## HTML
```html
<div class="tbh-18-group w-full min-h-svh flex flex-wrap">
<section class="tbh-18a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18a-stage" aria-label="Neon pulse button">
<button type="button" class="cursor-pointer rounded-full border-2 border-tbh18a-neon px-10 py-4 text-base font-extrabold uppercase tracking-[0.05em] text-white bg-[color-mix(in_oklab,var(--color-tbh18a-neon)_24%,var(--color-tbh18a-stage))] shadow-[0_0_14px_-2px_color-mix(in_oklab,var(--color-tbh18a-neon)_70%,transparent)] transition-colors duration-200 hover:animate-tbh18a-pulse focus-visible:animate-tbh18a-pulse focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)] motion-reduce:focus-visible:shadow-[0_0_24px_3px_var(--color-tbh18a-neon)]">
Go live
</button>
</section>
<section class="tbh-18b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh18-sans bg-tbh18b-stage" aria-label="Neon flicker button">
<button type="button" class="cursor-pointer rounded-lg border-2 border-tbh18b-neon px-[42px] py-4 text-base font-extrabold uppercase tracking-[0.08em] text-tbh18b-neon bg-transparent [text-shadow:0_0_8px_var(--color-tbh18b-neon)] shadow-[0_0_10px_-2px_var(--color-tbh18b-neon),inset_0_0_10px_-4px_var(--color-tbh18b-neon)] transition-colors duration-200 hover:animate-tbh18b-flick focus-visible:animate-tbh18b-flick focus-visible:outline-3 focus-visible:outline-white focus-visible:outline-offset-4 motion-reduce:hover:animate-none motion-reduce:focus-visible:animate-none motion-reduce:hover:shadow-[0_0_22px_-2px_var(--color-tbh18b-neon),inset_0_0_14px_-4px_var(--color-tbh18b-neon)]">
Open bar
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh18a-neon: #e065c6;
--color-tbh18a-neon: oklch(0.72 0.2 330);
--color-tbh18a-stage: #0a0710;
--color-tbh18a-stage: oklch(0.12 0.02 320);
--color-tbh18b-neon: #5fdde8;
--color-tbh18b-neon: oklch(0.78 0.16 195);
--color-tbh18b-stage: #04100f;
--color-tbh18b-stage: oklch(0.13 0.02 195);
--font-tbh18-sans: 'Space Grotesk', system-ui, sans-serif;
--animate-tbh18a-pulse: tbh18a-pulse 1.4s ease-in-out infinite;
--animate-tbh18b-flick: tbh18b-flick 2.2s steps(1) infinite;
}
/* Stays CSS: both keyframes animate box-shadow, text-shadow and filter — none of which
has a keyframe-able utility form, and all of which repaint rather than composite. */
@keyframes tbh18a-pulse {
0%,
100% {
box-shadow: 0 0 12px -2px color-mix(in oklab, var(--color-tbh18a-neon) 65%, transparent);
text-shadow: 0 0 6px color-mix(in oklab, var(--color-tbh18a-neon) 60%, transparent);
filter: brightness(1);
}
50% {
box-shadow: 0 0 34px 6px var(--color-tbh18a-neon), 0 0 60px 12px color-mix(in oklab, var(--color-tbh18a-neon) 55%, transparent);
text-shadow: 0 0 16px var(--color-tbh18a-neon);
filter: brightness(1.25);
}
}
@keyframes tbh18b-flick {
0%,
19%,
21%,
23%,
80%,
100% {
opacity: 1;
box-shadow: 0 0 22px -2px var(--color-tbh18b-neon), inset 0 0 14px -4px var(--color-tbh18b-neon);
}
20%,
22% {
opacity: 0.5;
box-shadow: none;
}
}
.tbh-18-group {
width: 100%;
min-height: 100svh;
}
.tbh-18a,
.tbh-18b {
min-height: 100svh;
}
```How this works
The pulse animates paint, and that is the cost. An animated box-shadow repaints rather than composites, which is why the glow is kept small, slow (1.4s) and hover-only. This is the one demo in the collection where the honest advice is 'use sparingly' — and stating the animation in the hover variant makes that restriction visible.
Reduced motion still gets the answer. Dropping the animation would leave hover with no feedback at all, so the motion-reduce: twin pins a steady glow roughly at the keyframe's midpoint. Same message, no motion.
Design B flickers rather than breathes. Two brief dips at 20% and 22% on a steps(1) curve, which is enough to suggest a bad connection without becoming a strobe. Keep the dips short; a slow flicker is a photosensitivity hazard.
Make it yours
- Neon:
--color-tbh18a-neon/-tbh18b-neon. Fill, border, glow and text-shadow all derive from it viacolor-mix(). - Pulse rate: the
1.4sin the animation token. Below 1s it reads as alarm rather than life. - Glow reach: the
34px/60pxspreads in the keyframe's 50% stop. - Resting glow: the button already carries a small static shadow — raise it and the pulse becomes a swell rather than an ignition.
Gotchas — read before shipping
- Animated
box-shadowis heavier than any transform in this collection. Keep it to one element, on hover, and never on a list of buttons. - The label must clear 4.5:1 at the DIMMEST frame, not the brightest — check the 0% stop, since that is where the glow contributes least.
- Design B's flicker must stay brief and irregular. Long, regular flashes between 3–50Hz are a seizure risk; two 2% dips per 2.2s cycle are not.
filter: brightness()inside the keyframe affects the label too, which is intended here — but it also creates a containing block, so do not nest positioned decoration inside a pulsing button.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Animated box-shadow, text-shadow and filter are universal; with animation unavailable both buttons keep their static neon rim and fill.