04Monospace label list
Each feature gets a monospace number label up front, and list rows sweep in from below one after another.
Best forFeature lists, changelog entries
ui-monospace 라벨stagger 등장translateY

<main class="stage">
<ul class="list is-demo">
<li class="list__row list__row--1"><span class="list__num">01</span><span class="list__text">Real-time collaborative editing</span></li>
<li class="list__row list__row--2"><span class="list__num">02</span><span class="list__text">Keyboard-only command bar</span></li>
<li class="list__row list__row--3"><span class="list__num">03</span><span class="list__text">Per-version changelog</span></li>
</ul>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 줄 하나가 스치듯 나타났다 사라지는 한 주기
$easing: $ease-out;
$color: $subject-blue;
@include stage($stage-paper);
.list {
list-style: none;
margin: 0;
padding: $sp-4;
display: flex;
flex-direction: column;
gap: $sp-2;
width: clamp(220px, 76vw, 320px);
background: $subject-ink;
border-radius: $r-card;
box-shadow: $shadow-raised;
}
.list__row {
display: flex;
align-items: center;
gap: $sp-4;
padding: $sp-3 $sp-4;
border-radius: $r-sm;
// 데모 루프는 .is-demo 안에서만 — 정적(실사용) 상태는 opacity 1 이라야 글자 대비가 산다
.list.is-demo & { animation-name: rowSweep; }
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
.list__row--1 { animation-delay: 0s; }
.list__row--2 { animation-delay: $duration * -0.67; }
.list__row--3 { animation-delay: $duration * -0.34; }
.list__num {
font: 700 clamp(11px, 2.8vw, 13px)/1 "Courier New", ui-monospace, monospace;
color: $subject-blue-light; // 먹 면 위 파란 글자 — AA 4.5 (blog-design 글자 대비)
}
.list__text {
color: $subject-cream;
font: 600 clamp(11px, 2.8vw, 13px)/1.3 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes rowSweep {
0%, 62%, 100% { opacity: .55; background: transparent; transform: translateY(0); }
17% { opacity: 1; background: rgba($color, .12); transform: translateY(-2px); }
}
@media (prefers-reduced-motion: reduce) {
.list__row { animation: none !important; opacity: .85; background: transparent; transform: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.list {
list-style: none;
margin: 0;
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
width: clamp(220px, 76vw, 320px);
background: #17141a;
border-radius: 16px;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.list__row {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 16px;
border-radius: 8px;
}
.list.is-demo .list__row {
animation-name: rowSweep;
}
.list__row {
animation-duration: 2s;
animation-timing-function: ease-out;
animation-iteration-count: infinite;
}
.list__row--1 {
animation-delay: 0s;
}
.list__row--2 {
animation-delay: -1.34s;
}
.list__row--3 {
animation-delay: -0.68s;
}
.list__num {
font: 700 clamp(11px, 2.8vw, 13px)/1 "Courier New", ui-monospace, monospace;
color: #5b8cf8;
}
.list__text {
color: #fff7e6;
font: 600 clamp(11px, 2.8vw, 13px)/1.3 -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
}
@keyframes rowSweep {
0%, 62%, 100% {
opacity: 0.55;
background: transparent;
transform: translateY(0);
}
17% {
opacity: 1;
background: rgba(47, 109, 246, 0.12);
transform: translateY(-2px);
}
}
@media (prefers-reduced-motion: reduce) {
.list__row {
animation: none !important;
opacity: 0.85;
background: transparent;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./FeatureListLinear.module.scss";
export type FeatureListLinearProps = {
rows?: [string, string, string];
/** 줄 하나가 스치듯 나타났다 사라지는 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function FeatureListLinear({
rows = ["실시간 협업 편집", "키보드 전용 커맨드바", "버전별 변경 로그"],
duration = 2000,
easing = "ease-out",
color = "#2f6df6",
}: FeatureListLinearProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
const delays = ["0s", `${-duration * 0.67}ms`, `${-duration * 0.34}ms`];
return (
<ul className={styles.list} style={vars}>
{rows.map((text, i) => (
<li key={text} className={styles.row} style={{ animationDelay: delays[i] }}>
<span className={styles.num}>{String(i + 1).padStart(2, "0")}</span>
<span className={styles.text}>{text}</span>
</li>
))}
</ul>
);
}04 Monospace label list — Measured area 27.006% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · Linear Style Landing Page: 9 Dark SaaS Sections
The post this demo lives in
Linear Style Landing Page: 9 Dark SaaS SectionsA linear style landing page — all over Dribbble and Pinterest right now — leans on Linear.app's ink-black background and See all demos in this post


