콘텐츠로 이동
Study Noteshadcn/ui

2. 테마 = 리소스 목록

테마는 색 설정이 아니다. 앱의 시각적 결정 전체를 담는 목록이다.

이 장에서 처음 나오는 말4개
리소스Resource
이 덱에서 쓰는 말. 테마가 소유하는 항목 한 종류를 뜻한다 — 색, 형태, 글자, 컴포넌트, 아이콘·폰트 다섯 가지.
의미 토큰Semantic Token
"이 색이 무슨 역할인가"로 이름 붙인 토큰. --zinc-900(무슨 색인가)이 아니라 --primary(무슨 역할인가).
원시 토큰Primitive Token
값 그 자체에 이름을 붙인 토큰. --zinc-900: #18181b. 팔레트에 해당한다.
하드코딩Hardcoding
값을 이름 없이 코드에 직접 박아 넣는 것. bg-[#18181b] 처럼. 나중에 바꾸려면 전부 찾아야 한다.

문제부터 — 이름이 없으면 무슨 일이 생기나

섹션 제목: “문제부터 — 이름이 없으면 무슨 일이 생기나”

프로젝트 6개월 차, 디자이너가 말한다. “브랜드 색을 남색에서 초록으로 바꿉시다.”

<button className="bg-blue-600 text-white">저장</button>
<div className="border-blue-600">…</div>
<a className="text-blue-600">더 보기</a>
<span className="bg-blue-50 text-blue-900">진행 중</span>
  • blue-600이 몇 군데 있는지 아무도 모른다. 전체 검색하면 214군데가 나온다
  • 그중 어떤 게 “브랜드 색”이고 어떤 게 “그냥 파란색 안내 문구”인지 구분이 안 된다
  • blue-50, blue-900처럼 같은 결정에서 파생된 것들도 따로 찾아야 한다
  • 다 바꾸고 나면 다크 모드가 깨진다. 다크용 dark:bg-blue-400도 따로 있었기 때문

작업 견적: 2~3일 + 회귀 테스트.

아래 데모에서 버튼을 눌러보자. 카드·버튼·탭·진행바가 전부 바뀐다. 하지만 HTML은 한 글자도 바뀌지 않는다. <button class="ui-btn ui-btn--default">저장</button> 그대로다.

← 눌러보세요
로그인
계정으로 계속하기
NewBeta

Material Design과 사내 SI 스타일이 이렇게 다른데도 마크업이 같다는 게 요점이다. 바뀐 것은 이름에 매달린 값뿐이다.

“테마 = 색”이라고 생각하면 절반만 관리하게 된다. 실제로는 다섯 종류다.

테마를 색·형태·글자·컴포넌트·아이콘 폰트 파일 다섯 종류의 리소스 목록으로 펼친 그림

색 — 4장

--primary, --background, --destructive … 짝(배경/글자)으로 관리하는 것이 규칙이다.

형태 — 5장

--radius 하나에서 전체 둥글기가 파생된다. 간격·테두리·그림자도 여기 속한다.

글자 — 6장

폰트 패밀리, 크기 스케일, 줄 간격. 한글은 영문과 규칙이 다르다.

컴포넌트 — 7장

button.tsx 안의 “outline이란 이런 것” 같은 결정. 코드지만 명백히 테마의 일부다.

아이콘 · 폰트 파일 — 8장

아이콘 세트 선택과 굵기, 폰트 파일을 어떻게 내려받게 할 것인가.

그리고 두 벌 — 9장

위 다섯 개를 라이트/다크 두 벌로 갖는 것. 이름은 그대로, 값만 다르다.

토큰에는 두 층이 있다. 이 구분을 놓치면 이름을 붙여도 소용이 없다.

원시 토큰이 의미 토큰을 거쳐 컴포넌트로 가야 하고, 컴포넌트가 원시 토큰을 직접 쓰면 안 된다는 것을 보여주는 그림
원시 토큰의미 토큰
예--zinc-900, --blue-500--primary, --muted, --destructive
답하는 질문무슨 색인가무슨 역할인가
개수많다 (팔레트 전체)적다 (20개 안팎)
테마마다대체로 같다바뀐다
컴포넌트가 쓰는 것❌✅

자료를 찾다 보면 원시 → 의미 → 컴포넌트 3층 구조를 보게 된다. --button-bg: var(--primary) 같은 층을 하나 더 두는 것이다.

대부분의 프로젝트는 2층이면 충분하다. 3층은 컴포넌트가 수백 개고 팀이 여러 개로 나뉘어 서로 다른 속도로 움직일 때 의미가 생긴다. 처음부터 만들면 관리할 이름만 3배가 된다.

“같은 컴포넌트”라는 말의 무게

섹션 제목: ““같은 컴포넌트”라는 말의 무게”

아래 넷은 완전히 같은 마크업이다. 값만 다르다.

shadcn/ui 기본 (zinc)
로그인
계정으로 계속하기
Material Design 3
로그인
계정으로 계속하기
Radix Colors (indigo)
로그인
계정으로 계속하기
사내 엔터프라이즈
로그인
계정으로 계속하기

여기서 읽어야 할 것은 “예쁘다”가 아니라 —

  • 각 열은 다른 디자인 시스템이다. Material은 모서리가 크고 보라 계열, 사내 SI는 각지고 진한 파랑
  • 그런데 HTML을 새로 쓰지 않았다. 컴포넌트를 갈아엎지도 않았다
  • 즉 디자인 시스템 교체 = 토큰 교체로 만들 수 있다

“테마를 만든다”는 말을 실무 동작으로 풀면 이렇다.

동작무슨 뜻인가어디서
정의한다리소스마다 이름과 값을 정한다globals.css의 :root
두 벌로 만든다같은 이름에 다크용 값을 하나 더 준다globals.css의 .dark
연결한다Tailwind가 bg-primary 클래스를 만들게 한다@theme inline 블록
강제한다컴포넌트가 이름만 쓰도록 규칙을 세운다코드 리뷰 · 린트
배포한다다른 앱·팀이 같은 테마를 받게 한다레지스트리 (11장)

세 번째 “연결한다”가 자주 빠진다. CSS 변수를 정의하는 것만으로는 bg-primary 유틸리티 클래스가 생기지 않는다 — 4장에서 자세히 본다.

  • 테마는 색 설정이 아니라 앱의 시각적 결정 전체를 담는 목록이다
  • 가치는 예쁨이 아니라 바꾸는 비용을 상수로 만드는 것 — 파일이 몇 개든 두 줄
  • 테마가 소유하는 리소스 다섯: 색 · 형태 · 글자 · 컴포넌트 · 아이콘/폰트
  • 토큰은 원시(무슨 색인가) → 의미(무슨 역할인가) 두 층. 컴포넌트는 의미 토큰만 쓴다
  • 3층 구조는 대개 필요 없다
  • 관리한다는 것 = 정의 → 두 벌 → 연결 → 강제 → 배포