04시스템 로그 리스트
목록 각 줄이 타임스탬프 접두어를 달고 있고, 밝은 스캔 바가 위에서 아래로 줄을 하나씩 훑으며 확인 표시를 남깁니다.
쓰임기능 목록, 온보딩 체크리스트
세로 스캔타임스탬프순차 체크

<main class="stage">
<div class="loglist is-demo">
<ul class="loglist__rows">
<li class="loglist__row"><span class="loglist__ts">00:01</span><span class="loglist__text">사용자 인증 확인</span><span class="loglist__ok">OK</span></li>
<li class="loglist__row"><span class="loglist__ts">00:02</span><span class="loglist__text">위치 데이터 동기화</span><span class="loglist__ok">OK</span></li>
<li class="loglist__row"><span class="loglist__ts">00:03</span><span class="loglist__text">이상 패턴 스캔</span><span class="loglist__ok">OK</span></li>
<li class="loglist__row"><span class="loglist__ts">00:04</span><span class="loglist__text">백업 서버 연결</span><span class="loglist__ok">OK</span></li>
</ul>
<p class="loglist__cursorline">READY <span class="loglist__cursor" aria-hidden="true"></span></p>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 스캔 바가 줄 4개를 순서대로 내려가는 한 주기
$easing: $ease-spring; // 스캔 바가 밝아지는 곡선
$color: $stage-yellow; // 호박색 터미널 강조색
@include stage($stage-paper);
.loglist {
box-sizing: border-box;
width: clamp(240px, 84vw, 360px);
padding: $sp-4;
border-radius: $r-card;
background: $subject-ink;
box-shadow: 0 0 0 1px rgba($color, .4), 0 0 24px rgba($color, .16);
// 320px 폰 체크: 로그 4줄 + 커서줄이 129·130 형제 값 그대로면 200px 높이를 넘는다
// (131-04 실측: scrollHeight 223px vs innerHeight 200px). padding·행간을 한 단계씩 좁힌다.
}
.loglist__rows {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: $sp-1;
}
.loglist__row {
position: relative;
display: flex;
align-items: center;
gap: $sp-3;
padding: $sp-1 $sp-3;
border-radius: $r-xs;
animation: rowScan $duration $easing infinite;
}
.loglist__row:nth-child(1) { animation-delay: 0s; }
.loglist__row:nth-child(2) { animation-delay: -.5s; }
.loglist__row:nth-child(3) { animation-delay: -1s; }
.loglist__row:nth-child(4) { animation-delay: -1.5s; }
@keyframes rowScan {
0%, 100% { box-shadow: inset 0 0 0 1px rgba($color, 0), inset 0 0 0 0 rgba($color, 0); }
5% { box-shadow: inset 0 0 0 1px rgba($color, .5), inset 0 0 16px 0 rgba($color, .18); }
15% { box-shadow: inset 0 0 0 1px rgba($color, 0), inset 0 0 0 0 rgba($color, 0); }
}
.loglist__ts {
flex: none;
color: rgba($color, .7);
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
}
.loglist__text {
flex: 1 1 auto;
min-width: 0;
color: $subject-cream;
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12px);
line-height: 1.3;
font-family: inherit;
}
.loglist__ok {
flex: none;
opacity: 0;
color: $color;
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: .06em;
animation: rowOk $duration $easing infinite;
}
.loglist__row:nth-child(1) .loglist__ok { animation-delay: 0s; }
.loglist__row:nth-child(2) .loglist__ok { animation-delay: -.5s; }
.loglist__row:nth-child(3) .loglist__ok { animation-delay: -1s; }
.loglist__row:nth-child(4) .loglist__ok { animation-delay: -1.5s; }
@keyframes rowOk {
0%, 8% { opacity: 0; }
14%, 100% { opacity: 1; }
}
.loglist__cursorline {
margin: $sp-2 0 0;
color: rgba($color, .7);
font-family: "Courier New", monospace;
font-weight: 400;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: .04em;
}
.loglist__cursor {
display: inline-block;
width: 6px;
height: 10px;
background: $color;
vertical-align: -1px;
animation: blink $dur-loop steps(1) infinite;
}
@keyframes blink {
0%, 49% { opacity: 1; }
50%, 100% { opacity: .2; }
}
@media (prefers-reduced-motion: reduce) {
.loglist__row, .loglist__ok, .loglist__cursor {
animation: none !important;
}
.loglist__row { box-shadow: inset 0 0 0 1px rgba($color, 0); }
.loglist__ok { opacity: 1; }
.loglist__cursor { opacity: 1; }
}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);
}
.loglist {
box-sizing: border-box;
width: clamp(240px, 84vw, 360px);
padding: 16px;
border-radius: 16px;
background: #17141a;
box-shadow: 0 0 0 1px rgba(255, 210, 63, 0.4), 0 0 24px rgba(255, 210, 63, 0.16);
}
.loglist__rows {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
gap: 4px;
}
.loglist__row {
position: relative;
display: flex;
align-items: center;
gap: 12px;
padding: 4px 12px;
border-radius: 4px;
animation: rowScan 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.loglist__row:nth-child(1) {
animation-delay: 0s;
}
.loglist__row:nth-child(2) {
animation-delay: -0.5s;
}
.loglist__row:nth-child(3) {
animation-delay: -1s;
}
.loglist__row:nth-child(4) {
animation-delay: -1.5s;
}
@keyframes rowScan {
0%, 100% {
box-shadow: inset 0 0 0 1px rgba(255, 210, 63, 0), inset 0 0 0 0 rgba(255, 210, 63, 0);
}
5% {
box-shadow: inset 0 0 0 1px rgba(255, 210, 63, 0.5), inset 0 0 16px 0 rgba(255, 210, 63, 0.18);
}
15% {
box-shadow: inset 0 0 0 1px rgba(255, 210, 63, 0), inset 0 0 0 0 rgba(255, 210, 63, 0);
}
}
.loglist__ts {
flex: none;
color: rgba(255, 210, 63, 0.7);
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
}
.loglist__text {
flex: 1 1 auto;
min-width: 0;
color: #fff7e6;
font-weight: 600;
font-size: clamp(11px, 2.6vw, 12px);
line-height: 1.3;
font-family: inherit;
}
.loglist__ok {
flex: none;
opacity: 0;
color: #ffd23f;
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: 0.06em;
animation: rowOk 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.loglist__row:nth-child(1) .loglist__ok {
animation-delay: 0s;
}
.loglist__row:nth-child(2) .loglist__ok {
animation-delay: -0.5s;
}
.loglist__row:nth-child(3) .loglist__ok {
animation-delay: -1s;
}
.loglist__row:nth-child(4) .loglist__ok {
animation-delay: -1.5s;
}
@keyframes rowOk {
0%, 8% {
opacity: 0;
}
14%, 100% {
opacity: 1;
}
}
.loglist__cursorline {
margin: 8px 0 0;
color: rgba(255, 210, 63, 0.7);
font-family: "Courier New", monospace;
font-weight: 400;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: 0.04em;
}
.loglist__cursor {
display: inline-block;
width: 6px;
height: 10px;
background: #ffd23f;
vertical-align: -1px;
animation: blink 2s steps(1) infinite;
}
@keyframes blink {
0%, 49% {
opacity: 1;
}
50%, 100% {
opacity: 0.2;
}
}
@media (prefers-reduced-motion: reduce) {
.loglist__row, .loglist__ok, .loglist__cursor {
animation: none !important;
}
.loglist__row {
box-shadow: inset 0 0 0 1px rgba(255, 210, 63, 0);
}
.loglist__ok {
opacity: 1;
}
.loglist__cursor {
opacity: 1;
}
}import type { CSSProperties } from "react";
import styles from "./FeatureListHud.module.scss";
export type FeatureListHudRow = { ts: string; text: string };
export type FeatureListHudProps = {
rows?: FeatureListHudRow[];
ok?: string;
ready?: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
const defaultRows: FeatureListHudRow[] = [
{ ts: "00:01", text: "사용자 인증 확인" },
{ ts: "00:02", text: "위치 데이터 동기화" },
{ ts: "00:03", text: "이상 패턴 스캔" },
{ ts: "00:04", text: "백업 서버 연결" },
];
export default function FeatureListHud({
rows = defaultRows,
ok = "OK",
ready = "READY",
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#ffd23f",
}: FeatureListHudProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<div className={styles.list} style={vars}>
<ul className={styles.rows}>
{rows.map((row) => (
<li key={row.ts} className={styles.row}>
<span className={styles.ts}>{row.ts}</span>
<span className={styles.text}>{row.text}</span>
<span className={styles.ok}>{ok}</span>
</li>
))}
</ul>
<p className={styles.cursorline}>
{ready} <span className={styles.cursor} aria-hidden="true" />
</p>
</div>
);
}04 시스템 로그 리스트 — 측정 누적면적 9.292% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · HUD 디자인, 화면이 나를 겨누는 랜딩 부품 9개
이 데모가 속한 글
HUD 디자인, 화면이 나를 겨누는 랜딩 부품 9개HUD 디자인은 게임이나 SF 영화 속 조준경·레이더처럼, 화면 위에 좌표·상태 수치를 얹어 마치 나를 지켜보고 있는 듯한 인상을 주는 인터페이스 스타일입니다. 이 편은 그 감시 카메라 감성을 실제로 돌아가는 랜딩 이 글의 데모 전부 보기


