CSS 변수만으로는 한계가 있어요
”이 버튼 색상을 primary로 바꿔주세요”라는 디자이너의 요청에 당황한 적 있나요? CSS 파일을 뒤져보니 --blue-500
, --primary-blue
, --main-color
같은 비슷한 이름들만 가득하고, 정작 어떤 걸 써야 할지 모르겠어요.
:root {
--blue-500: #3b82f6;
--primary-blue: #2563eb;
--main-color: #1d4ed8;
--accent: #3b82f6; /* 어? 이건 blue-500과 같은 값이네? */
}
프로젝트가 커질수록 이런 문제가 심해져요. 체계적인 디자인 토큰 네이밍으로 이 혼란을 해결할 수 있어요.
디자인 토큰이 뭔가요?
디자인 토큰은 colors
, spacing
, typography
, scale
같은 디자인 시스템을 구성하는 가장 작은 단위예요. 쉽게 말해서 “디자인의 레고 블록”이라고 생각하시면 돼요.
왜 토큰을 써야 할까요?
잘 만들어진 토큰 이름은 디자이너와 개발자 사이의 소통을 원활하게 해줘요. 그리고 프로젝트가 커져도 일관성을 유지할 수 있고요.
토큰은 어떻게 진화할까요?
가장 간단한 토큰부터 시작해서 점진적으로 의미 있는 토큰으로 발전시켜 보아요.
1단계: 기본 색상 값
가장 단순한 형태의 색상 정의
보시는 것처럼 토큰이 점점 더 구체적이고 의미 있게 변해가죠?
처음엔 단순히 blue-500
이었던 것이 마지막엔 color-action-primary-hover
가 되었어요. 이제 개발자가 “버튼에 마우스를 올렸을 때 primary 색상”이 필요하다면 바로 어떤 토큰을 써야 할지 알 수 있어요.
하지만 이렇게 복잡한 토큰을 만들려면 체계적인 규칙이 필요해요. 그래서 4단계 레벨 시스템을 사용하는 거예요.
토큰 이름은 4가지 레벨로 구성돼요:
- NameSpace: 프로젝트나 팀 이름으로 다른 라이브러리와 구분해요
- Object: 특정 컴포넌트(button, input 등)에서만 사용할 때 지정해요
- Base: 가장 기본이 되는 카테고리와 속성이에요 ⭐
- Modifier: 구체적인 용도나 상태를 나타내요
이제 각 레벨을 어떻게 활용하는지 차근차근 알아볼까요?
Base 레벨 이해하기
가장 기본이 되는 카테고리와 속성을 정의해요. 모든 토큰의 출발점이 되는 레벨이에요.
대부분의 토큰은 color
, spacing
, typography
같은 전형적인 카테고리에 속해요. Property에는 weight
, size
등이 포함되고요.
// 흰 배경 색상
$color-background: #fff;
// 큰 텍스트
$typography-xl: 2rem;
// 빨간 알림 배경 색상
$color-feedback-background: #fb3b1e;
하지만 Base 레벨만으로는 구체적인 용도를 표현하기 어려워요. 그래서 Modifier가 필요한 거예요.
동음이의어는 반드시 피하세요!
typo
, text
같은 축약어는 여러 의미로 해석될 수 있어요. 조금 길더라도 typography
처럼 명확한 이름을 사용하는 것이 좋아요.
Modifier로 목적 부여하기
Base 레벨에 구체적인 목적과 의미를 추가하는 단계예요. 이제 토큰이 언제, 어떻게 사용될지 명확해져요.
색상에 Modifier를 추가하면 이렇게 그룹화할 수 있어요:
// 피드백 색상 그룹
$color-feedback-success: #10b981;
$color-feedback-error: #ef4444;
// 액션 색상 그룹
$color-action-primary: #3b82f6;
$color-action-secondary: #6b7280;
Typography에도 Scale 개념을 적용할 수 있어요:
// 제목 크기
$typography-heading-xxl: 3rem;
// 부제목 크기
$typography-heading-l: 2rem;
Modifier의 세 가지 타입:
- Variant: 용도별로 구분해요 (
success
,error
,primary
등) - State: 상호작용 상태를 나타내요 (
hover
,disabled
,active
등) - Scale: 크기나 레벨을 구분해요 (
xs
,s
,m
,l
,xl
또는100
,200
,300
등)
예를 들어, 모바일에서 버튼 최소 높이를 48px 이상으로 해야 한다면 이렇게 할 수 있어요:
html {
--size-action-height-coarse: 32px;
@media (pointer: coarse) {
--size-action-height-coarse: 48px;
}
}
button {
min-height: var(--size-action-height-coarse);
}
특정성 vs 유연성
color-success
처럼 범용적인 이름보다는 color-feedback-success
처럼 구체적인 이름이 좋아요. 팀원들이 같은 관점으로 토큰을 이해할 수 있거든요.
특정 컴포넌트에만 적용하고 싶다면 Object 레벨을 사용하면 돼요.
Object로 범위 제한하기
특정 컴포넌트에서만 사용할 토큰은 컴포넌트 이름을 포함해서 범위를 명확히 해요.
예를 들어, input 요소의 테두리 색상이 상태에 따라 바뀌어야 한다면:
// Input 컴포넌트 전용 토큰
$input-color-border-default: #d1d5db;
$input-color-border-error: #ef4444;
$input-color-border-success: #10b981;
로컬에서 전역으로
처음부터 전역 토큰을 만들지 마세요. 컴포넌트별로 로컬 토큰을 만들고, 재사용이 확실해지면 그때 전역으로 옮기는 것이 네임스페이스 오염을 방지하는 좋은 방법이에요.
인풋 요소에 아이콘이나 링크 같은 중첩된 요소가 포함될 때도 BEM CSS 방법론처럼 컴포넌트와 요소 이름을 모두 포함할 수 있어요:
$input-prefix-icon-size: 1rem;
$input-inline-link-color-text: #3b82f6;
언제 전역으로 옮길까요?
여러 컴포넌트에서 같은 토큰이 필요하다는 게 확실해지면 전역으로 옮기면 돼요:
// 로컬 토큰 (처음)
$input-color-border-error: #ef4444;
// 전역 토큰 (재사용 확실해진 후)
$forms-color-border-error: #ef4444;
NameSpace로 충돌 방지하기
여러 프로젝트나 라이브러리 간 토큰 이름 충돌을 방지하기 위해 고유한 접두사를 추가해요.
System: 팀이나 프로젝트 이름으로 구분해요. 5자 이하로 하는 게 좋아요.
Theme: 다크/라이트 테마나 브랜드별 테마를 구분할 때 사용해요.
$lds-orange-color-action-primary: #f97316;
$lds-dark-color-background-primary: #1f2937;
$lds-light-color-text-primary: #111827;
빌드 시간에 자동 추가
매번 NameSpace를 직접 입력하는 대신, TailwindCSS의 prefix처럼 빌드 시간에 자동으로 추가하면 편리해요.
네이밍 규칙 정리
지금까지 배운 4단계 레벨을 조합하면 이런 순서로 토큰을 만들 수 있어요:
NameSpace-Object-Base-Modifier
실제 예시:
lds-button-color-background-hover
(모든 레벨 포함)color-action-primary
(NameSpace, Object 생략)button-spacing-padding-x
(NameSpace 생략)
레벨 | 위치 | 예시 |
---|---|---|
NameSpace | 가장 앞 | lds- , bendd- |
Object | 두 번째 | button- , input- |
Base | 중간 | color-action- , size- |
Modifier | 가장 뒤 | -hover , -large |
유연하게 접근하세요
모든 토큰에 4단계를 다 써야 하는 건 아니에요. 프로젝트 상황에 맞게 필요한 레벨만 사용하면 돼요.
실무에서 유용한 팁들
다크모드 대응
CSS 변수를 활용하면 테마별 토큰을 깔끔하게 관리할 수 있어요:
:root {
--color-background-primary: #ffffff;
--color-text-primary: #1f2937;
}
[data-theme="dark"] {
--color-background-primary: #1f2937;
--color-text-primary: #f9fafb;
}
자동화로 편리하게
NameSpace를 매번 입력하기 귀찮다면 빌드 시간에 자동으로 추가하세요:
// 개발 시
$color-primary: $blue-500;
// 빌드 후
$myapp-color-primary:$blue-500;
컴포넌트 라이브러리 연동
React나 Vue 컴포넌트와 토큰을 연결할 때:
// Button 컴포넌트 전용 토큰
--button-color-background-primary: var(--color-action-primary);
--button-color-background-primary-hover: var(--color-action-primary-hover);
--button-spacing-padding-x: var(--spacing-medium);
--button-spacing-padding-y: var(--spacing-small);
Class Variance Authority로 더 쉽게
Class Variance Authority (CVA)를 사용해도 토큰 기반의 컴포넌트 variant를 체계적으로 관리할 수 있어요:
import { cva } from 'class-variance-authority';
const buttonVariants = cva(
// 기본 토큰들
'bd-inline-flex bd-items-center bd-justify-center bd-rounded-md',
{
variants: {
variant: {
primary: 'bd-bg-primary bd-text-primary-foreground',
secondary: 'bd-bg-secondary bd-text-secondary-foreground',
outline: 'bd-border bd-border-input bd-bg-background',
},
size: {
sm: 'bd-h-9 bd-px-3',
md: 'bd-h-10 bd-px-4',
lg: 'bd-h-11 bd-px-8',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
// 사용 시
<Button variant="outline" size="lg">...</Button>
이렇게 하면 디자인 토큰의 네이밍 규칙과 컴포넌트 API가 일치해서 개발자 경험이 훨씬 좋아져요!
토큰 문서화하기
토큰이 많아질수록 문서화가 중요해요. Storybook이나 별도 문서 사이트에서 토큰 목록과 사용법을 정리해두면 팀원들이 쉽게 찾아 쓸 수 있어요.
마치며
디자인 토큰 네이밍이 처음엔 복잡해 보일 수 있지만, 체계적인 규칙을 따르면 일관성 있는 디자인 시스템을 만들 수 있어요.
가장 중요한 건 팀 전체가 같은 규칙을 따르는 거예요. 완벽한 토큰을 처음부터 만들려고 하지 말고, 프로젝트가 성장하면서 점진적으로 개선해 나가세요.
더 알아보기
디자인 토큰에 대해 더 자세히 알고 싶다면:
- 디자인 토큰 커뮤니티 그룹 - 표준 규격과 도구들
- 크로스 플랫폼 디자인 시스템, 1.5년의 기록 - 실무 경험담
- 효과적인 토큰 작성하기 - 네이밍 베스트 프랙티스