콘텐츠로 이동
Study Noteshadcn/ui

10. 내 테마 만들기

앞의 여섯 장을 한 파일에 모으는 장이다. globals.css 하나를 완성한다.

이 장에서 처음 나오는 말3개
프리셋Preset
미리 만들어 둔 테마 한 벌. shadcn add 로 통째로 받아 적용할 수 있다.
톤온톤Tone on Tone
한 색상에서 밝기만 다르게 뽑아 쓰는 배색. 브랜드 색 하나로 팔레트를 만드는 가장 안전한 방법.
대비 검사Contrast Check
배경과 글자의 밝기 차이가 기준(본문 4.5:1)을 넘는지 확인하는 것. 브라우저 개발자도구에 내장돼 있다.
브랜드 색과 중립색에서 시작해 의미 토큰·형태·글자를 정하고 다크 한 벌과 검사로 끝나는 테마 제작 일곱 단계

출발점은 두 개다 — 브랜드 색과 중립색. 나머지는 그 둘에서 파생하거나 기본값에서 시작하되, 대비·상태색·제품 성격에 맞춰 검증한다.

먼저 브랜드 색을 oklch로 변환해 밝기·채도·색상 전체를 출발값으로 삼는다. 버튼 배경에서는 색상 인상을 유지하면서 밝기와 채도를 조절한다.

--primary: oklch(0.55 0.20 264);
/* ↑ ↑
채도 색상 */
색상 각도대략
25빨강
60주황
95노랑
150초록
195청록
264파랑
300보라

hex 브랜드 색이 있다면 브라우저 개발자도구에서 색을 클릭해 oklch로 바꿔 보면 숫자가 나온다.

4장에서 본 그대로다. 현재 프리셋에서는 따뜻한 인상은 stone·taupe, 차가운 인상은 mist, 무채색은 neutral·zinc부터 비교한다. 미묘한 차이는 shadcn/create의 실제 화면으로 고른다.

zinc — 중립적인 출발점
50
100
200
300
400
500
600
700
800
900
950
  1. 라이트 값을 채운다

    app/globals.css
    @import "tailwindcss";
    :root {
    /* 브랜드 */
    --primary: oklch(0.55 0.20 264);
    --primary-foreground: oklch(0.99 0 0);
    /* 바탕과 표면 */
    --background: oklch(1 0 0);
    --foreground: oklch(0.145 0 0);
    --card: oklch(1 0 0);
    --card-foreground: oklch(0.145 0 0);
    --popover: oklch(1 0 0);
    --popover-foreground: oklch(0.145 0 0);
    /* 보조 */
    --secondary: oklch(0.97 0 0);
    --secondary-foreground: oklch(0.205 0 0);
    --muted: oklch(0.97 0 0);
    --muted-foreground: oklch(0.556 0 0);
    --accent: oklch(0.97 0 0);
    --accent-foreground: oklch(0.205 0 0);
    /* 경고 */
    --destructive: oklch(0.577 0.245 27);
    /* 선 */
    --border: oklch(0.922 0 0);
    --input: oklch(0.922 0 0);
    --ring: oklch(0.55 0.20 264); /* 포커스 링은 브랜드 색으로 */
    /* 형태 */
    --radius: 0.625rem;
    }
  2. 다크 값을 채운다 — 밝기를 뒤집고 채도를 낮춘다

    .dark {
    --primary: oklch(0.72 0.16 264); /* 밝게, 채도 ↓ */
    --primary-foreground: oklch(0.20 0.04 264);
    --background: oklch(0.145 0 0); /* 순수 검정 아님 */
    --foreground: oklch(0.985 0 0);
    --card: oklch(0.205 0 0); /* 배경보다 밝게 = 떠 보임 */
    --card-foreground: oklch(0.985 0 0);
    --popover: oklch(0.205 0 0);
    --popover-foreground: oklch(0.985 0 0);
    --secondary: oklch(0.269 0 0);
    --secondary-foreground: oklch(0.985 0 0);
    --muted: oklch(0.269 0 0);
    --muted-foreground: oklch(0.708 0 0);
    --accent: oklch(0.269 0 0);
    --accent-foreground: oklch(0.985 0 0);
    --destructive: oklch(0.704 0.191 22);
    --border: oklch(1 0 0 / 10%); /* 반투명 흰색이 자연스럽다 */
    --input: oklch(1 0 0 / 15%);
    --ring: oklch(0.72 0.16 264);
    }
  3. Tailwind에 연결한다 — 빠뜨리면 클래스가 안 생긴다

    @theme inline {
    --color-background: var(--background);
    --color-foreground: var(--foreground);
    --color-card: var(--card);
    --color-card-foreground: var(--card-foreground);
    --color-popover: var(--popover);
    --color-popover-foreground: var(--popover-foreground);
    --color-primary: var(--primary);
    --color-primary-foreground: var(--primary-foreground);
    --color-secondary: var(--secondary);
    --color-secondary-foreground: var(--secondary-foreground);
    --color-muted: var(--muted);
    --color-muted-foreground: var(--muted-foreground);
    --color-accent: var(--accent);
    --color-accent-foreground: var(--accent-foreground);
    --color-destructive: var(--destructive);
    --color-border: var(--border);
    --color-input: var(--input);
    --color-ring: var(--ring);
    --radius-sm: calc(var(--radius) * 0.6);
    --radius-md: calc(var(--radius) * 0.8);
    --radius-lg: var(--radius);
    --radius-xl: calc(var(--radius) * 1.4);
    --radius-2xl: calc(var(--radius) * 1.8);
    --font-sans: 'Pretendard Variable', system-ui, sans-serif;
    --font-mono: ui-monospace, Menlo, monospace;
    }
  4. 바탕에 적용한다

    app/layout.tsx
    <body className="bg-background text-foreground font-sans antialiased">

토큰을 바꾸면 화면이 어떻게 달라지는지 다시 한 번 —

← 눌러보세요
로그인
계정으로 계속하기
NewBeta
  • shadcn 공식 테마 편집기 — 웹에서 색을 조절하고 CSS를 복사한다

  • tweakcn — 커뮤니티 편집기. 미리 만든 테마가 많다

  • 프리셋 받기 — 남이 만든 테마를 통째로 가져온다

    터미널 창
    pnpm dlx shadcn@latest add https://tweakcn.com/r/themes/<이름>.json

처음에는 프리셋에서 시작해 브랜드 색만 바꾸는 편이 빠르다. 색 20개를 손으로 조화롭게 맞추는 건 생각보다 어렵다.

색

--primary 위에 --primary-foreground 대비가 4.5:1 이상인가. --muted-foreground가 라이트/다크 양쪽에서 읽히는가. --border가 배경과 구분되는가.

형태

--radius를 극단값(0, 2rem)으로 바꿔 봐도 화면이 안 깨지는가. 포커스 링이 잘리지 않는가 (overflow-hidden 주의).

글자

폰트 폴백이 있는가. 한글 본문에 break-keep이 걸려 있는가. 로딩 중 레이아웃이 덜컹거리지 않는가.

다크

새로고침해도 안 번쩍이는가. 카드가 배경과 구분되는가. 로고가 멀쩡한가.

브라우저 개발자도구에서 색 값 옆의 네모를 클릭하면 대비 비율이 바로 나온다. AA 통과 표시가 있으면 본문 기준(4.5:1)을 넘은 것이다.

대상필요한 대비
본문 글자4.5:1
큰 글자 (약 18.7px 이상 굵게, 24px 이상 보통 굵기)3:1
테두리·아이콘 등 비텍스트3:1

--muted-foreground가 가장 자주 걸린다. “흐리게” 하려다 안 읽히게 만드는 경우다.

  • 출발점은 두 개 — 브랜드 색과 중립색 계열. 나머지는 파생하거나 기본값에서 시작한다
  • 브랜드 색을 그대로 --primary에 넣지 않는다. foreground와 한 쌍으로 실제 대비를 측정한다
  • 다크 값은 밝기를 뒤집고 채도를 낮춘다. --card는 --background보다 밝게
  • 다크의 --border는 반투명 흰색(oklch(1 0 0 / 10%))이 자연스럽다
  • --ring을 브랜드 색으로 두면 한 줄로 앱 인상이 바뀐다
  • 프리셋에서 시작해 브랜드 색만 바꾸는 것이 실용적이다
  • 검사에서 가장 자주 걸리는 것은 --muted-foreground 대비