본문으로 건너뛰기
구현 데모
공고 화면 2-1공통 디자인 기준

색·여백·글자·컴포넌트를 정의한 기준표

이 데모의 화면 네 개(목록 · 상세 · 참여 신청 · 개최자 관리)는 전부 아래 토큰에서 나왔습니다. 표에 적힌 값은 문서용으로 옮겨 적은 것이 아니라 지금 이 페이지가 CSS 에서 읽어 온 계산값입니다. 상단 바(작은 화면에서는 화면 아래)의 Variables 스위치를 누르면 이 표의 hex 와 px 도 같이 바뀝니다.

현재 모드density = · colorway =
01

토큰은 3계층이다

이 계층 구조가 그대로 Figma Variables 컬렉션 구조다. 컴포넌트는 semantic 이름만 부른다 — primitive 를 직접 부르면 모드 전환이 그 자리에서 깨진다.

1PRIMITIVE원시값--violet-500: 컬렉션 primitive · 모드 없음

hex·px 리터럴이 존재하는 유일한 층. 컴포넌트가 직접 부르지 않는다

2SEMANTIC역할 이름--color-accent: var(--violet-500)컬렉션 semantic · 모드가 붙는 층

값은 primitive 참조뿐. 모드를 바꾸면 여기만 다시 계산된다

3COMPONENT컴포넌트 로컬--table-row-h: 컴포넌트 로컬 변수

한 컴포넌트 안에서만 쓰는 치수. semantic 만 참조한다

02

보라를 쓰되 그라데이션은 쓰지 않는다. 단색 면·테두리·글자로만 쓰고, 액센트는 한 화면에 한 곳에서만 강하다. 중립축은 순수 회색이 아니라 바이올렛을 4% 섞은 색이라 지면 전체가 한 계열로 묶인다.

violet팬덤 톤의 주축. 액센트·틴트·테두리가 전부 여기서 나온다
violet-50
violet-100
violet-200
violet-500
violet-600
violet-700
neon형광 마젠타. 마감임박·잔여 1자리에만 쓴다
neon-50
neon-400
neon-500
neon-700
ink중립축. 순수 회색이 아니라 바이올렛을 ~4% 섞었다
ink-900
ink-700
ink-500
ink-400
ink-200
ink-100
canvas
white
mint / mosscolorway 모드 B. 같은 방법으로 hue 만 치환했다
mint-50
mint-200
mint-500
mint-600
mint-700
moss-900
moss-500
moss-200
canvas-mint
status신호색. 각 색마다 면(-50)과 글자(-700)를 짝으로 둔다
green-500
green-50
green-700
amber-500
amber-50
amber-700
red-500
red-50
red-700
Figma 변수CSS 변수현재 값모드용도
color/canvas--color-canvascolorway지면. 순백을 쓰지 않는다
color/surface/card--color-surface-cardcolorway카드·시트 면
color/surface/sunken--color-surface-sunkencolorway표 헤더·눌린 면
color/text/default--color-textcolorway본문·제목
color/text/muted--color-text-mutedcolorway보조 설명·레이블
color/text/subtle--color-text-subtlecolorway비본문 힌트
color/line/default--color-linecolorway카드·표 경계선
color/accent/default--color-accentcolorway주 CTA. 화면당 한 곳에만 강하게
color/accent/hover--color-accent-hovercolorway액센트 호버
color/accent/weak--color-accent-weakcolorway액센트 틴트 면
color/accent/text--color-accent-textcolorway밝은 면 위 액센트 글자
color/accent/on--color-accent-oncolorway액센트 면 위 글자. 모드에 따라 흰색↔먹색으로 뒤집힌다
color/hot/default--color-hot고정긴급 신호 전용
color/success/default--color-success고정입금확인·발송완료
color/warning/default--color-warning고정입금대기
color/danger/default--color-danger고정취소·마감
03

여백

4·8·12·16·20·24·32·40·48·64 열 단계. 이 밖의 값은 쓰지 않으며, scripts/check-tokens.mjs 가 코드에서 벗어난 여백을 잡아낸다.

space/1
space/2
space/3
space/4
space/5
space/6
space/8
space/10
space/12
space/16
Figma 변수CSS 변수현재 값모드용도
space/gutter--spacing-gutterdensity지면 좌우·섹션 내부 여백
space/section--spacing-sectiondensity섹션 사이
space/card-gap--spacing-cardgapdensity카드 그리드 gap
space/stack--spacing-stackdensity한 덩어리 안 요소 간
04

글자

한글·본문은 Pretendard Variable 을 self-host 한다. 금액·계좌번호·D-day·수량은 전부 JetBrains Mono 에 tabular-nums 를 걸어 자릿수가 흔들리지 않게 한다. 자간 -0.02em·행간 1.6 은 한글 본문 기준이다.

type/display분철 자리 신청하기
type/title분철 자리 신청하기
type/subtitle분철 자리 신청하기
type/body분철 자리 신청하기
type/label분철 자리 신청하기
type/caption분철 자리 신청하기
type/mono/lg14,500원 · 3020-77-441829
type/mono/md14,500원 · 3020-77-441829
type/mono/sm14,500원 · 3020-77-441829
05

모서리와 그림자

모서리는 컨트롤·카드 2단계에 pill 을 더한 셋. 그림자는 2단계뿐이고 검정이 아니라 중립축 hue 를 섞은 색이라 지면과 같은 계열로 떨어진다.

control
card
shadow/sm기본 카드
shadow/md떠 있는 면
Figma 변수CSS 변수현재 값모드용도
radius/control--radius-controldensity버튼·인풋
radius/card--radius-carddensity카드·시트
radius/pill--radius-pill고정배지·칩
shadow/sm--shadow-smcolorway기본 카드
shadow/md--shadow-mdcolorway떠 있는 면·호버
06

컴포넌트 층 토큰

컴포넌트 안에서만 쓰는 치수다. 개최자 관리 표가 밀도 모드에서 눈에 띄게 조여드는 이유가 여기 있다. 단 --control-h 는 모드가 바뀌어도 44px 아래로 내려가지 않는다 — 터치 타겟은 밀도가 넘지 않는 하한선이다.

Figma 변수CSS 변수현재 값모드용도
control/height--control-h고정버튼·인풋 높이 = 터치 타겟 하한
card/padding--card-paddensity카드 안 여백
slot-chip/height--slot-chip-hdensity자리 칩 높이
slot-chip/radius--slot-chip-radiusdensity자리 칩 모서리
table/row-height--table-row-hdensity개최자 표 행 높이
table/cell-x--table-cell-pxdensity표 셀 좌우 패딩
table/cell-y--table-cell-pydensity표 셀 상하 패딩
table/font-size--table-fontdensity표 글자 크기
progress/height--progress-h고정진행률 바 두께
07

컴포넌트 인벤토리 — 중첩 2단계

L1 13종 · L2 8종. L2 는 L1 만 담고, 페이지가 L2 를 배치하면 거기서 끝난다. 아래 목록은 손으로 적은 것이 아니라 scripts/check-nesting.mjs 가 소스를 훑어 만든 결과다.

L1 · 원자 (중첩 0)
모집중잔여 1자리입금확인14,500D-1· 17시간
L1 (중첩 0)역할
AlbumArt앨범 아트를 SVG 로 그린다. 색을 토큰에서 읽어 모드를 따라간다
Amount금액·수량. 모노 + tabular-nums 로 자릿수가 흔들리지 않는다
Avatar개최자 식별. 이니셜만 — 사진 파일을 쓰지 않는다
Badge상태 표기 — 분철 상태 · 자리 상태 · 신청 상태 전부 이 하나로 덮는다
Button주/보조/톤/고스트 4변주. 높이는 터치 타겟 토큰
Checkbox표 선택. 시각 20px, 히트 영역 44px
Chip필터·탭. 시각 높이 36px, 히트 영역 44px
CountdownPillD-day. 렌더 시각 기준으로 매번 다시 계산된다
Field입력. 레이블·힌트·접미 슬롯 포함
ProgressBar모집 진행률. width 가 아니라 scaleX 로 움직인다
Segmented세그먼트 컨트롤. 정렬·모드 전환에 쓴다
Skeleton로딩. 결과 레이아웃을 닮은 형태로만 쓴다
TokenPing모드 전환 시 바뀐 토큰명을 그 자리에 띄운다
L2 (중첩 1)담고 있는 L1
AccountBoxBadge Button
AppHeaderBadge
EmptyStateButton
SlotGridAmount Badge
SplitCardAlbumArt Amount Avatar Badge CountdownPill ProgressBar
StatRowBadge Amount
TableRowBadge Checkbox
VariablesSwitchSegmented TokenPing
깊이가 필요해 보일 때의 판단

상단 바(AppHeader)와 Variables 스위치는 둘 다 L2 다. 상단 바 안에 스위치를 넣으면 그 순간 L2 안에 L2 가 생겨 중첩 3단계가 된다. 그래서 컴포넌트로 묶지 않고 레이아웃에서 형제로 나란히 펼쳤다. 중첩을 지키는 일은 규칙을 외우는 게 아니라 이런 자리에서 매번 내리는 판단이고, 아래 스크립트가 그 판단이 무너졌는지를 대신 본다.

08

Figma Variables 컬렉션 매핑

이 데모의 CSS 계층을 그대로 Figma 파일 구조로 옮긴 표다. 실제 납품 파일도 이 세 컬렉션으로 만든다.

컬렉션모드담는 변수규칙
primitive없음 (단일 모드)violet · neon · ink · mint · status · space · radius · size원시값만. 컴포넌트가 직접 부르지 않는다
semanticdensity: default / compact · colorway: violet / mintcolor/* · space/* · type/* · radius/* · shadow/*값은 primitive 참조만. 모드가 붙는 유일한 컬렉션
component (로컬)semantic 을 따라간다control/* · card/* · slot-chip/* · table/* · progress/*컴포넌트 안에서만 쓰는 치수. semantic 만 참조
09

규칙을 사람이 지키지 않고 스크립트가 지킨다

세 검사는 pnpm build 앞에 붙어 있어서, 규칙이 깨진 코드는 빌드가 되지 않는다. 아래는 pnpm check:nesting 의 실제 출력이다.

pnpm check:nestingL2 가 L2 를 담으면 실패. 위 인벤토리 표가 이 스크립트의 출력이다
pnpm check:tokens기본 팔레트 호출·그라데이션·허용 밖 여백·hex 리터럴을 잡는다
pnpm check:contrastglobals.css 를 파싱해 두 컬러웨이의 명도 대비를 WCAG 로 계산한다

  컴포넌트 중첩 검사 — 2단계 이내 (공고 산출물 조건 ③)
  ───────────────────────────────────────────────────────────────────
  파일                               레벨  중첩  담고 있는 L1
  ───────────────────────────────────────────────────────────────────
  components/l1/AlbumArt.tsx         L1    0     —
  components/l1/Amount.tsx           L1    0     —
  components/l1/Avatar.tsx           L1    0     —
  components/l1/Badge.tsx            L1    0     —
  components/l1/Button.tsx           L1    0     —
  components/l1/Checkbox.tsx         L1    0     —
  components/l1/Chip.tsx             L1    0     —
  components/l1/CountdownPill.tsx    L1    0     —
  components/l1/Field.tsx            L1    0     —
  components/l1/ProgressBar.tsx      L1    0     —
  components/l1/Segmented.tsx        L1    0     —
  components/l1/Skeleton.tsx         L1    0     —
  components/l1/TokenPing.tsx        L1    0     —
  components/l2/AccountBox.tsx       L2    1     Badge Button
  components/l2/AppHeader.tsx        L2    1     Badge
  components/l2/EmptyState.tsx       L2    1     Button
  components/l2/SlotGrid.tsx         L2    1     Amount Badge
  components/l2/SplitCard.tsx        L2    1     AlbumArt Amount Avatar Badge CountdownPill ProgressBar
  components/l2/StatRow.tsx          L2    1     Badge Amount
  components/l2/TableRow.tsx         L2    1     Badge Checkbox
  components/l2/VariablesSwitch.tsx  L2    1     Segmented TokenPing
  ───────────────────────────────────────────────────────────────────
  L1 13개 · L2 8개 · 최대 중첩 1 (페이지 배치까지 2단계)

  ✓ 통과 — L2 가 L2 를 담은 곳이 없습니다.
10

이 기준으로 만든 화면

공고 §2 의 화면 번호를 그대로 따랐다. 각 화면이 어떤 밀도 프리셋을 쓰는지가 다르다 — 같은 토큰 위에서 밀도만 달리 잡았다.

공고 화면경로우선 뷰포트밀도
2-1 공통 디자인 기준/tokensPC문서 밀도
2-2 분철 목록/모바일 우선커머스 저밀도 — 이미지 우선, 카드 gap 16px
2-3 분철 상세/split/[id]모바일 우선커머스 저밀도 + 고정 CTA
2-4 참여 신청·입금/split/[id]/join모바일 우선단일 열, 금액이 최상위 위계
2-5 개최자 관리/hostPC 비중 높음고밀도 — 행 38px, 셀 10/12px, 13px

화면 다섯은 전부 위 토큰만 부른다. 새 화면을 붙일 때도 새 색·새 여백을 만들지 않는다.