생성된 문서 · 직접 고치지 않는다
값은 src/index.css에서 읽어 만든다. 셸·컴포넌트는 구현 문서에 있다.
이렇게
<div className="bg-surface text-fg-muted
border-border rounded-lg">
이렇게 말고
<div className="bg-[#fff] text-[#5a616b]"
style={{ color: '#5a616b' }}>
하드코딩한 색은 npm run check:design이 막는다. 크기·간격에 임의값(h-[46px])을 쓰는 것은 막지 않지만, 같은 값이 세 번 반복되면 토큰으로 올린다.
쓸 토큰이 없으면 임의 색을 만들지 말고 먼저 묻는다 — 대개 이미 있는 의미에 이름을 잘못 찾고 있는 경우다.
값이 조정된 토큰 4개는 카드에 ✎ 표시가 있다
무엇이 무엇 위에 얹히는지를 정한다
canvas
#f4f5f7
페이지 배경. 카드가 얹히는 바닥이다.
예 · 매니저 화면의 콘텐츠 영역
surface
#ffffff
카드·패널·모달의 면. 글자가 얹히는 기본 배경이다.
예 · 표 카드, 인증 화면의 폼 쪽
surface-2
#fafbfc
같은 카드 안에서 한 단계 눌러야 하는 면.
예 · 표 헤더 행, 기수 선택기 배경
border
#e2e5ea
기본 경계선. 면을 나눌 때 쓴다.
예 · 카드 테두리, 표의 행 구분선
border-strong
#cfd4dc
입력 요소처럼 경계가 또렷해야 하는 곳.
예 · 텍스트 입력, 셀렉트 테두리
배경이 왜 흰색이 아니라 회색인가
카드가 얹히는 바닥과 카드 자체를 구분하기 위해서다. 둘 다 흰색이면 카드 경계를 테두리에만 의존하게 되고, 데이터 밀도가 높은 표 화면에서 구조가 읽히지 않는다. 다만 이 차이 때문에 글자 대비를 검사할 때 두 배경을 모두 확인해야 한다.
surface와 surface-2를 왜 나눴나
표 헤더처럼 "같은 카드 안에서 한 단계 눌러야 하는 면"이 반복해서 필요했다. 그때마다 회색을 즉석에서 고르면 화면마다 다른 회색이 생긴다.
이름이 곧 역할이다. 번호가 아니다
fg
#181b20
본문과 제목. 읽으라고 놓은 글자의 기본값이다.
예 · 표 셀 값, 화면 제목
fg-muted
#5a616b
본문보다 한 단계 낮은 보조 설명.
예 · 입력 라벨, 화면 부제, 사용자 이름
와이어 실측: 이 색을 쓰는 CSS 규칙 236개 중 22%만 작은 글씨와 함께 쓰였다. 즉 본문 크기로도 쓰이는 보조 텍스트다.
fg-subtle✎
#686f7d
캡션·메타 정보. 그리고 비활성·보류 상태를 색 없이 표현할 때.
예 · 측정일, 총개수, breadcrumb, 미도래 판정
SSOT의 #8b929c는 페이지 배경에서 2.88:1로 AA에 크게 못 미쳤다. 남은 시간·응시 기간처럼 판단에 쓰이는 정보에 사용되므로 장식 예외가 적용되지 않아 값을 올렸다. 와이어 실측상 이 색을 쓰는 규칙의 65%가 작은 글씨와 함께 쓰인다.
왜 text가 아니라 fg인가
Tailwind에서 글자색 유틸리티의 접두사가 이미 text-다. 토큰 이름까지 text로 하면 text-text-2가 되어 같은 말이 두 번 나온다. fg(foreground)는 배경(bg)과 짝이 맞는 표준 어휘이고, text-fg-muted처럼 읽힌다.
왜 1·2·3 번호가 아니라 muted·subtle인가
번호는 "무엇에 쓰는 색인지"를 말해주지 않는다. 실제로 와이어에서 사용처를 세어보니 셋째 단계는 65%가 작은 글씨와 함께 쓰이는 캡션·메타용이었다. 역할이 이름이 되면 문서를 안 봐도 어디 쓸지 짐작할 수 있다.
비활성·보류는 무슨 색인가
fg-subtle이다. 회색이지 의미 색이 아니다. 비활성을 danger로 칠하면 "위험"과 "지금은 해당 없음"이 같은 색이 되어 화면이 거짓말을 한다.
액센트는 하나뿐이다
primary
#3a4db8
주 액션과 선택 상태, 그리고 링크.
예 · 로그인 버튼, 선택된 탭, 텍스트 링크
절제된 단일 액센트다. 강조하고 싶다고 의미 색(danger·warning)을 쓰지 않는다 — 그건 상태를 뜻한다.
primary-hover
#31429e
주 액션 위에 커서가 올라갔을 때.
예 · 버튼 hover
primary-soft
#eef0fb
선택되었음을 배경으로 알릴 때.
예 · 선택된 표 행, 아바타 배경
primary-border
#cdd4f2
primary-soft 면의 테두리.
예 · 선택된 카드의 윤곽
brand-1
#31429e
인증 화면 좌측 패널 그라디언트의 시작색.
예 · 로그인·회원가입·계정 활성화 화면
인증 화면 전용이다. 앱 내부 화면에는 이 그라디언트를 쓰지 않는다.
brand-2
#232a4d
같은 그라디언트의 끝색.
예 · 위와 동일
액센트가 왜 하나뿐인가
이 제품의 디자인 방향이 "조용한 뉴트럴 베이스 + 절제된 단일 액센트"다. 상태는 의미 색으로만 말한다. 액센트가 여럿이면 무엇이 주 액션인지 흐려진다.
강조하고 싶은데 primary가 약해 보이면
의미 색을 강조 용도로 쓰지 않는다. danger는 빨간색이 아니라 "위험"이라는 뜻이고, 강조 목적으로 쓰면 사용자가 위험 신호를 무시하기 시작한다. 크기·굵기·배치로 강조한다.
같은 의미 = 항상 같은 색. 화면에서 임의 색을 신설하지 않는다
danger
#c0392b
위험·차단·에러. 그리고 하락 시그널.
예 · 인증 실패 문구, 권한 차단 안내, 비활성 계정
danger-soft
#fbeceb
위험 알림의 배경면.
예 · 폼 상단 에러 알림
danger-border
#f0cdc9
위험 알림의 테두리.
예 · 동일
warning✎
#96631a
주의·표본 부족·미검증·일시 잠금. 아직 판단할 수 없거나 조건부인 상태.
예 · 계정 잠금 안내, 미검증 이메일 배너, 표본 부족 표시
SSOT의 #b3771a는 3.77:1로 AA 미달이었다. 경고 문구가 가장 안 읽히는 우선순위 역전이 생겨 값을 어둡게 올렸다. 배경색(warning-soft)은 그대로다.
warning-soft
#fbf3e6
주의 알림의 배경면.
예 · 잠금 안내 박스
warning-border
#e6d3a8
주의 알림의 테두리.
예 · 동일
success✎
#1a7d52
정상·활성·회복·승인.
예 · 활성 계정 배지, 회복 판정, 승인된 교안
SSOT의 #1f8a5b는 4.33:1로, AA 기준에서 0.17 모자랐다. 눈으로는 구분할 수 없는 차이여서 앞선 감사에서도 지적되지 않았고 계산해서 발견했다.
success-soft
#e8f5ee
정상·완료 알림의 배경면.
예 · 완료 안내
success-border
#b7dfc9
정상·완료 알림의 테두리.
예 · 동일
info
#2f6bb0
정보·안내·온디맨드. 사용자가 다음 행동을 고를 수 있는 안내.
예 · 인증 메일 재발송 안내, 근거 패널
info-soft
#eaf2fa
정보 알림의 배경면.
예 · 재발송 안내 박스
info-border
#c5daf0
정보 알림의 테두리.
예 · 동일
neutral-soft
#e0e3ea
의미가 없는 회색 면. 상태를 뜻하지 않는 구분에 쓴다.
예 · 비활성 토글 트랙, 중립 태그
같은 의미에 항상 같은 색을 쓰는 이유
사용자가 색을 학습하게 하려는 것이다. 한 화면에서 회복이 초록이었는데 다른 화면에서 파랑이면, 색은 아무 정보도 주지 못하는 장식이 된다. 새 상태가 생기면 임의 색을 만들지 말고 SSOT 매핑표에 추가한다.
색만으로 상태를 표현해도 되나
안 된다. 색을 구분하기 어려운 사용자에게 정보가 전달되지 않는다. 이 제품의 와이어는 이미 색 옆에 텍스트를 함께 둔다(진행도 옆 2/4, 판정 옆 견고·부분·표면). 그 규칙을 유지한다.
soft와 border는 언제 쓰나
알림·배지처럼 면이 필요한 경우에 셋이 한 벌로 움직인다 — 글자는 진한 색, 배경은 soft, 테두리는 border. 셋을 섞어 쓰면 대비가 무너진다.
밝은 배경용 색은 어두운 면에서 통하지 않는다
nav
#1e2436
좌측 네비게이션 바탕.
예 · 매니저·슈퍼어드민 셸의 사이드바
nav-active
#2a3147
현재 보고 있는 메뉴 항목의 배경.
예 · 선택된 nav 항목
nav-fg
#aeb4c6
어두운 네비 위의 글자.
예 · 선택되지 않은 메뉴 이름
nav-accent✎
#d9a441
어두운 면 위의 강조.
예 · 총괄 전용 메뉴에 붙는 배지
밝은 배경용 warning을 그대로 쓰면 어두운 네비 위에서 3.01:1로 미달한다. 밝은 쪽을 위해 어둡게 만든 색이기 때문이다. 어두운 면에는 별도 색이 필요하다.
네비만 왜 색을 따로 갖나
어두운 배경 위에서는 밝은 배경용 색이 그대로 통하지 않는다. 밝은 쪽에서 읽히게 하려고 어둡게 만든 색은 어두운 배경에서 오히려 안 읽힌다. 실제로 총괄 배지에 밝은 배경용 warning을 썼다가 3.01:1로 미달한 것을 계산해서 발견했다.
다크 모드와는 다른가
다르다. 이건 특정 영역(사이드바)이 어두운 것이고, 다크 모드는 전체 테마가 뒤집히는 것이다. 다크 모드 요구는 현재 명세에 없다.
위 분류에 속하지 않는 것. 우리가 정의한 토큰이 여기 나타나면 분류가 빠진 것이다.
reach-0
#df6150
reach-1
#ec9a5c
reach-2
#ecc95e
reach-3
#a9cf8e
reach-4
#57b083
reach-fg
#241108
reach-na-bg
#eceef1
code
#1e2436
code-fg
#d6dae6
code-dim
#8992a6
데이터 밀도가 높은 운영 도구라 기준이 14px다(웹 기본 16px 아님)
text-2xs11px다람쥐 헌 쳇바퀴에 타고파text-xs12px다람쥐 헌 쳇바퀴에 타고파text-sm13px다람쥐 헌 쳇바퀴에 타고파text-base14px다람쥐 헌 쳇바퀴에 타고파text-lg16px다람쥐 헌 쳇바퀴에 타고파text-xl20px다람쥐 헌 쳇바퀴에 타고파text-2xl26px다람쥐 헌 쳇바퀴에 타고파text-3xl32px다람쥐 헌 쳇바퀴에 타고파왜 기본이 16px이 아닌가
한 화면에 표·지표·목록이 함께 놓이는 운영 도구다. 16px 기준으로 잡으면 표가 한 화면에 안 들어와 스크롤이 늘고, 그러면 비교가 어려워진다. 대신 대비를 AA로 유지해 작아도 읽히게 했다.
2xs·3xl은 어디 쓰나
2xs(11px)는 확정 화면 실측에서 가장 많이 쓰인 크기(148회)라 뒤늦게 승격했다 — 배지·메타·표의 보조 정보. 3xl은 인증 화면의 마케팅 헤드라인 전용이다.
font-sans다람쥐 Sphinx 0123
font-mono다람쥐 Sphinx 0123
왜 웹폰트를 안 쓰나
한글 웹폰트는 용량이 크고, 라틴 전용 폰트를 넣어봐야 한글은 결국 시스템 폰트로 폴백된다. 얻는 것보다 첫 화면이 느려지는 비용이 크다고 봤다.
rounded-sm · 6pxrounded-md · 10pxrounded-lg · 14pxrounded-xl · 18pxshadow-card그림자가 왜 하나뿐인가
이 제품에서 떠 있어야 하는 것은 카드뿐이다. 모달은 배경을 덮어 구분되고, 나머지는 테두리로 나뉜다. 그림자 단계를 여럿 두면 "얼마나 떠 있나"를 매번 고민하게 된다.