
디자인 시안에서 파란색을 스포이드로 찍어 그대로 CSS 에 적는다. 버튼 하나 만드는 데 background: #3B82F6 한 줄이면 된다. 그다음 화면에서도 같은 버튼이 필요하니 그 줄을 복사한다. 몇 달 뒤에는 같은 서비스 안에서 버튼과 링크와 배지가 전부 파란색인데 값이 조금씩 다른 상태가 된다.
같은 색을 일관되게 쓰려면 값을 정하고 관리할 위치가 필요하다.
디자인 토큰을 기준으로 값을 관리하고 코드에 전달하는 순서를 정리했다. 코드에서만 값을 바꿨을 때 생기는 불일치도 살펴본다.
1. 색상 하드코딩으로 생기는 불일치
복사한 #3B82F6 이 열 군데쯤 늘어난 다음에 디자이너가 브랜드 파란색을 한 톤 낮추기로 한다. 이제 고칠 자리를 찾아야 한다. 검색만 잘하면 되는 문제로 보이지만 아니다.
안 걸린다. 누군가는 #3b82f6 이라고 소문자로 적었고, 누군가는 rgb(59, 130, 246) 으로 적었고, 누군가는 그 색에서 살짝 어두운 값을 눈대중으로 골라 넣었다. 검색은 글자만 찾지 색을 찾지 않는다.
값에 이름을 붙이면 찾을 필요가 없어진다.
:root {
--color-primary: #3b82f6;
}
.button {
background: var(--color-primary);
}
이렇게 이름을 붙여 한곳에 모아 둔 값을 디자인 토큰이라고 한다. 색만 해당하는 건 아니다. 간격, 글자 크기, 모서리 둥글기, 그림자처럼 디자인에서 반복해 쓰는 값이면 전부 대상이다. 브랜드 색을 바꾼다는 건 이제 :root 안의 한 줄을 고치는 일이 된다.
여기까지는 굳이 체계라고 부를 것도 없다. 변수 하나 뺐을 뿐이니까.
2. 원시 토큰과 시맨틱 토큰
토큰을 정리해 둔 파일을 열면 같은 색이 이름 두 개로 두 번 적혀 있다.
:root {
/* 원시 토큰 */
--blue-500: #3b82f6;
--gray-900: #111827;
/* 의미 토큰 */
--color-primary: var(--blue-500);
--color-text: var(--gray-900);
}
한 번이면 될 걸 왜 두 번 적나. 두 이름이 서로 다른 질문에 답하기 때문이다.
| 원시 토큰 | 의미 토큰 | |
|---|---|---|
| 예 | --blue-500, --spacing-4 | --color-primary, --space-card-gap |
| 답하는 질문 | 이건 무슨 색인가 | 이 자리에 무슨 색이 와야 하나 |
| 누가 정하나 | 팔레트를 만드는 사람 | 화면의 역할을 정하는 사람 |
| 바뀌는 때 | 브랜드 팔레트를 새로 뽑을 때 | 강조색을 파란색에서 초록색으로 옮길 때 |
표 1. 원시 토큰과 의미 토큰의 네 가지 차이
--blue-500 은 그냥 값에 이름표를 붙인 것이라 무엇에 쓰는지가 안 적혀 있다. 반면 --color-primary 는 역할 이름이라, 그 자리에 파란색이 오든 초록색이 오든 이름은 그대로다.
그래서 컴포넌트가 쓰는 건 의미 토큰 쪽이다. 버튼 코드에 var(--blue-500) 을 적어 두면, 강조색을 초록으로 옮기는 순간 그 버튼만 파란색으로 남는다. #3B82F6 을 직접 적었을 때와 똑같은 상황이 이름만 바꿔 다시 온다.
3. 디자인 값을 코드로 전달하는 순서
이름을 붙였다고 끝이 아니다. 그 이름을 누가 정하느냐가 다음 문제다. 디자이너가 도구 안에서 정한 값과 개발자가 CSS 에 적어 둔 값이 따로 놀면, 이름만 생기고 어긋남은 그대로다.
그래서 출발점을 하나로 정한다. 흐름은 대개 이렇다.
디자인 도구 토큰 정의 빌드 결과물 화면
(Figma Variables) → (JSON) → (Style Dictionary) → (CSS 변수) → (컴포넌트)
Figma Variables 는 색이나 숫자 같은 값을 이름 붙여 저장해 두는 기능이다. 변수가 다른 변수를 가리키게 할 수도 있어서, 원시 토큰과 의미 토큰의 두 단계를 디자인 도구 안에서도 그대로 만들 수 있다. 다만 이 변수들을 코드 쪽으로 내보내는 공식 경로가 정확히 무엇인지는 확인하지 못했다. 플러그인, REST API, 별도 연동 도구까지 방법이 여럿이고 팀마다 다르게 쓴다.
Style Dictionary 는 그렇게 뽑아낸 토큰 정의 파일을 받아서 형식을 바꿔 내보내는 빌드 도구다. 같은 정의 하나로 웹용 CSS 변수도 만들고, 필요하면 JS 상수 파일이나 모바일용 파일도 같이 만든다. 세부 설정 형식은 버전에 따라 달라서 여기 옮겨 적지는 않는다.
요점은 도구 이름이 아니다. 화살표가 한쪽으로만 그려져 있다는 것이다. 값을 새로 정하는 자리는 맨 왼쪽 하나뿐이고, 나머지는 받아서 형식만 바꾼다. 빌드가 만들어 낸 CSS 파일에 손으로 색을 하나 추가하면 다음 빌드에서 지워진다. 그런 파일 맨 위에 보통 자동 생성이라는 주석이 붙어 있는데, 그건 편집하지 말라는 표시다.
4. 코드에서만 값을 바꿨을 때의 문제
화살표를 거스른다는 게 대단한 일은 아니다. 대개 이렇게 생겼다.
/* 디자인 시안에 있는 회색인데 토큰 이름을 못 찾았다 */
.card {
background: #f3f4f6;
}
/* 여기는 14px 이어야 하는데 맞는 토큰이 없다 */
.label {
font-size: 14px;
}
급할 때 한 줄 적는 것이고, 그 화면은 실제로 잘 나온다. 문제는 그 뒤다. 디자인에서 회색 값을 조금 바꾸면 다른 화면은 다 따라 바뀌는데 이 카드만 안 바뀐다. 이런 자리가 하나씩 쌓이면 처음 이야기로 돌아간다. 같은 파란색이 화면마다 조금씩 다른 상태다.
더 곤란한 쪽은 따로 있다. 이런 자리가 몇 개인지 아무도 모르게 된다는 것이다. 토큰 체계를 만들어 두면 보통 "색은 전부 토큰에서 온다"를 전제로 다음 일을 결정한다. 브랜드 색을 바꾸는 작업을 한 줄짜리로 잡거나, 다크모드를 토큰 교체만으로 끝낼 수 있다고 계획하거나. 직접 적어 넣은 값이 섞여 있으면 그 전제가 틀린 게 되고, 계획도 같이 틀린다.
그래서 팀들이 이 자리를 검사로 막는다. CSS 파일에서 # 으로 시작하는 색 값을 찾아 실패시키는 lint 규칙 정도면 시작으로 충분하다. 리뷰에서 눈으로 잡는 것과 달리 이건 바쁜 날에도 똑같이 돈다.
5. 다크 모드에 시맨틱 토큰 적용하기
원시 토큰과 의미 토큰을 왜 굳이 나누는지는 다크모드를 붙일 때 제일 또렷해진다.
의미 토큰 없이 다크모드를 만들면, 색을 쓰는 컴포넌트마다 조건이 하나씩 붙는다. 버튼에도, 카드에도, 입력창에도. 화면이 백 개면 그 조건도 그만큼 늘어난다.
의미 토큰이 있으면 컴포넌트는 한 글자도 안 바뀐다.
:root {
--color-surface: var(--gray-0);
--color-text: var(--gray-900);
}
.dark {
--color-surface: var(--gray-900);
--color-text: var(--gray-50);
}
--color-surface 라는 이름은 그대로고 그 안에 들어가는 원시 토큰만 갈아 끼운다. 카드가 var(--color-surface) 를 쓰고 있으면 다크모드에서 알아서 어두워진다.
이때 3절의 화살표를 거스른 자리가 한꺼번에 드러난다. background: white 라고 적어 둔 카드는 다크모드에서도 흰색으로 남는다. 다크모드를 붙이는 작업의 상당 부분은 새 색을 정하는 게 아니라 이렇게 굳어 있는 값을 찾아 토큰으로 바꾸는 일이다.
6. 토큰 관리에 필요한 추가 작업
토큰 체계를 도입하면 다음과 같은 관리 작업이 늘어난다.
| 무엇을 | 어떤 식으로 |
|---|---|
| 이름을 정하는 시간 | --color-text-subtle 인지 --color-text-secondary 인지 정하는 데 회의가 필요하다. 값 자체보다 이름 논의가 길어진다 |
| 이름을 바꾸기 어려움 | 한 번 퍼진 토큰 이름은 화면 전체에 흩어져서, 바꾸려면 전부 찾아 고쳐야 한다. 첫 이름을 오래 쓰게 된다 |
| 예외 화면의 번거로움 | 이벤트 페이지처럼 브랜드 규칙 밖의 화면에서는 맞는 토큰이 없다. 토큰을 새로 추가하거나 예외를 허용하거나 둘 중 하나인데, 둘 다 깔끔하지 않다 |
| 처음 오는 사람의 진입 비용 | 간격 토큰의 숫자가 px 가 아닌 경우가 많다. 4 가 16px 인 식이라, 모르고 보면 값이 안 맞는 것처럼 읽힌다 |
| 값 하나 바꾸는 거리 | 색 하나 고치는 데 디자인 도구부터 다시 시작해야 한다. 한 방향으로 흐른다는 건 지름길이 없다는 뜻이기도 하다 |
표 2. 토큰 체계를 도입하고 치르는 다섯 가지
급한 수정에도 원본 토큰부터 바꿔야 하는 점이 번거롭다. CSS 한 줄만 고치면 당장은 끝나지만 문서와 코드의 값이 달라진다.
아직 확인하지 못한 구성
- Figma Variables 를 코드로 내보내는 방법 중에 무엇이 표준에 가까운지. 여러 경로가 있다는 것까지만 안다.
- Style Dictionary 의 최신 버전 설정 형식. 버전 사이에 바뀌었다고 하는데 직접 확인하지 못했다.
- 한 코드베이스에서 브랜드 여러 개를 전환하는 구성. 원시 토큰 묶음을 통째로 갈아 끼우는 방식으로 된다고 하는데 직접 만들어 보지 않았다.
시안에서 색을 스포이드로 찍어 CSS 에 옮기는 일이 없어진다. 대신 이 자리가 무슨 역할인지를 먼저 찾는다. 강조인지, 그냥 배경인지, 흐린 보조 글자인지.