04차례로 깨는 기능 목록
여섯 행의 미니 오브가 위에서 아래로 차례로 밝아졌다 어두워지는 2초 루프를 돕니다(animation-delay). 한 번에 하나만 밝아져서 시선이 목록을 따라 자연스럽게 내려갑니다.
쓰임기능 목록, 처리 단계 소개
animation-delayopacityradial-gradient

<main class="stage">
<section class="fl is-demo" id="fl">
<p class="fl__eyebrow">
<span class="fl__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>
AI 워크스페이스
</p>
<h2 class="fl__title">차례로 깨어나는 기능 6가지</h2>
<ul class="fl__list">
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">문서에서 답 찾기</span></li>
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">회의록 자동 요약</span></li>
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">표 데이터 정리</span></li>
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">초안 다시 쓰기</span></li>
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">말투 맞춰 번역</span></li>
<li class="fl__row"><span class="fl__orb" aria-hidden="true"></span><span class="fl__label">코드 조각 만들기</span></li>
</ul>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// 변형은 이 셋만 바꾼다
$duration: $dur-loop; // 한 바퀴 2초. 여섯 행이 이 안에서 한 번씩 깬다 (2000 ÷ 6 = 334ms 간격)
$easing: $ease-out;
$color: $subject-blue;
@include stage($stage-ink);
// 오브 표면 — 왼쪽 위 하이라이트 한 겹 + 본체 한 겹. 가장자리는 무대 쪽으로 가라앉는다
@mixin orbSkin($hue) {
background:
radial-gradient(circle at 34% 26%, rgba(255, 255, 255, .92) 0%, rgba(255, 255, 255, 0) 44%),
radial-gradient(circle at 52% 54%, $hue 0%, rgba($hue, .45) 58%, rgba($subject-ink, .92) 100%);
}
.fl { width: min(100%, 400px); }
.fl__eyebrow {
display: flex; align-items: center; gap: $sp-1; margin: 0;
font-weight: 700; font-size: clamp(8px, 2.4vw, 10px); line-height: 1; letter-spacing: .08em;
color: rgba(255, 255, 255, .72); // 잉크 무대 위 9.81:1 — 계산 2026-09-12
}
.fl__ico { display: inline-flex; width: 12px; height: 12px; flex: 0 0 auto; color: $color; } // 장식 아이콘, 잉크 위 4.03:1
.fl__ico svg { width: 100%; height: 100%; }
.fl__title {
margin: $sp-1 0 0; font-weight: 800; font-size: clamp(13px, 3.6vw, 16px); line-height: 1.25;
color: $subject-cream; // 잉크 위 16.2:1
}
.fl__list { list-style: none; margin: $sp-3 0 0; padding: 0; }
.fl__row {
display: grid; grid-template-columns: 14px 1fr; align-items: center; gap: $sp-3;
padding: $sp-1 0; border-top: 1px solid rgba(255, 255, 255, .1);
}
.fl__row:first-child { border-top: 0; }
// 빛 무리는 box-shadow 한 겹. opacity 가 요소째 흐려지므로 오브가 어두워질 때 무리도 같이 죽는다
.fl__orb { width: 14px; height: 14px; border-radius: 50%; box-shadow: 0 0 10px rgba($color, .5); @include orbSkin($color); }
.fl__label {
font-weight: 600; font-size: clamp(10px, 3vw, 12px); line-height: 1.3;
color: $subject-cream; // 루프의 가장 어두운 순간(opacity .62)에도 잉크 위 7.09:1
}
// ── 미리보기 루프: 오브가 위에서 아래로 한 칸씩 깬다
// animation 단축을 쓰면 delay 가 0s 로 리셋된다 → 낱개 속성으로. 지연이 걸리므로 fill-mode: backwards 필수
.fl.is-demo .fl__orb,
.fl.is-demo .fl__label {
animation-duration: $duration; animation-timing-function: $easing;
animation-iteration-count: infinite; animation-fill-mode: backwards;
}
.fl.is-demo .fl__orb { animation-name: flWake; }
.fl.is-demo .fl__label { animation-name: flLabelWake; }
.fl.is-demo .fl__row:nth-child(1) .fl__orb, .fl.is-demo .fl__row:nth-child(1) .fl__label { animation-delay: 0ms; }
.fl.is-demo .fl__row:nth-child(2) .fl__orb, .fl.is-demo .fl__row:nth-child(2) .fl__label { animation-delay: 334ms; }
.fl.is-demo .fl__row:nth-child(3) .fl__orb, .fl.is-demo .fl__row:nth-child(3) .fl__label { animation-delay: 667ms; }
.fl.is-demo .fl__row:nth-child(4) .fl__orb, .fl.is-demo .fl__row:nth-child(4) .fl__label { animation-delay: 1000ms; }
.fl.is-demo .fl__row:nth-child(5) .fl__orb, .fl.is-demo .fl__row:nth-child(5) .fl__label { animation-delay: 1334ms; }
.fl.is-demo .fl__row:nth-child(6) .fl__orb, .fl.is-demo .fl__row:nth-child(6) .fl__label { animation-delay: 1667ms; }
// 밝은 구간은 16%(320ms) 로 끝난다 — 다음 행이 깨는 334ms 보다 짧아야 한 번에 하나만 밝다
@keyframes flWake {
0% { opacity: .32; transform: scale(.86); }
5% { opacity: 1; transform: scale(1.18); }
11% { opacity: 1; transform: scale(1.06); }
16% { opacity: .32; transform: scale(.86); }
100% { opacity: .32; transform: scale(.86); }
}
@keyframes flLabelWake {
0% { opacity: .62; }
5% { opacity: 1; }
11% { opacity: 1; }
16% { opacity: .62; }
100% { opacity: .62; }
}
// ── 320×200 폰 압축 (BD9). 이 안에서는 간격을 올리지 않는다
@media (max-width: 340px), (max-height: 220px) {
.fl__eyebrow { font-size: 8px; }
.fl__ico { width: 10px; height: 10px; }
.fl__title { font-size: 11px; }
.fl__list { margin-top: $sp-2; }
.fl__row { grid-template-columns: 10px 1fr; gap: $sp-2; }
.fl__orb { width: 10px; height: 10px; }
.fl__label { font-size: 9px; }
}
@media (prefers-reduced-motion: reduce) {
.fl.is-demo .fl__orb,
.fl.is-demo .fl__label { animation: none !important; opacity: 1 !important; transform: none !important; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.fl {
width: min(100%, 400px);
}
.fl__eyebrow {
display: flex;
align-items: center;
gap: 4px;
margin: 0;
font-weight: 700;
font-size: clamp(8px, 2.4vw, 10px);
line-height: 1;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.72);
}
.fl__ico {
display: inline-flex;
width: 12px;
height: 12px;
flex: 0 0 auto;
color: #2f6df6;
}
.fl__ico svg {
width: 100%;
height: 100%;
}
.fl__title {
margin: 4px 0 0;
font-weight: 800;
font-size: clamp(13px, 3.6vw, 16px);
line-height: 1.25;
color: #fff7e6;
}
.fl__list {
list-style: none;
margin: 12px 0 0;
padding: 0;
}
.fl__row {
display: grid;
grid-template-columns: 14px 1fr;
align-items: center;
gap: 12px;
padding: 4px 0;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.fl__row:first-child {
border-top: 0;
}
.fl__orb {
width: 14px;
height: 14px;
border-radius: 50%;
box-shadow: 0 0 10px rgba(47, 109, 246, 0.5);
background: radial-gradient(circle at 34% 26%, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0) 44%), radial-gradient(circle at 52% 54%, #2f6df6 0%, rgba(47, 109, 246, 0.45) 58%, rgba(23, 20, 26, 0.92) 100%);
}
.fl__label {
font-weight: 600;
font-size: clamp(10px, 3vw, 12px);
line-height: 1.3;
color: #fff7e6;
}
.fl.is-demo .fl__orb,
.fl.is-demo .fl__label {
animation-duration: 2s;
animation-timing-function: ease-out;
animation-iteration-count: infinite;
animation-fill-mode: backwards;
}
.fl.is-demo .fl__orb {
animation-name: flWake;
}
.fl.is-demo .fl__label {
animation-name: flLabelWake;
}
.fl.is-demo .fl__row:nth-child(1) .fl__orb, .fl.is-demo .fl__row:nth-child(1) .fl__label {
animation-delay: 0ms;
}
.fl.is-demo .fl__row:nth-child(2) .fl__orb, .fl.is-demo .fl__row:nth-child(2) .fl__label {
animation-delay: 334ms;
}
.fl.is-demo .fl__row:nth-child(3) .fl__orb, .fl.is-demo .fl__row:nth-child(3) .fl__label {
animation-delay: 667ms;
}
.fl.is-demo .fl__row:nth-child(4) .fl__orb, .fl.is-demo .fl__row:nth-child(4) .fl__label {
animation-delay: 1000ms;
}
.fl.is-demo .fl__row:nth-child(5) .fl__orb, .fl.is-demo .fl__row:nth-child(5) .fl__label {
animation-delay: 1334ms;
}
.fl.is-demo .fl__row:nth-child(6) .fl__orb, .fl.is-demo .fl__row:nth-child(6) .fl__label {
animation-delay: 1667ms;
}
@keyframes flWake {
0% {
opacity: 0.32;
transform: scale(0.86);
}
5% {
opacity: 1;
transform: scale(1.18);
}
11% {
opacity: 1;
transform: scale(1.06);
}
16% {
opacity: 0.32;
transform: scale(0.86);
}
100% {
opacity: 0.32;
transform: scale(0.86);
}
}
@keyframes flLabelWake {
0% {
opacity: 0.62;
}
5% {
opacity: 1;
}
11% {
opacity: 1;
}
16% {
opacity: 0.62;
}
100% {
opacity: 0.62;
}
}
@media (max-width: 340px), (max-height: 220px) {
.fl__eyebrow {
font-size: 8px;
}
.fl__ico {
width: 10px;
height: 10px;
}
.fl__title {
font-size: 11px;
}
.fl__list {
margin-top: 8px;
}
.fl__row {
grid-template-columns: 10px 1fr;
gap: 8px;
}
.fl__orb {
width: 10px;
height: 10px;
}
.fl__label {
font-size: 9px;
}
}
@media (prefers-reduced-motion: reduce) {
.fl.is-demo .fl__orb,
.fl.is-demo .fl__label {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
}import styles from "./FeatureListOrb.module.scss";
export type FeatureListOrbProps = {
/** 한 바퀴(ms). 행 수로 나눈 값이 행 사이 간격이 된다 */
duration?: number;
easing?: string;
/** 오브 색. 6자리 hex 로 준다 (#2f6df6) */
color?: string;
};
const FEATURES = [
"문서에서 답 찾기",
"회의록 자동 요약",
"표 데이터 정리",
"초안 다시 쓰기",
"말투 맞춰 번역",
"코드 조각 만들기",
];
// 6자리 hex 에 알파 두 자리를 붙인다 — #2f6df6 + "73" 꼴
function withAlpha(hex: string, alpha: number): string {
const n = Math.round(alpha * 255);
return hex + (n < 16 ? "0" : "") + n.toString(16);
}
export default function FeatureListOrb({
duration = 2000,
easing = "ease-out",
color = "#2f6df6",
}: FeatureListOrbProps) {
const orbSkin = {
background:
"radial-gradient(circle at 34% 26%, rgba(255, 255, 255, 0.92) 0%, rgba(255, 255, 255, 0) 44%), " +
"radial-gradient(circle at 52% 54%, " + color + " 0%, " + withAlpha(color, 0.45) + " 58%, rgba(23, 20, 26, 0.92) 100%)",
boxShadow: "0 0 10px " + withAlpha(color, 0.5),
};
// animation 단축을 쓰면 delay 가 0s 로 리셋된다 → 낱개로. 지연이 걸리므로 fill-mode 는 scss 쪽에서 backwards
const loop = (i: number) => ({
animationDuration: duration + "ms",
animationTimingFunction: easing,
animationDelay: Math.round((duration / FEATURES.length) * i) + "ms",
});
return (
<main className={styles.stage}>
<section className={`${styles.fl} ${styles.demoLoop}`}>
<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>
AI 워크스페이스
</p>
<h2 className={styles.title}>차례로 깨어나는 기능 6가지</h2>
<ul className={styles.list}>
{FEATURES.map((feature, i) => (
<li className={styles.row} key={feature}>
<span className={styles.orb} aria-hidden="true" style={{ ...orbSkin, ...loop(i) }} />
<span className={styles.label} style={loop(i)}>{feature}</span>
</li>
))}
</ul>
</section>
</main>
);
}04 차례로 깨는 기능 목록 — 측정 누적면적 4.368% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · ai 랜딩페이지 오브 디자인 키트, 빛나는 섹션 9개
이 데모가 속한 글
ai 랜딩페이지 오브 디자인 키트, 빛나는 섹션 9개ai 랜딩페이지 오브 디자인은 화면 전체를 물들이는 대신 짙은 단색 무대 위에 빛나는 구슬 하나만 올려 제품의 성격을 말하는 방식입니다. 배경 그라디언트 없이도 AI 다워 보인다는 이 계산을 섹션 아홉 개로 잘라 정 이 글의 데모 전부 보기


