디자인 시스템은 한번 짓고 멈추는 건물이 아니라 해마다 가지를 뻗는 나무에 가깝습니다. 그래서 토큰과 컴포넌트를 늘리고 바꿀 때 화면이 깨지지 않게 다루는 코드 습관이 중요합니다. 이번에는 CSS 변수를 실제로 어떻게 확장하고 물러나게 하는지 코드로 보여드리겠습니다.
44.1 토큰 값만 바꾸는 패치
브랜드 파랑을 아주 조금 밝게 다듬는 정도라면 토큰의 값만 손대면 됩니다. 이름을 그대로 두면 그 토큰을 쓰던 모든 자리가 자동으로 새 값을 따라옵니다.
:root {
/* v1.2.0 -> v1.2.1 파랑 명도 소폭 상향 */
--color-primary: #2563eb;
}
.btn-primary {
background: var(--color-primary);
color: #fff;
}
버튼도 배지도 링크도 var(--color-primary) 하나만 바라보고 있으니, 값 한 줄을 고치면 화면 전체가 같이 바뀝니다. 쓰던 사람은 이름이 그대로라 아무것도 안 바꿔도 됩니다.
44.2 토큰을 더해서 넓히기
있던 것을 건드리지 않고 새 토큰을 얹는 확장은 가장 안전합니다. 기존 자리는 그대로 두고 새 쓰임에만 새 이름을 내주는 것이지요.
:root {
--color-primary: #2563eb;
--color-danger: #dc2626;
/* v1.3.0 추가: 성공, 경고 색 */
--color-success: #16a34a;
--color-warning: #d97706;
}
.badge-success { background: var(--color-success); }
.badge-warning { background: var(--color-warning); }
기존 토큰은 손대지 않았으니 옛 화면은 흔들림이 없고, 새 화면만 늘어난 색을 씁니다. 살을 붙이되 뼈대는 건드리지 않는 셈입니다.
44.3 이름을 바꿔야 할 때
가장 조심스러운 변화는 토큰의 이름을 바꾸는 일입니다. 흔한 실수는 옛 이름을 어느 날 갑자기 지워 버리는 것입니다.
/* 나쁜 예: brand 를 primary 로 바꾸며 옛 이름 삭제 */
:root {
--color-primary: #2563eb;
}
/* 그런데 예전 화면 수백 곳은 아직 이렇게 씁니다 */
.old-header {
background: var(--color-brand); /* 값이 없어 투명이 됨 */
}
정의가 사라진 var(--color-brand) 는 값을 못 찾아 배경이 사라집니다. 그 이름을 쓰던 화면이 한꺼번에 깨지는 순간입니다.
44.4 별칭으로 하위 호환 남기기
이름을 바꾸더라도 옛 이름을 곧장 지우지 않고 새 이름을 가리키는 별칭으로 남겨 두면 아무것도 깨지지 않습니다. 옛 이름은 새 값을 그대로 물려받습니다.
:root {
--color-primary: #2563eb;
/* @deprecated v2.0.0에 제거 예정, --color-primary 사용 */
--color-brand: var(--color-primary);
}
이제 옛 이름 --color-brand 도 새 이름과 똑같은 파랑을 냅니다. 옛 화면은 멀쩡히 돌아가고, 새 화면은 --color-primary 를 쓰면 됩니다. 낡은 다리를 헐기 전에 새 다리를 먼저 놓아 둔 셈이지요.
44.5 점진 이관과 저물게 하기
별칭을 둔 동안 옛 이름을 쓰던 자리를 천천히 새 이름으로 옮깁니다. 주석으로 언제 사라질지 미리 못박아 두면 서두르지 않고 이관할 수 있습니다.
/* 이관 중: 아래 두 줄은 결국 같은 색 */
.old-header { background: var(--color-brand); } /* 옛 자리, 이관 대상 */
.new-header { background: var(--color-primary); } /* 새 자리 */
/* 모든 자리를 옮긴 뒤 v2.0.0에서 별칭 제거 */
:root {
--color-primary: #2563eb;
/* --color-brand: var(--color-primary); 삭제 완료 */
}
모두가 새 다리로 건넌 것을 확인한 다음에야 낡은 다리를 조용히 거둡니다. 예고 없이 걷어 냈다면 아직 옛 다리 위에 있던 화면들이 물에 빠졌을 겁니다.
44.6 변화의 무게를 판으로 매기기
변화가 일으키는 파장에 따라 판 번호를 다르게 올립니다. 값만 다듬으면 끝자리, 토큰을 더하면 가운데 자리, 이름을 깨뜨리면 앞자리를 올리는 감각입니다.
/* 1.2.0 -> 1.2.1 패치: --color-primary 값만 조정 */
/* 1.2.1 -> 1.3.0 마이너: --color-success 토큰 추가 */
/* 1.3.0 -> 2.0.0 메이저: --color-brand 이름 제거(호환 깨짐) */
번호만 봐도 이 판으로 올릴 때 내 화면이 흔들릴지 아닐지 짐작됩니다. 끝자리만 올랐다면 마음 놓고 따라가고, 앞자리가 올랐다면 무엇이 깨졌는지 확인하고 옮겨 갑니다.
44.7 테마를 층으로 얹어 확장
색을 늘리는 또 다른 방향은 테마입니다. 밑바탕 값을 :root 에 두고, 다크 테마는 그 위에 [data-theme] 층을 얹어 같은 토큰 이름의 값만 갈아 끼웁니다.
:root {
--bg: #ffffff;
--text: #111827;
--color-primary: #2563eb;
}
[data-theme="dark"] {
--bg: #0f172a;
--text: #e5e7eb;
--color-primary: #60a5fa;
}
body {
background: var(--bg);
color: var(--text);
}
컴포넌트는 var(--bg) 와 var(--text) 만 바라볼 뿐 자기가 밝은지 어두운지 모릅니다. 최상위에 data-theme="dark" 를 걸면 같은 토큰이 어두운 값으로 바뀌어 화면 전체가 갈아입습니다. 부품을 하나도 안 고치고 테마를 통째로 더한 것이지요.
44.8 컴포넌트도 변형으로 넓히기
토큰뿐 아니라 컴포넌트를 넓힐 때도 원래 클래스를 건드리지 않고 변형 클래스를 더합니다. 기본형은 그대로 두고 크기나 모양만 얹는 것입니다.
.btn {
padding: 10px 16px;
border-radius: 6px;
background: var(--color-primary);
color: #fff;
}
/* v1.4.0 추가: 작은 변형과 외곽선 변형 */
.btn--sm { padding: 6px 12px; font-size: 13px; }
.btn--outline {
background: transparent;
color: var(--color-primary);
border: 1px solid var(--color-primary);
}
기존 .btn 을 쓰던 화면은 아무 영향이 없고, 새 화면만 .btn .btn--outline 처럼 변형을 덧붙여 씁니다. 확장이란 대개 이렇게 기존 위에 조용히 얹는 일입니다.
44.9 정리
시스템을 키우는 요령은 결국 무엇도 갑자기 사라지지 않게 하는 데 있습니다. 값만 바꾸는 패치는 이름을 지키고, 토큰과 변형을 더하는 확장은 기존을 안 건드리며, 이름을 바꿀 때는 별칭으로 하위 호환을 남기고 주석으로 저물 날을 예고합니다. 이 규율만 지키면 시스템은 시간이 흘러도 어질러지지 않고 건강하게 자랍니다.