색과 글자의 체계
지후가 행사 일정 페이지의 코드에서 "초록"을 검색했다. #2f7d5b, #2e7d5a, #307c5b… 눈으로는 구별되지 않는 초록이 여섯 가지 나왔고, 글자 크기는 17px, 1.0625rem, 1.1em이 뒤섞여 있었다. 다크 모드를 넣어 보자며 모든 색의 명도를 뒤집었더니 책방 초록은 검은 바탕에 가라앉아 탁해졌고, 카드의 그림자는 아예 사라졌다. 색과 글자 크기를 하나하나 고르는 일은 ColorBook과 TypeBook에서 이미 했다. 이 장은 그 결정들을 한 곳에 이름 붙여 두고, 한 곳을 바꾸면 화면 전체가 따라 바뀌게 만드는 체계를 다룬다.
- 디자인 토큰이 무엇이고, 원시·의미·컴포넌트 토큰의 세 층이 각각 무엇을 맡는지 설명한다.
- 토큰 이름을 색이 아니라 쓰임으로 짓고, 부품은 의미 토큰만 보게 하는 규칙을 세운다.
- 다크 모드가 명도 뒤집기나 색 반전 필터로 되지 않는 까닭을 대비와 높이(그림자) 표현으로 보이고, 어두운 화면의 값을 따로 정한다.
- 글자색과 바탕의 짝을 토큰으로 정해 두고 그 짝의 대비만 검사하는 방법을 쓴다.
- px·rem·em의 차이를 사용자 글자 크기 설정으로 확인하고, 글자 크기 단계를 rem 토큰으로 정한다.
흩어진 값, 이름 붙은 결정
웹사이트를 몇 달 만들다 보면 같은 결정이 여러 자리에 복사된다. 버튼의 초록, 링크의 초록, 날짜 배지의 초록. 처음에는 같은 값을 붙여 넣었지만, 어느 날 디자인 도구에서 눈으로 색을 찍어 온 값이 한 자리 섞이고, 다른 날 반올림한 값이 섞인다. #2f7d5b와 #2e7d5a는 화면에서 구별되지 않지만 코드에서는 다른 색이다. 그래서 "초록을 조금 밝게"라는 결정 하나를 반영하려면 모든 파일을 뒤져 비슷한 값을 다 찾아야 하고, 하나라도 놓치면 화면에 초록이 두 개가 된다.
디자인 토큰(Design token)은 이 문제를 이름으로 푼다. 토큰은 이름이 붙은 디자인 결정 하나다. "버튼 바탕은 이 초록", "본문 글자는 이 크기", "카드 사이 간격은 24픽셀". 값은 한 곳에만 적고, 쓰는 곳은 모두 이름을 부른다. 이 이름은 세일즈포스의 디자인 시스템 팀이 2014년 무렵 쓰기 시작해 널리 퍼진 것으로 알려져 있고, 지금은 W3C의 디자인 토큰 커뮤니티 그룹(Design Tokens Community Group)이 디자인 도구와 코드 사이에 토큰을 주고받는 파일 형식을 정리하고 있다.
웹에서 토큰을 담는 가장 단순한 그릇은 CSS의 사용자 정의 속성(Custom property), 흔히 "CSS 변수"라고 부르는 것이다. --로 시작하는 이름에 값을 적고, 쓰는 곳에서 var()로 부른다. 변수는 다른 변수를 부를 수 있어서, 토큰을 여러 층으로 쌓을 수 있다.
:root {
/* ① 원시 토큰: 실제 값. 색 이름과 단계 번호 */
--green-600: #2f7d5b;
--green-400: #5eaa85;
--neutral-50: #f6fcf8;
/* ② 의미 토큰: 쓰임의 이름. 원시 토큰을 가리킨다 */
--color-action: var(--green-600);
--color-bg: var(--neutral-50);
/* ③ 컴포넌트 토큰: 부품의 자리. 의미 토큰을 가리킨다 */
--button-bg: var(--color-action);
}
.button { background: var(--button-bg); }
세 층은 맡은 질문이 다르다. 원시 토큰(Primitive token)은 "어떤 색들이 있는가"에 답한다. ColorBook 14장에서 서윤이 만든 초록 사다리 열 칸과 중립 사다리 열 칸이 그대로 원시 토큰이 된다. 의미 토큰(Semantic token)은 "이 색은 무엇에 쓰는가"에 답한다. 바탕, 본문 글자, 흐린 글자, 테두리, 주된 행동. 컴포넌트 토큰(Component token)은 "이 부품의 이 자리는 무엇을 쓰는가"에 답한다. 아래 시뮬레이터의 왼쪽은 책방 E 행사 카드의 토큰 나무, 오른쪽은 그 토큰으로 칠한 카드다. 원시 초록의 색상을 돌리거나, 의미 토큰 "주된 행동"이 가리키는 칸을 바꿔 본다.
토큰 나무: 한 곳을 바꾸면 따라 바뀌는 것
원시 토큰을 바꾸면 그 값을 쓰는 모든 의미와 부품이 바뀐다. 의미 토큰의 화살표를 바꾸면 그 쓰임만 바뀐다. 컴포넌트 토큰을 바꾸면 그 부품의 그 자리만 바뀐다. 층이 있으면 결정의 크기에 맞는 곳을 고칠 수 있다. 브랜드 색을 바꾸는 큰 결정은 원시 층에서, "버튼을 한 칸 진하게"는 의미 층에서, "이 카드의 테두리만 없애자"는 컴포넌트 층에서 한다. 그리고 ③에서 본 것처럼 값을 직접 적은 자리가 하나라도 있으면 체계는 그 자리에서 끊긴다. 지후가 검색으로 찾은 여섯 초록이 바로 그런 끊긴 자리들이었다.
이름 짓기: 원시에서 의미로
토큰 체계의 절반은 이름이다. ColorBook 14장의 작업실 노트가 말했듯 토큰 이름을 green-600처럼 색으로 지으면, 버튼을 청록으로 바꾸는 순간 이름이 거짓말이 된다. 그래서 색 이름은 원시 층에만 둔다. 의미 층은 "무엇에 쓰는가"로 짓는다. 이름을 짓는 틀은 팀마다 다르지만 대개 몇 조각을 정해진 순서로 잇는다.
이름을 정할 때 지키면 좋은 규칙이 몇 가지 있다. 첫째, 부품은 원시 토큰을 직접 부르지 않는다. 버튼이 --green-600을 직접 부르면 다크 모드에서 버튼만 따로 고쳐야 한다. 둘째, 짝을 이름에 드러낸다. 주된 행동의 바탕이 --color-action이면 그 위에 얹는 글자는 --color-on-action이다. 바탕과 글자를 짝으로 부르면 대비 검사도 짝으로 할 수 있다(4절). 셋째, 의미 토큰의 수는 적게 시작한다. 바탕, 표면, 글자, 흐린 글자, 테두리, 주된 행동, 주된 행동 위 글자 일곱 개면 대부분의 화면이 칠해진다. 새 토큰은 "기존 토큰으로는 이 자리의 쓰임을 말할 수 없을 때"만 더한다.
글자 크기와 간격도 같은 방식으로 토큰이 된다. 3장에서 정한 간격 단계 4·8·12·16·24·32·48·64가 --space-1부터 --space-8까지가 되고, TypeBook에서 정한 본문 크기와 5장의 제목 식이 --text-body, --text-h1이 된다. 간격 토큰에 번호를 붙이는 까닭은 간격에는 "쓰임"보다 "크기 순서"가 중요하기 때문이다. 다만 자주 쓰는 자리에는 의미 이름을 덧붙인다. 예를 들어 --space-card-gap: var(--space-5).
--color-action이면, 시안을 넘길 때마다 누군가 번역을 해야 하고 번역은 틀린다. 대부분의 디자인 도구에는 변수나 스타일에 이름을 붙이고 밝은·어두운 모드 값을 따로 넣는 기능이 있다. 시안의 이름을 코드의 이름과 한 글자도 다르지 않게 맞추고, 시안에서 어떤 요소를 골랐을 때 hex 대신 토큰 이름이 보이게 한다. 지후는 이렇게 말했다. "시안에서 #2f7d5b가 보이면 저는 그걸 또 복사해요. color-action이 보이면 복사할 게 없죠."
다크 모드는 반전이 아니다
운영체제와 브라우저는 사용자가 어두운 화면을 원하는지를 웹 페이지에 알려 준다. CSS의 @media (prefers-color-scheme: dark)가 그 신호를 받는다(Media Queries Level 5). 토큰이 있으면 다크 모드는 의미 토큰의 화살표를 한 벌 더 적는 일이 된다. 문제는 그 화살표가 어디를 가리켜야 하는가다. 가장 먼저 떠오르는 방법은 반전이다. 밝은 것은 어둡게, 어두운 것은 밝게. 그런데 반전에는 두 가지가 있고, 둘 다 다른 이유로 실패한다.
하나는 명도 뒤집기다. 모든 토큰의 OKLCH 명도 \(L\)을 \(1 - L\)로 바꾸고 채도와 색상은 둔다. 바탕(0.985)은 거의 검정(0.015)이 되고 짙은 글자(0.306)는 밝은 회색(0.694)이 된다. 그런데 명도가 가운데쯤인 색은 거의 제자리다. 책방 초록(0.532)은 0.468이 되어, 검은 바탕 위에서 오히려 더 어둡고 무겁게 가라앉는다. 다른 하나는 브라우저의 색 반전 필터(filter: invert(1) hue-rotate(180deg))다. sRGB 값을 통째로 뒤집은 뒤 색상을 반 바퀴 돌려 되돌린다. 초록은 의외로 그럴듯하게 살아남지만, 사진은 필름 음화가 되고 그림자는 빛나는 테두리가 된다.
아래 시뮬레이터의 왼쪽은 밝은 화면의 행사 카드, 오른쪽은 세 방법으로 만든 어두운 화면이다. 읽음값은 이 책의 엔진으로 잰 대비와 명도다.
어두운 화면을 만드는 세 가지 방법
설계한 어두운 화면에는 몇 가지 원칙이 들어 있다. 바탕은 순수한 검정이 아니라 아주 어두운 회색(중립 900, OKLCH 명도 0.21)이다. 글자도 순수한 흰색이 아니라 아주 밝은 회색(중립 100)이다. 주색은 밝은 화면보다 두 칸 밝은 초록 400을 쓴다. ColorBook 9장에서 본 것처럼 같은 색도 어두운 바탕 위에서는 더 어둡고 무겁게 보이기 때문이고, 대비로 재도 600번은 어두운 바탕에서 3.55에 그친다. 그리고 주색이 밝아졌으니 그 위에 얹는 글자는 흰색이 아니라 짙은 색이 된다. 의미 토큰 --color-on-action이 짝으로 함께 바뀌는 까닭이다.
다크 모드에서 바꾸지 말아야 할 것도 있다. 사진과 그림은 반전하지 않는다. 다만 밝은 사진이 어두운 화면에서 눈부시면 밝기를 조금 낮추는 정도는 쓴다. 로고는 ColorBook 9장에서 서윤이 만든 어두운 바탕용 변형처럼 따로 준비한 판을 쓴다. 그리고 CSS의 color-scheme: light dark를 적어, 스크롤 막대나 입력 칸처럼 브라우저가 그리는 부분도 어두운 화면에 맞춰 그리게 한다. 마지막으로, 운영체제 설정을 따르는 것을 기본으로 두되 페이지 안에서도 바꿀 수 있게 한다. 이 책의 오른쪽 위 테마 단추가 그런 예다.
html { filter: invert(1) hue-rotate(180deg); } 한 줄로 다크 모드를 "완성"하는 요령이 돌아다닌다. 시안 단계에서 어두운 화면의 대략적인 느낌을 보는 데는 쓸 수 있다. 하지만 사진, 지도, 그림, 로고가 모두 뒤집히고, 그것을 되돌리려고 이미지마다 필터를 한 번 더 거는 순간 코드는 토큰 없는 시절보다 복잡해진다. 브랜드 색도 필터의 계산식이 정한 값이 되어, 그 값이 대비 기준을 넘는지는 우연에 맡겨진다.
짝을 정하면 대비 검사가 끝난다
ColorBook 13장과 TypeBook 7장에서 서윤은 글자와 바탕의 대비 기준을 정했다. 웹 콘텐츠 접근성 지침 WCAG 2.2의 성공 기준 1.4.3은 본문 글자에 4.5:1, 큰 글자(24px 이상, 또는 굵게 18.66px 이상)에 3:1을 요구하고, 1.4.6(AAA)은 각각 7:1과 4.5:1을 요구한다. 1.4.11은 버튼 테두리나 아이콘처럼 글자가 아닌 요소에 3:1을 요구한다. 문제는 화면의 모든 글자와 모든 바탕을 짝지어 검사하는 일이 끝이 없다는 것이다.
토큰이 있으면 이 일이 유한해진다. 글자 쪽 의미 토큰이 다섯 개, 바탕 쪽 의미 토큰이 세 개라면 짝은 열다섯 개뿐이다. 이 열다섯 짝을 밝은 화면과 어두운 화면에서 한 번씩 재고, 기준에 못 미치는 짝은 "쓰지 않는 조합"으로 표에 적는다. 그러면 새 화면을 만들 때 검사할 것은 "표에 없는 조합을 쓰지 않았는가" 하나다. 아래 표의 칸을 누르면 그 짝을 크게 보여 준다.
글자 토큰 × 바탕 토큰 대비표
표에서 붉은 칸은 "그 색이 틀렸다"는 뜻이 아니라 "그 짝을 본문에 쓰지 않는다"는 뜻이다. 강조색은 큰 제목과 장식에 쓰고, 크림 띠에는 짙은 글자만 올린다. 이 규칙을 문서로만 남기면 잊힌다. 그래서 토큰 이름에 짝을 넣는 방법이 쓰인다. 크림 띠의 바탕이 --color-band라면 그 위에 쓸 글자는 --color-on-band 하나만 두고, 그 값은 짙은 색으로 고정한다. 크림 띠 위에 흐린 글자를 쓰고 싶어도 쓸 토큰이 없다. 체계가 실수를 막는 방식이다.
rem: 글자 크기 설정을 받아 주는 단위
색의 토큰이 화면 모드를 따라야 한다면, 글자의 토큰은 사람을 따라야 한다. 브라우저에는 기본 글자 크기 설정이 있다. 대개 16px이고, 시력이 약한 사람은 이것을 20px, 24px로 올려 둔다. 이 설정이 페이지에 전해지는지는 크기를 어떤 단위로 적었느냐에 달려 있다.
| 단위 | 기준 | 기본 글자 크기를 24px로 올리면 |
|---|---|---|
px | CSS 픽셀 | 17px은 17px 그대로. 설정이 무시된다. |
rem | 루트(html)의 글자 크기 | 1.0625rem은 16 × 1.0625 = 17px에서 24 × 1.0625 = 25.5px로 커진다. |
em | 그 요소(또는 부모)의 글자 크기 | 함께 커지지만, 겹쳐 쓰면 곱해져 예상보다 커지거나 작아진다. |
브라우저의 확대(Ctrl +)는 이것과 다르다. 확대는 CSS 픽셀 자체를 키우므로 px로 적은 글자도 커진다. 그 대신 화면 폭이 CSS 픽셀로 좁아져 5장의 중단점이 바뀐다. 기본 글자 크기 설정은 글자만 키운다. 그래서 px로 적은 페이지는 확대에는 반응하지만 글자 크기 설정에는 반응하지 않는다. 두 가지 모두를 받아 주려면 글자 크기를 rem으로 적고, 중단점을 em으로 적는다. TypeBook 11장에서 서윤이 루트 글자 크기를 건드리지 않기로 한 것도 이 때문이다. html { font-size: 62.5%; }처럼 루트를 10px로 바꾸는 요령은 계산을 쉽게 하지만, 사용자가 정한 기준을 디자이너가 덮어쓴다.
글자만 rem으로 바꾸고 나머지를 px로 두면 새로운 문제가 생긴다. 높이를 44px로 고정한 버튼 안에서 글자가 커지면 글자가 위아래 여백을 밀어내며 상자에 꽉 끼고(더 커지면 상자 밖으로 흘러나간다), 높이를 고정한 카드에서는 글이 카드 밖으로 넘친다. 아래 시뮬레이터는 폭 768px 화면(태블릿을 세로로 든 정도)의 행사 목록이다. 기본 글자 크기를 올리며 세 가지 적기 방식을 비교한다.
기본 글자 크기를 올리면
세 번째 방식이 이 장의 결론이다. 글자 크기는 rem, 글자를 담는 상자는 높이 대신 rem 여백, 중단점은 em. 그러면 사용자가 글자를 키웠을 때 페이지는 "같은 모양을 크게"가 아니라 "더 좁은 화면의 모양"으로 바뀐다. 글자를 1.5배로 키운 768px 화면은 글자 기준으로 보면 512px 화면과 같기 때문이다. 다만 화면 가장자리 여백처럼 글자와 상관없는 값까지 rem으로 적으면, 글자를 키운 사람의 좁은 화면에서 여백이 글 칸을 먹는다. 무엇을 글자에 묶고 무엇을 화면에 묶을지 토큰마다 정한다.
글자 크기 단계
글자 크기 토큰은 몇 개가 적당할까. TypeBook 8장에서 본 대로 크기 단계가 너무 많으면 위계가 흐려지고, 단계 사이가 너무 좁으면 서로 구별되지 않는다. 그래서 흔히 본문 크기에 일정한 비율을 거듭 곱해 단계를 만든다. 비율에는 음정의 이름을 붙이곤 한다. 1.2(단3도), 1.25(장3도), 1.333(완전4도), 1.5(완전5도), 1.618(황금비). 웹 디자이너 팀 브라운(Tim Brown)이 2011년 A List Apart의 글 「More Meaningful Typography」에서 이런 모듈러 스케일(Modular scale)을 웹에 소개했다.
웹에서는 비율 하나로 모든 화면을 덮기 어렵다. 넓은 화면에 맞춘 큰 비율은 휴대폰에서 제목을 두세 줄로 꺾고, 휴대폰에 맞춘 작은 비율은 넓은 화면에서 위계를 밋밋하게 만든다. 그래서 좁은 화면에서는 비율을 조금 줄이고 넓은 화면에서는 늘이는 방법을 쓴다. 두 화면의 크기를 5장의 clamp()로 이으면 그 사이의 화면에서는 비율도 서서히 바뀐다. 아래에서 비율을 바꾸며 360px 화면과 1,280px 화면의 단계를 본다.
비율로 만드는 글자 크기 단계
단계를 정했으면 실제 값은 반올림해 토큰으로 적는다. 계산기에서 나온 29.38px을 그대로 쓰기보다 30px처럼 기억하기 쉬운 값으로 고르고, 앞 장들에서 이미 정한 값(5장의 제목 30 → 40px)과 맞춘다. 비율은 출발점이고, 최종 값은 실제 글과 화면에서 눈으로 고른다. TypeBook 13장에서 서윤이 1.25와 1.5 사이에서 1.333을 고른 것도 비율 표가 아니라 휴대폰에서 꺾이는 제목을 보고서였다.
토큰 작업대: 흩어진 값에서 체계로
마지막으로 책방 E 행사 일정 페이지를 두 판으로 비교한다. "첫 코드"는 지후가 검색으로 찾아낸 그대로다. 색은 hex로, 크기는 px로 자리마다 적혀 있고, 어두운 화면은 명도 뒤집기로 만들었다. "서윤의 토큰"은 이 장에서 정한 체계다. 화면 모드와 기본 글자 크기를 바꾸면서 아래 칸에 적힌 CSS와 읽음값을 본다.
행사 일정 페이지: 첫 코드와 서윤의 토큰
토큰이 해 주지 않는 일도 분명히 해 두자. 토큰은 결정을 기록하고 전파할 뿐, 결정을 대신 내려 주지 않는다. 주색이 어떤 초록이어야 하는지는 ColorBook에서, 본문이 몇 픽셀이어야 하는지는 TypeBook에서 따로 고민했다. 토큰은 그 고민의 결과를 잃어버리지 않게 하고, 다음에 바꿀 때 한 곳만 고치게 한다. 그래서 토큰 표에는 값과 함께 왜를 적어 두는 것이 좋다. "주색은 사다리 600, 흰 글자 대비 4.99 때문" 같은 한 줄이, 반년 뒤 누군가 "조금만 밝게"를 말할 때 무엇을 다시 재야 하는지 알려 준다.
서윤의 웹 개편 노트
여섯 개의 초록과 사라진 그림자
2월 첫 주, 행사 일정 페이지의 반응형 코드를 마친 지후가 다크 모드를 넣어 보자고 했다. "운영체제가 어두운 화면이면 따라가게요. 금방 해요." 지후는 짧은 스크립트로 모든 색의 OKLCH 명도를 뒤집었다. 결과는 금방 나왔다. 흰 바탕은 검정이 되었는데, 책방 초록 버튼은 명도 0.53에서 0.47로 거의 그대로라 검은 바탕 위에서 대비가 3.19로 떨어졌다. 서윤이 보기에는 "탁하게 가라앉은" 초록이었다. 흐린 날짜 줄은 3.06, 흰 카드는 검은 바탕과 똑같은 검정이 되어 경계가 사라졌고 카드의 그림자도 함께 묻혔다. 한도경은 휴대폰을 들여다보다가 "밤에 불 끈 방에서 그림을 보는 것 같네. 액자도 벽도 다 까매" 했다.
더 큰 문제는 그다음에 보였다. 지후가 다크 모드용 값을 넣으려고 코드에서 초록을 검색했더니 #2f7d5b 말고도 #2e7d5a, #307c5b, #2f7c5a, #2d7b59, #317e5c가 나왔다. 시안에서 스포이트로 찍은 값, 반올림한 값, 다른 페이지에서 복사한 값이었다. 회색도 셋이었고, TypeBook 여름 노트에서 버린 날짜 줄의 연회색 #999999가 신간 목록에 다시 들어와 있었다. 글자 크기는 17px과 1.0625rem이 섞여, 도경의 휴대폰에서는 같은 본문이 페이지마다 다른 크기로 보였다.
서윤은 ColorBook 가을 노트의 웹 팔레트 v1과 TypeBook 여름 노트를 펼쳐 놓고 토큰 표를 새로 만들었다. 이름 규칙은 두 층이다. 원시 토큰은 색 이름과 단계로 짓는다(--green-50 … --green-900, --neutral-50 … --neutral-900, 그리고 책방의 색 --ink #22342a, --sage #567666, --cream #ffe2d7, --accent #d24f39). 의미 토큰은 --color- 뒤에 쓰임을 적고(bg, surface, text, text-muted, border, action, on-action, band, on-band), 부품은 의미 토큰만 부른다. 컴포넌트 토큰은 버튼처럼 상태가 많은 부품에만 나중에 더하기로 했다. 본문 글자를 ColorBook 표의 중립 900으로 할지 TypeBook 노트의 짙은 색 #22342a로 할지 잠시 망설였는데, 손님들이 이미 서점 소식 글에서 익숙해진 #22342a(바탕 위 12.68)를 택했다. 흐린 글자도 같은 이유로 ColorBook 표의 중립 700 대신 TypeBook의 차분한 초록 #567666(4.83)으로 했다. 대비는 낮아지지만 본문 기준 4.5를 넘는다.
어두운 화면의 값은 따로 지정했다. 바탕은 중립 900 #151917, 카드 표면은 한 단계 밝은 중립 800 #2a2f2d로 띄우고 그림자는 쓰지 않는다. 본문 글자는 중립 100(바탕 위 15.79), 흐린 글자는 중립 400(8.04), 주색은 사다리 400 #5eaa85(6.39)이고, 주색 위 글자는 흰색 대신 중립 900(6.39)이다. 크림 띠는 어두운 화면에서 #402e27이 되고 그 위에는 중립 100만 올린다. 강조색은 밝은 화면에서 바탕 위 4.11이라 큰 글자에만, 어두운 화면에서는 명도를 0.70으로 올린 #ed7761(6.26)을 쓴다. 글자 크기는 모두 rem으로 옮겼다. 작은 글자 0.875rem(14px), 본문은 TypeBook의 clamp 식, 소제목·부제목·제목은 넓은 화면 비율 1.333에서 출발해 반올림한 20 → 24px, 24 → 32px, 그리고 5장의 30 → 40px이다. 3장의 간격 단계도 --space-1(0.25rem)부터 --space-8(4rem)까지 rem으로 옮기되, 휴대폰 화면 가장자리 여백 16px만은 글자를 키운 손님의 화면에서 글 칸을 먹지 않도록 px로 남겼다. 지후가 같은 페이지를 다시 검색하자 hex는 원시 토큰을 적은 파일 하나에만 남았다. 도경은 글자를 가장 크게 한 휴대폰으로 어두운 화면을 열어 보고는 "이번엔 액자가 보이네" 했다.
| 의미 토큰 | 밝은 화면 | 어두운 화면 | 대비(밝은 · 어두운) |
|---|---|---|---|
bg | neutral-50 #f6fcf8 | neutral-900 #151917 | — |
surface | white #ffffff | neutral-800 #2a2f2d | — |
text | ink #22342a | neutral-100 #edf3f0 | 12.68 · 15.79 |
text-muted | sage #567666 | neutral-400 #aab0ac | 4.83 · 8.04 |
action | green-600 #2f7d5b | green-400 #5eaa85 | 4.80 · 6.39 |
on-action | white | neutral-900 | 4.99 · 6.39 |
band · on-band | cream · ink | #402e27 · neutral-100 | 10.74 · 11.40 |
--green-600, --neutral-50, 책방 색 --ink --sage --cream --accent), 의미 토큰은 --color- + 쓰임(바탕과 그 위 글자는 x · on-x 짝으로), 부품은 의미 토큰만 부른다. hex는 원시 토큰 파일에만. 다크 모드 값은 반전하지 않고 위 표처럼 따로 지정한다(높이는 그림자 대신 밝은 표면으로). 강조색 #d24f39는 밝은 화면에서 큰 글자와 장식에만, 어두운 화면에서는 #ed7761. 글자 크기는 모두 rem: --text-sm 0.875rem, --text-body clamp(1.0625rem, 1.038rem + 0.1087vw, 1.125rem), --text-h3 20 → 24px, --text-h2 24 → 32px, --text-h1 30 → 40px(5장 식). 간격 단계는 rem, 화면 가장자리 여백 16px만 px. 중단점은 5장대로 em.핵심 정리
- 디자인 토큰은 이름이 붙은 디자인 결정 하나다. 값은 한 곳에만 적고 쓰는 곳은 이름을 부른다. 웹에서는 CSS 사용자 정의 속성(
--이름,var())이 가장 단순한 그릇이다. - 원시 토큰은 "어떤 값이 있는가"(색 이름 + 단계), 의미 토큰은 "무엇에 쓰는가"(바탕, 글자, 주된 행동), 컴포넌트 토큰은 "이 부품의 이 자리"에 답한다. 부품은 원시 토큰을 직접 부르지 않는다.
- 바탕과 그 위의 글자는
x·on-x처럼 짝으로 이름 짓는다. 그러면 대비 검사가 유한한 짝의 표가 되고, 기준 미달 짝은 아예 쓸 토큰이 없게 만들 수 있다. - 다크 모드는 반전이 아니다. 명도 뒤집기는 가운데 명도의 브랜드 색을 가라앉히고 표면의 구분을 없애며, 반전 필터는 사진과 그림자를 뒤집는다. 어두운 화면의 의미 토큰은 따로 고른다.
- 어두운 화면에서는 순수한 검정 대신 아주 어두운 회색을, 주색은 사다리의 더 밝은 칸을, 높이는 그림자 대신 더 밝은 표면으로 말한다. 대비 검사는 밝은 화면과 똑같이 한다.
- 글자 크기를 px로 적으면 브라우저의 기본 글자 크기 설정이 무시된다. 글자는 rem, 글자를 담는 상자는 고정 높이 대신 rem 여백, 중단점은 em으로 적으면 글자를 키운 사람에게 "더 좁은 화면의 모양"이 간다.
- 글자 크기 단계는 본문에 비율을 거듭 곱해 출발하고, 좁은 화면에서는 비율을 낮춘다. 최종 값은 반올림해 rem 토큰으로 적고, 실제 글과 화면에서 확인한다.
확인 퀴즈
1. 버튼이 --green-600을 직접 부르던 코드를, 버튼은 --color-action을 부르고 --color-action이 --green-600을 가리키게 바꿨다. 이렇게 한 층을 더 둔 가장 큰 이득은?
2. 모든 색의 OKLCH 명도를 L → 1 − L로 뒤집어 다크 모드를 만들었더니, 책방 초록(L 0.53) 버튼이 검은 바탕에서 탁해 보였다. 가장 알맞은 설명과 처방은?
3. 브라우저의 기본 글자 크기를 24px로 올린 사용자가 본문을 font-size: 1.0625rem으로 적은 페이지를 열었다. 본문 글자의 크기는?
17px로 적었다면 설정과 상관없이 17px에 머문다. 기본 글자 크기 설정은 화면을 확대하는 기능(Ctrl +)과 다르다.4. 본문 17px에 비율 1.333을 그대로 써서 h1을 본문의 세 단계 위(17 × 1.333³)로 두었더니, 360px 휴대폰에서 "2월 행사와 모임 일정"이 두 줄로 꺾였다. 이 장에서 다룬 방법으로 가장 알맞은 것은?
출처
- W3C Design Tokens Community Group, Design Tokens Format Module — 도구 사이에 토큰을 주고받는 파일 형식(요약). "디자인 토큰"이라는 이름이 세일즈포스 디자인 시스템 팀에서 2014년 무렵 쓰이기 시작했다는 이야기는 널리 알려진 설명으로 옮겼다.
- W3C, CSS Custom Properties for Cascading Variables Module Level 1 —
--이름과var(). - W3C, Media Queries Level 5 —
prefers-color-scheme. W3C, CSS Color Adjustment Module Level 1 —color-scheme. - W3C, Filter Effects Module Level 1 —
invert(),hue-rotate()의 계산 행렬(시뮬레이터에 사용). - W3C, Web Content Accessibility Guidelines (WCAG) 2.2, 성공 기준 1.4.3 Contrast (Minimum), 1.4.6 Contrast (Enhanced), 1.4.11 Non-text Contrast, 1.4.4 Resize Text.
- Björn Ottosson, "A perceptual color space for image processing", 2020 — OKLab·OKLCH(이 책의 엔진 ART에서 사용).
- Tim Brown, "More Meaningful Typography", A List Apart, 2011 — 웹의 모듈러 스케일(요약).
- 색 값과 대비는 이 책의 엔진(ART)으로 계산했다. 초록 사다리와 중립 사다리는 ColorBook 14장의 웹 팔레트 v1이다. 케이스의 "첫 코드"는 가상의 예다.