07Drifting-orb CTA band
Behind the band one large orb drifts slowly back and forth (translateX) while the solid button in front lifts 1px with a deeper shadow on hover. No glowing buttons here.
Best forSign-up bands, request-a-demo CTAs
translateXradial-gradienttranslateY

<main class="stage">
<section class="cb is-demo" id="cb">
<!-- 오브는 순수 장식 — 낭독기에서 숨긴다 -->
<span class="cb__orb" aria-hidden="true"></span>
<p class="cb__eyebrow">
<span class="cb__ico" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M194.82,151.43l-55.09,20.3-20.3,55.09a7.92,7.92,0,0,1-14.86,0l-20.3-55.09-55.09-20.3a7.92,7.92,0,0,1,0-14.86l55.09-20.3,20.3-55.09a7.92,7.92,0,0,1,14.86,0l20.3,55.09,55.09,20.3A7.92,7.92,0,0,1,194.82,151.43Z" opacity="0.2"/><path d="M197.58,129.06,146,110l-19-51.62a15.92,15.92,0,0,0-29.88,0L78,110l-51.62,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0L146,178l51.62-19a15.92,15.92,0,0,0,0-29.88ZM137,164.22a8,8,0,0,0-4.74,4.74L112,223.85,91.78,169A8,8,0,0,0,87,164.22L32.15,144,87,123.78A8,8,0,0,0,91.78,119L112,64.15,132.22,119a8,8,0,0,0,4.74,4.74L191.85,144ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z"/></svg></span>
Free trial open
</p>
<h2 class="cb__title">Run your first prompt before today ends</h2>
<p class="cb__sub">No card needed — your team workspace opens in five minutes.</p>
<button type="button" class="cb__cta" id="cbCta">
Start for free
<span class="cb__ico cb__ico--sm" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,128l-72,72V56Z" opacity="0.2"/><path d="M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z"/></svg></span>
</button>
<p class="cb__note">Cancel anytime</p>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 오브가 좌 → 우 → 좌 를 한 바퀴 도는 시간
$easing: $ease-spring; // 버튼이 떠오르는 이징
$color: $subject-lilac; // 오브 핵심 색
@include stage($stage-orange);
.cb {
position: relative; overflow: hidden; // 오브가 밴드 밖으로 새지 않게
box-sizing: border-box; width: min(100%, 420px);
background: $subject-ink; border-radius: $r-card; box-shadow: $shadow-raised;
padding: $sp-5; text-align: center;
}
// ── 오브: 겹친 radial-gradient 한 덩어리. 필터도 이미지도 없이 그라디언트만으로 공처럼 보이게 한다
// 핵심 알파를 .55 로 묶으면 오브 한가운데가 가장 밝을 때도 글자가 AA 를 넘는다 —
// 렌더 픽셀 실측(오브를 가운데 세우고 글자를 감춘 뒤, 요소마다 그 띠에서 가장 밝은 배경을 샘플).
// 네 요소는 오브의 서로 다른 높이에 걸치므로 배경도 각각 다르다 — 한 배경을 공유하지 않는다:
// 제목 크림 100% / #53528a → 6.70:1
// 부제 크림 100% / #6e6096 → 5.22:1 (오브 핵을 정면으로 지나 가장 밝은 자리)
// 눈썹 크림 72% / #475c90 → 6.32:1
// 주석 크림 62% / #1e305f → 5.59:1
// 부제만 크림 72% 로 두었을 때 3.58:1 이라 알파를 100% 로 올렸다 (11px 500 은 큰 글자 예외도 못 받는다).
// 위계는 색이 아니라 크기(19px↔11px)와 굵기(800↔500)가 만든다. (오브 없는 잉크 바탕에서는 17.11:1)
.cb__orb {
position: absolute; left: 50%; top: 50%;
margin-left: -104px; margin-top: -104px; // 자기 크기의 절반만큼 당겨 가운데로
width: 208px; height: 208px; border-radius: 50%; z-index: 0; pointer-events: none;
background: radial-gradient(circle at 50% 50%,
rgba($color, .55) 0%,
rgba($subject-blue, .40) 38%,
rgba($subject-blue, 0) 70%);
// 유영은 부품의 기본 성질 — .is-demo 와 무관하게 항상 돈다
animation-name: orbDrift;
animation-duration: $duration;
animation-timing-function: ease-in-out; // 왕복이라 양 끝에서 느려져야 한다 ($ease-out 은 편도용)
animation-iteration-count: infinite;
}
// 왕복이라 되돌아오는 경로가 그대로 보인다 — 끝에서 처음으로 튀는 구간이 없다
@keyframes orbDrift {
0% { transform: translateX(-34%); }
50% { transform: translateX(34%); }
100% { transform: translateX(-34%); }
}
.cb__eyebrow,
.cb__title,
.cb__sub,
.cb__cta,
.cb__note { position: relative; z-index: 1; }
.cb__eyebrow {
display: inline-flex; align-items: center; gap: $sp-1; margin: 0;
font-weight: 800; font-size: clamp(9px, 2.6vw, 10px); line-height: 1;
letter-spacing: .04em; color: rgba($subject-cream, .72); // 눈썹 띠 최악 배경 #475c90 위 6.32:1
}
.cb__ico { display: inline-flex; width: 14px; height: 14px; flex: 0 0 auto; }
.cb__ico--sm { width: 12px; height: 12px; }
.cb__ico svg { width: 100%; height: 100%; }
.cb__title {
margin: $sp-2 0 0; font-weight: 800; font-size: clamp(14px, 4.2vw, 19px); line-height: 1.25;
color: $subject-cream; // 제목 띠 최악 배경 #53528a 위 6.70:1 (잉크 바탕 17.11:1)
}
.cb__sub {
margin: $sp-2 0 0; font-weight: 500; font-size: clamp(9px, 2.8vw, 11px); line-height: 1.45;
color: $subject-cream; // 부제 띠 최악 배경 #6e6096 위 5.22:1 (72% 였을 때 3.58:1 미달)
}
// ── 솔리드 버튼: 빛나지 않는다. 떠오름은 transform, 깊이는 그림자
.cb__cta {
display: inline-flex; align-items: center; gap: $sp-2; margin-top: $sp-4;
padding: $sp-3 $sp-6; border: 0; border-radius: $r-control; cursor: pointer;
background: $subject-cream; color: $subject-ink; // 17.11:1
font-family: inherit; font-weight: 800; font-size: clamp(11px, 3.2vw, 13px); line-height: 1;
box-shadow: $shadow-press;
transition: transform $dur-quick $easing, box-shadow $dur-quick $easing;
}
.cb__cta:hover,
.cb__cta:focus-visible { transform: translateY(-1px); box-shadow: $shadow-raised; }
.cb__cta:active { transform: translateY(0); box-shadow: $shadow-press; }
.cb__note {
margin: $sp-2 0 0; font-weight: 600; font-size: clamp(8px, 2.4vw, 9px); line-height: 1;
color: rgba($subject-cream, .62); // 주석 띠 최악 배경 #1e305f 위 5.59:1
}
// ── 미리보기 루프: 호버를 못 하는 격자 칸에서도 "떠오르는 버튼" 을 보여준다 (복귀 90% 안)
.cb.is-demo .cb__cta {
animation-name: ctaLift;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
@keyframes ctaLift {
0%, 40% { transform: translateY(0); box-shadow: $shadow-press; }
54%, 84% { transform: translateY(-1px); box-shadow: $shadow-raised; }
90%, 100% { transform: translateY(0); box-shadow: $shadow-press; }
}
// ── 320×200 폰 실측 대비 압축 (BD9). 이 블록 안에서는 간격을 올리지 않는다
@media (max-width: 340px), (max-height: 220px) {
.cb { padding: $sp-3; }
.cb__orb { width: 160px; height: 160px; margin-left: -80px; margin-top: -80px; }
.cb__eyebrow { font-size: 8px; }
.cb__ico { width: 12px; height: 12px; }
.cb__title { font-size: 12px; margin-top: $sp-1; }
.cb__sub { font-size: 8px; margin-top: $sp-1; }
.cb__cta { margin-top: $sp-2; padding: $sp-2 $sp-4; font-size: 10px; }
.cb__note { font-size: 7px; margin-top: $sp-1; }
}
@media (prefers-reduced-motion: reduce) {
.cb__orb { animation: none !important; transform: none !important; }
.cb.is-demo .cb__cta { animation: none !important; }
.cb__cta { transition: none !important; }
}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);
}
.cb {
position: relative;
overflow: hidden;
box-sizing: border-box;
width: min(100%, 420px);
background: #17141a;
border-radius: 16px;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
padding: 20px;
text-align: center;
}
.cb__orb {
position: absolute;
left: 50%;
top: 50%;
margin-left: -104px;
margin-top: -104px;
width: 208px;
height: 208px;
border-radius: 50%;
z-index: 0;
pointer-events: none;
background: radial-gradient(circle at 50% 50%, rgba(185, 160, 255, 0.55) 0%, rgba(47, 109, 246, 0.4) 38%, rgba(47, 109, 246, 0) 70%);
animation-name: orbDrift;
animation-duration: 2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
@keyframes orbDrift {
0% {
transform: translateX(-34%);
}
50% {
transform: translateX(34%);
}
100% {
transform: translateX(-34%);
}
}
.cb__eyebrow,
.cb__title,
.cb__sub,
.cb__cta,
.cb__note {
position: relative;
z-index: 1;
}
.cb__eyebrow {
display: inline-flex;
align-items: center;
gap: 4px;
margin: 0;
font-weight: 800;
font-size: clamp(9px, 2.6vw, 10px);
line-height: 1;
letter-spacing: 0.04em;
color: rgba(255, 247, 230, 0.72);
}
.cb__ico {
display: inline-flex;
width: 14px;
height: 14px;
flex: 0 0 auto;
}
.cb__ico--sm {
width: 12px;
height: 12px;
}
.cb__ico svg {
width: 100%;
height: 100%;
}
.cb__title {
margin: 8px 0 0;
font-weight: 800;
font-size: clamp(14px, 4.2vw, 19px);
line-height: 1.25;
color: #fff7e6;
}
.cb__sub {
margin: 8px 0 0;
font-weight: 500;
font-size: clamp(9px, 2.8vw, 11px);
line-height: 1.45;
color: #fff7e6;
}
.cb__cta {
display: inline-flex;
align-items: center;
gap: 8px;
margin-top: 16px;
padding: 12px 24px;
border: 0;
border-radius: 12px;
cursor: pointer;
background: #fff7e6;
color: #17141a;
font-family: inherit;
font-weight: 800;
font-size: clamp(11px, 3.2vw, 13px);
line-height: 1;
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.cb__cta:hover,
.cb__cta:focus-visible {
transform: translateY(-1px);
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.cb__cta:active {
transform: translateY(0);
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
}
.cb__note {
margin: 8px 0 0;
font-weight: 600;
font-size: clamp(8px, 2.4vw, 9px);
line-height: 1;
color: rgba(255, 247, 230, 0.62);
}
.cb.is-demo .cb__cta {
animation-name: ctaLift;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
@keyframes ctaLift {
0%, 40% {
transform: translateY(0);
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
}
54%, 84% {
transform: translateY(-1px);
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
90%, 100% {
transform: translateY(0);
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
}
}
@media (max-width: 340px), (max-height: 220px) {
.cb {
padding: 12px;
}
.cb__orb {
width: 160px;
height: 160px;
margin-left: -80px;
margin-top: -80px;
}
.cb__eyebrow {
font-size: 8px;
}
.cb__ico {
width: 12px;
height: 12px;
}
.cb__title {
font-size: 12px;
margin-top: 4px;
}
.cb__sub {
font-size: 8px;
margin-top: 4px;
}
.cb__cta {
margin-top: 8px;
padding: 8px 16px;
font-size: 10px;
}
.cb__note {
font-size: 7px;
margin-top: 4px;
}
}
@media (prefers-reduced-motion: reduce) {
.cb__orb {
animation: none !important;
transform: none !important;
}
.cb.is-demo .cb__cta {
animation: none !important;
}
.cb__cta {
transition: none !important;
}
}// 사람이 손을 대면 미리보기 루프를 끄고 진짜 호버·포커스에 넘긴다
var band = document.getElementById('cb');
var cta = document.getElementById('cbCta');
function stopPreview() { band.classList.remove('is-demo'); }
cta.addEventListener('pointerenter', stopPreview);
cta.addEventListener('focus', stopPreview);
cta.addEventListener('click', stopPreview);import { useState } from "react";
import styles from "./CtaBandOrb.module.scss";
export type CtaBandOrbProps = {
/** 오브가 좌 → 우 → 좌 를 한 바퀴 도는 시간 */
duration?: string;
/** 버튼이 떠오르는 이징 */
easing?: string;
/** 오브 핵심 색 — --orbColor 로 내려간다 */
color?: string;
};
export default function CtaBandOrb({
duration = "2s",
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#b9a0ff",
}: CtaBandOrbProps) {
// 사람이 손을 대면 미리보기 루프를 끄고 진짜 호버·포커스에 넘긴다
const [preview, setPreview] = useState(true);
const stopPreview = () => { setPreview(false); };
return (
<main className={styles.stage}>
<section className={styles.cb} style={{ "--orbColor": color }}>
{/* 오브는 순수 장식 — 낭독기에서 숨긴다 */}
<span
className={styles.orb}
aria-hidden="true"
style={{
animationName: "orbDrift",
animationDuration: duration,
animationTimingFunction: "ease-in-out",
animationIterationCount: "infinite",
}}
/>
<p className={styles.eyebrow}>
<span className={styles.ico} aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M194.82,151.43l-55.09,20.3-20.3,55.09a7.92,7.92,0,0,1-14.86,0l-20.3-55.09-55.09-20.3a7.92,7.92,0,0,1,0-14.86l55.09-20.3,20.3-55.09a7.92,7.92,0,0,1,14.86,0l20.3,55.09,55.09,20.3A7.92,7.92,0,0,1,194.82,151.43Z" opacity="0.2"/><path d="M197.58,129.06,146,110l-19-51.62a15.92,15.92,0,0,0-29.88,0L78,110l-51.62,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0L146,178l51.62-19a15.92,15.92,0,0,0,0-29.88ZM137,164.22a8,8,0,0,0-4.74,4.74L112,223.85,91.78,169A8,8,0,0,0,87,164.22L32.15,144,87,123.78A8,8,0,0,0,91.78,119L112,64.15,132.22,119a8,8,0,0,0,4.74,4.74L191.85,144ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z"/></svg></span>
무료 체험 열림
</p>
<h2 className={styles.title}>첫 프롬프트를 오늘 안에 돌려보세요</h2>
<p className={styles.sub}>카드 등록 없이 5분이면 팀 작업실이 열립니다.</p>
<button
type="button"
className={styles.cta}
style={preview ? {
animationName: "ctaLift",
animationDuration: duration,
animationTimingFunction: easing,
animationIterationCount: "infinite",
} : undefined}
onPointerEnter={stopPreview}
onFocus={stopPreview}
onClick={stopPreview}
>
무료로 시작하기
<span className={`${styles.ico} ${styles.icoSm}`} aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M216,128l-72,72V56Z" opacity="0.2"/><path d="M221.66,122.34l-72-72A8,8,0,0,0,136,56v64H40a8,8,0,0,0,0,16h96v64a8,8,0,0,0,13.66,5.66l72-72A8,8,0,0,0,221.66,122.34ZM152,180.69V75.31L204.69,128Z"/></svg></span>
</button>
<p className={styles.note}>언제든 해지할 수 있어요</p>
</section>
</main>
);
}07 Drifting-orb CTA band — Measured area 33.242% · moving frames 18/23Original by GODRICH · SCSS and React ship in the zip · AI Landing Page Design Kit: 9 Glowing Orb Sections
The post this demo lives in
AI Landing Page Design Kit: 9 Glowing Orb SectionsAI landing page design has gone AI-native without painting a gradient background. The page stays one flat dark color, an See all demos in this post


