UI Components Easy
Pulsating Button
A call-to-action button with a pulsing glow ring animation that draws attention and encourages clicks.
Open in Lab
MCP
css javascript vue svelte
Targets: TS JS HTML React Vue Svelte
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, sans-serif;
background: #0a0a0a;
color: #f1f5f9;
min-height: 100vh;
}
.demo {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
padding: 2rem;
}
.demo-title {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
}
.hint {
color: #525252;
font-size: 0.875rem;
margin-bottom: 1rem;
}
.button-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 2.5rem;
}
/* --- Pulsating button base --- */
.pulse-btn {
--pulse-color: 59, 130, 246;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
border-radius: 999px;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: 0.01em;
cursor: pointer;
border: none;
outline: none;
color: #fff;
background: rgb(var(--pulse-color));
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.6);
animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
transition: transform 0.2s ease, filter 0.2s ease;
}
.pulse-btn:hover {
transform: scale(1.05);
filter: brightness(1.15);
}
.pulse-btn:active {
transform: scale(0.97);
}
/* Sizes */
.pulse-btn--sm {
padding: 0.625rem 1.5rem;
font-size: 0.8125rem;
}
.pulse-btn--lg {
padding: 1.125rem 2.75rem;
font-size: 1.0625rem;
}
/* Color variants */
.pulse-btn--blue {
--pulse-color: 59, 130, 246;
}
.pulse-btn--purple {
--pulse-color: 139, 92, 246;
}
.pulse-btn--emerald {
--pulse-color: 16, 185, 129;
}
.pulse-btn--rose {
--pulse-color: 244, 63, 94;
}
.pulse-btn--amber {
--pulse-color: 245, 158, 11;
color: #0a0a0a;
}
/* Pulse keyframes */
@keyframes pulse-ring {
0% {
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.5);
}
50% {
box-shadow: 0 0 0 12px rgba(var(--pulse-color), 0);
}
100% {
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0);
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
.pulse-btn {
animation: none;
}
}// Pulsating Button — minimal JS (CSS handles the animation)
(function () {
"use strict";
const buttons = document.querySelectorAll(".pulse-btn");
buttons.forEach((btn) => {
btn.addEventListener("click", () => {
// Brief pause and restart of animation on click for feedback
btn.style.animation = "none";
btn.offsetHeight; // trigger reflow
btn.style.animation = "";
});
});
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pulsating Button</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h2 class="demo-title">Pulsating Buttons</h2>
<p class="hint">Buttons with animated glow rings</p>
<div class="button-row">
<button class="pulse-btn pulse-btn--blue">
Get Started
</button>
<button class="pulse-btn pulse-btn--purple">
Subscribe Now
</button>
<button class="pulse-btn pulse-btn--emerald">
Download Free
</button>
</div>
<div class="button-row">
<button class="pulse-btn pulse-btn--rose pulse-btn--lg">
Sign Up Today
</button>
<button class="pulse-btn pulse-btn--amber pulse-btn--sm">
Learn More
</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { useCallback, useRef, type CSSProperties, type ReactNode, type MouseEvent } from "react";
interface PulsatingButtonProps {
children: ReactNode;
onClick?: (e: MouseEvent<HTMLButtonElement>) => void;
color?: string;
size?: "sm" | "md" | "lg";
className?: string;
}
const sizeMap = {
sm: { padding: "0.625rem 1.5rem", fontSize: "0.8125rem" },
md: { padding: "0.875rem 2rem", fontSize: "0.9375rem" },
lg: { padding: "1.125rem 2.75rem", fontSize: "1.0625rem" },
};
export function PulsatingButton({
children,
onClick,
color = "59, 130, 246",
size = "md",
className = "",
}: PulsatingButtonProps) {
const btnRef = useRef<HTMLButtonElement>(null);
const handleClick = useCallback(
(e: MouseEvent<HTMLButtonElement>) => {
const btn = btnRef.current;
if (btn) {
btn.style.animation = "none";
btn.offsetHeight;
btn.style.animation = "";
}
onClick?.(e);
},
[onClick]
);
const style: CSSProperties = {
["--pulse-color" as string]: color,
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: sizeMap[size].padding,
fontSize: sizeMap[size].fontSize,
borderRadius: "999px",
fontWeight: 600,
letterSpacing: "0.01em",
cursor: "pointer",
border: "none",
outline: "none",
color: "#fff",
background: `rgb(${color})`,
boxShadow: `0 0 0 0 rgba(${color}, 0.6)`,
animation: "pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
transition: "transform 0.2s ease, filter 0.2s ease",
};
return (
<>
<style>{`
@keyframes pulse-ring {
0% { box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.5); }
50% { box-shadow: 0 0 0 12px rgba(var(--pulse-color), 0); }
100% { box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0); }
}
@media (prefers-reduced-motion: reduce) {
.pulse-btn-react { animation: none !important; }
}
`}</style>
<button
ref={btnRef}
onClick={handleClick}
className={`pulse-btn-react ${className}`}
style={style}
onMouseEnter={(e) => {
e.currentTarget.style.transform = "scale(1.05)";
e.currentTarget.style.filter = "brightness(1.15)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.transform = "scale(1)";
e.currentTarget.style.filter = "brightness(1)";
}}
>
{children}
</button>
</>
);
}
export default function PulsatingButtonDemo() {
return (
<div
style={{
minHeight: "100vh",
background: "#0a0a0a",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: "2rem",
padding: "2rem",
fontFamily: "system-ui, -apple-system, sans-serif",
}}
>
<h2 style={{ fontSize: "1.5rem", fontWeight: 700, color: "#e2e8f0" }}>Pulsating Buttons</h2>
<p style={{ color: "#525252", fontSize: "0.875rem" }}>Buttons with animated glow rings</p>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2.5rem", justifyContent: "center" }}>
<PulsatingButton color="59, 130, 246">Get Started</PulsatingButton>
<PulsatingButton color="139, 92, 246">Subscribe Now</PulsatingButton>
<PulsatingButton color="16, 185, 129">Download Free</PulsatingButton>
</div>
<div style={{ display: "flex", flexWrap: "wrap", gap: "2.5rem", justifyContent: "center" }}>
<PulsatingButton color="244, 63, 94" size="lg">
Sign Up Today
</PulsatingButton>
<PulsatingButton color="245, 158, 11" size="sm">
Learn More
</PulsatingButton>
</div>
</div>
);
}<script setup>
const sizeMap = {
sm: { padding: "0.625rem 1.5rem", fontSize: "0.8125rem" },
md: { padding: "0.875rem 2rem", fontSize: "0.9375rem" },
lg: { padding: "1.125rem 2.75rem", fontSize: "1.0625rem" },
};
const demos = [
{ label: "Get Started", color: "59, 130, 246", size: "md" },
{ label: "Subscribe Now", color: "139, 92, 246", size: "md" },
{ label: "Download Free", color: "16, 185, 129", size: "md" },
{ label: "Sign Up Today", color: "244, 63, 94", size: "lg" },
{ label: "Learn More", color: "245, 158, 11", size: "sm" },
];
function btnStyle(color, size) {
const s = sizeMap[size] || sizeMap.md;
return {
"--pulse-color": color,
position: "relative",
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
padding: s.padding,
fontSize: s.fontSize,
borderRadius: "999px",
fontWeight: 600,
letterSpacing: "0.01em",
cursor: "pointer",
border: "none",
outline: "none",
color: "#fff",
background: `rgb(${color})`,
boxShadow: `0 0 0 0 rgba(${color}, 0.6)`,
animation: "pulse-ring-vue 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
transition: "transform 0.2s ease, filter 0.2s ease",
};
}
function handleClick(e) {
const btn = e.currentTarget;
btn.style.animation = "none";
btn.offsetHeight;
btn.style.animation = "";
}
function handleEnter(e) {
e.currentTarget.style.transform = "scale(1.05)";
e.currentTarget.style.filter = "brightness(1.15)";
}
function handleLeave(e) {
e.currentTarget.style.transform = "scale(1)";
e.currentTarget.style.filter = "brightness(1)";
}
</script>
<template>
<div class="pulse-demo">
<h2 class="pulse-heading">Pulsating Buttons</h2>
<p class="pulse-sub">Buttons with animated glow rings</p>
<div class="pulse-row">
<button
v-for="d in demos.slice(0, 3)"
:key="d.label"
:style="btnStyle(d.color, d.size)"
@click="handleClick"
@mouseenter="handleEnter"
@mouseleave="handleLeave"
>
{{ d.label }}
</button>
</div>
<div class="pulse-row">
<button
v-for="d in demos.slice(3)"
:key="d.label"
:style="btnStyle(d.color, d.size)"
@click="handleClick"
@mouseenter="handleEnter"
@mouseleave="handleLeave"
>
{{ d.label }}
</button>
</div>
</div>
</template>
<style scoped>
@keyframes pulse-ring-vue {
0% { box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.5); }
50% { box-shadow: 0 0 0 12px rgba(var(--pulse-color), 0); }
100% { box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0); }
}
@media (prefers-reduced-motion: reduce) {
button { animation: none !important; }
}
.pulse-demo {
min-height: 100vh;
background: #0a0a0a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
padding: 2rem;
font-family: system-ui, -apple-system, sans-serif;
}
.pulse-heading {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
}
.pulse-sub {
color: #525252;
font-size: 0.875rem;
}
.pulse-row {
display: flex;
flex-wrap: wrap;
gap: 2.5rem;
justify-content: center;
}
</style><script>
const demos = [
{ label: "Get Started", color: "59, 130, 246", size: "md", id: "blue" },
{ label: "Subscribe Now", color: "139, 92, 246", size: "md", id: "purple" },
{ label: "Download Free", color: "16, 185, 129", size: "md", id: "emerald" },
{ label: "Sign Up Today", color: "244, 63, 94", size: "lg", id: "rose" },
{ label: "Learn More", color: "245, 158, 11", size: "sm", id: "amber" },
];
const sizeClass = { sm: "pulse-btn--sm", md: "", lg: "pulse-btn--lg" };
function colorStyle(color) {
return `--pulse-color: ${color}; background: rgb(${color});`;
}
function handleClick(e) {
const btn = e.currentTarget;
btn.style.animation = "none";
btn.offsetHeight; // reflow
btn.style.animation = "";
}
</script>
<style>
:global(*), :global(*::before), :global(*::after) {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.demo-wrap {
min-height: 100vh;
background: #0a0a0a;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
padding: 2rem;
font-family: system-ui, -apple-system, sans-serif;
}
h2 {
font-size: 1.5rem;
font-weight: 700;
color: #e2e8f0;
}
p {
color: #525252;
font-size: 0.875rem;
}
.button-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 2.5rem;
}
.pulse-btn {
--pulse-color: 59, 130, 246;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.875rem 2rem;
border-radius: 999px;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: 0.01em;
cursor: pointer;
border: none;
outline: none;
color: #fff;
background: rgb(var(--pulse-color));
box-shadow: 0 0 0 0 rgba(var(--pulse-color), 0.6);
animation: pulse-ring 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
transition: transform 0.2s ease, filter 0.2s ease;
}
.pulse-btn:hover {
transform: scale(1.05);
filter: brightness(1.15);
}
.pulse-btn:active {
transform: scale(0.97);
}
.pulse-btn--sm {
padding: 0.625rem 1.5rem;
font-size: 0.8125rem;
}
.pulse-btn--lg {
padding: 1.125rem 2.75rem;
font-size: 1.0625rem;
}
.pulse-btn--amber {
color: #0a0a0a;
}
/* Svelte keeps @keyframes global — no scoping needed */
@keyframes pulse-ring {
0% { box-shadow: 0 0 0 0px rgba(var(--pulse-color), 0.55); }
50% { box-shadow: 0 0 0 14px rgba(var(--pulse-color), 0); }
100% { box-shadow: 0 0 0 0px rgba(var(--pulse-color), 0); }
}
@media (prefers-reduced-motion: reduce) {
.pulse-btn { animation: none; }
}
</style>
<div class="demo-wrap">
<h2>Pulsating Buttons</h2>
<p>Buttons with animated glow rings</p>
<div class="button-row">
{#each demos.slice(0, 3) as d}
<button
class="pulse-btn {sizeClass[d.size]} {d.id === 'amber' ? 'pulse-btn--amber' : ''}"
style={colorStyle(d.color)}
on:click={handleClick}
>
{d.label}
</button>
{/each}
</div>
<div class="button-row">
{#each demos.slice(3) as d}
<button
class="pulse-btn {sizeClass[d.size]} {d.id === 'amber' ? 'pulse-btn--amber' : ''}"
style={colorStyle(d.color)}
on:click={handleClick}
>
{d.label}
</button>
{/each}
</div>
</div>Pulsating Button
A button with a continuously pulsing glow ring that radiates outward, creating an eye-catching call-to-action effect perfect for hero sections and landing pages.
How it works
- A CSS
@keyframes pulseanimation expands abox-shadowoutward from the button - The shadow grows in size while fading in opacity, creating a radiating ring effect
- The animation loops infinitely with a smooth ease-out timing
Customization
- Color: Change the
--pulse-colorCSS variable to match your brand - Speed: Adjust
animation-durationfor faster or slower pulses - Intensity: Modify the
box-shadowspread values in the keyframes
When to use it
- Primary CTA buttons on hero sections
- Sign-up or subscribe buttons
- Any action you want to draw immediate attention to