03CTA 펄스 글로우
히어로 버튼이 scale 과 box-shadow 반경·불투명도를 같이 오가며 숨쉬듯 펄스해 시선을 끌고, 실제 :hover·:focus-visible 에서도 같은 색으로 반응한다.
쓰임무료 체험 시작 버튼, 대기자 명단 등록 CTA
box-shadowscale:hover · :focus-visible

<main class="stage is-demo">
<button type="button" class="cta">Get started</button>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop; // 2s — 펄스 한 바퀴
$easing: $ease-pop;
$color: $subject-cream;
@include stage($stage-orange);
.cta {
box-sizing: border-box;
border: 0;
cursor: pointer;
padding: $sp-4 $sp-8;
border-radius: $r-pill;
background: $color;
color: $subject-orange-deep;
font-weight: 800;
font-size: clamp(14px, 4vw, 16px);
line-height: 1;
font-family: inherit;
box-shadow: 0 0 0 0 rgba(255, 224, 102, 0);
transition: transform $dur-quick $ease-out;
}
.cta:hover { transform: translateY(-2px); }
.cta:focus-visible { outline: 2px solid $subject-ink; outline-offset: 2px; }
// box-shadow 를 애니메이션 대상으로 쓴다 — blog-demo 규칙 1(transform·opacity 만)의 의도적 예외.
// 글로우는 box-shadow(또는 filter) 없이는 성립하지 않는다 (사실.md 기록)
.stage.is-demo .cta { animation: pulse $duration $easing infinite; }
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 224, 102, .55); }
50% { transform: scale(1.045); box-shadow: 0 0 $sp-8 $sp-2 rgba(255, 224, 102, .55); }
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 224, 102, 0); }
}
@media (prefers-reduced-motion: reduce) {
.stage.is-demo .cta { animation: none; box-shadow: 0 0 $sp-6 $sp-1 rgba(255, 224, 102, .35); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.cta {
box-sizing: border-box;
border: 0;
cursor: pointer;
padding: 16px 32px;
border-radius: 999px;
background: #fff7e6;
color: #c93000;
font-weight: 800;
font-size: clamp(14px, 4vw, 16px);
line-height: 1;
font-family: inherit;
box-shadow: 0 0 0 0 rgba(255, 224, 102, 0);
transition: transform 220ms ease-out;
}
.cta:hover {
transform: translateY(-2px);
}
.cta:focus-visible {
outline: 2px solid #17141a;
outline-offset: 2px;
}
.stage.is-demo .cta {
animation: pulse 2s cubic-bezier(0.2, 1.4, 0.4, 1) infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 224, 102, 0.55);
}
50% {
transform: scale(1.045);
box-shadow: 0 0 32px 8px rgba(255, 224, 102, 0.55);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 224, 102, 0);
}
}
@media (prefers-reduced-motion: reduce) {
.stage.is-demo .cta {
animation: none;
box-shadow: 0 0 24px 4px rgba(255, 224, 102, 0.35);
}
}import type { CSSProperties } from "react";
import styles from "./CtaPulseGlow.module.scss";
export type CtaPulseGlowProps = {
label?: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function CtaPulseGlow({
label = "Get started",
duration = 2000,
easing = "cubic-bezier(.2,1.4,.4,1)",
color = "#fff7e6",
}: CtaPulseGlowProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<button type="button" className={styles.cta} style={vars}>
{label}
</button>
);
}03 CTA 펄스 글로우 — 측정 누적면적 8.864% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · CSS 히어로 섹션 애니메이션 9종
이 데모가 속한 글
CSS 히어로 섹션 애니메이션 9종히어로 섹션은 사이트에 들어가자마자 가장 먼저 보이는 큰 화면 하나, 큰 제목과 버튼이 자리 잡은 그 첫인상을 말한다. AOS·GSAP 없이, JS 0줄로 그 화면에 움직임을 주는 CSS 애니메이션 9가지를 모았다. 이 글의 데모 전부 보기


