같은 파란색이 화면마다 달라질 때: 디자인 토큰으로 정리하기

X Facebook

Written by

in

디자인 시안의 파란색을 CSS에 옮긴다. 버튼 하나에는 background: #3B82F6 한 줄이면 충분하다. 다음 화면에서도 같은 버튼이 필요해 그 줄을 복사한다. 몇 달 뒤에는 버튼과 링크, 배지가 모두 파란색인데 값은 조금씩 달라져 있다.

색을 바꿀 때마다 화면 전체를 찾아다니지 않으려면 값을 한곳에서 관리해야 한다. 반복되는 값에 이름을 붙인 것이 디자인 토큰이다. 토큰의 값과 역할을 나누는 이유, 디자인 도구에서 코드로 전달하는 흐름, 그 흐름을 벗어난 값이 만드는 문제를 살펴본다.

1. 색을 CSS에 직접 적으면 무슨 일이 나나

복사한 #3B82F6이 열 군데쯤 늘어난 다음에 디자이너가 브랜드 파란색을 한 톤 낮추기로 한다. 이제 고칠 자리를 찾아야 한다. 검색만 잘하면 되는 문제로 보이지만 아니다.

안 걸린다. 누군가는 #3b82f6이라고 소문자로 적었고, 누군가는 rgb(59, 130, 246)으로 적었고, 누군가는 그 색에서 살짝 어두운 값을 눈대중으로 골라 넣었다. 검색은 글자만 찾지 색을 찾지 않는다.

값에 이름을 붙이면 찾을 필요가 없어진다.

:root {
  --color-primary: #3b82f6;
}

.button {
  background: var(--color-primary);
}

이렇게 이름을 붙여 한곳에 모아 둔 값을 디자인 토큰이라고 한다. 색만 해당하는 건 아니다. 간격, 글자 크기, 모서리 둥글기, 그림자처럼 디자인에서 반복해 쓰는 값이면 전부 대상이다. 브랜드 색을 바꾼다는 건 이제 :root 안의 한 줄을 고치는 일이 된다.

여기까지는 굳이 체계라고 부를 것도 없다. 변수 하나 뺐을 뿐이니까.

2. blue-500과 color-primary는 왜 따로 있나

토큰을 정리해 둔 파일을 열면 같은 색이 이름 두 개로 두 번 적혀 있다.

: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에 옮기는 일이 없어진다. 대신 이 자리가 무슨 역할인지를 먼저 찾는다. 강조인지, 그냥 배경인지, 흐린 보조 글자인지.

Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다