---
name: ui-theme-system
description: |
  트리거: "테마 시스템 만들어줘", "디자인 토큰", "theme", "색상 팔레트 설정", "다크모드 구현", "design token"
  수행: 컬러 팔레트·타이포그래피·간격 시스템을 CSS 변수 + Tailwind config로 정의. 다크모드 지원 포함
  출력: CSS 변수 파일, tailwind.config.ts, ThemeProvider 컴포넌트
---

# UI Theme System Generator

## 목적

프로젝트 전반에 일관된 디자인 언어를 적용하기 위한 테마 시스템을 구축한다.
CSS 커스텀 프로퍼티(변수)와 Tailwind CSS 설정을 연동하여 다크모드까지 지원하는 완결된 디자인 토큰 체계를 제공한다.

## 실행 절차

1. **브랜드 색상 수집**: 주 색상(primary), 보조 색상(secondary), 시스템 색상(성공/경고/오류) 확인
2. **컬러 팔레트 생성**: 기본 색상에서 50~950 단계 스케일 자동 생성
3. **타이포그래피 시스템 정의**: 폰트 패밀리, 크기 스케일, 행간, 자간
4. **간격 시스템 정의**: 4px 기반 스페이싱 스케일
5. **CSS 변수 파일 생성**: :root (라이트) + .dark (다크) 토큰
6. **Tailwind config 연동**: theme.extend에 CSS 변수 참조 추가
7. **ThemeProvider 컴포넌트 생성**: 테마 전환 로직 + 시스템 설정 감지

## 출력 형식

### 파일 구조
```
src/
  styles/
    tokens.css          ← CSS 커스텀 프로퍼티 (라이트/다크)
    typography.css      ← 타이포그래피 유틸리티 클래스
  theme/
    ThemeProvider.tsx   ← 테마 컨텍스트 + Provider
    useTheme.ts         ← 테마 훅
tailwind.config.ts      ← Tailwind 확장 설정
```

### CSS 토큰 예시

```css
/* src/styles/tokens.css */

/* ============================================================
   컬러 팔레트
   ============================================================ */
:root {
  /* Primary (Blue) */
  --color-primary-50:  #eff6ff;
  --color-primary-100: #dbeafe;
  --color-primary-200: #bfdbfe;
  --color-primary-300: #93c5fd;
  --color-primary-400: #60a5fa;
  --color-primary-500: #3b82f6;
  --color-primary-600: #2563eb;
  --color-primary-700: #1d4ed8;
  --color-primary-800: #1e40af;
  --color-primary-900: #1e3a8a;
  --color-primary-950: #172554;

  /* Neutral (Gray) */
  --color-neutral-0:   #ffffff;
  --color-neutral-50:  #f9fafb;
  --color-neutral-100: #f3f4f6;
  --color-neutral-200: #e5e7eb;
  --color-neutral-300: #d1d5db;
  --color-neutral-400: #9ca3af;
  --color-neutral-500: #6b7280;
  --color-neutral-600: #4b5563;
  --color-neutral-700: #374151;
  --color-neutral-800: #1f2937;
  --color-neutral-900: #111827;
  --color-neutral-950: #030712;

  /* Semantic Colors */
  --color-success-light: #f0fdf4;
  --color-success:       #16a34a;
  --color-success-dark:  #14532d;
  --color-warning-light: #fffbeb;
  --color-warning:       #d97706;
  --color-warning-dark:  #78350f;
  --color-error-light:   #fef2f2;
  --color-error:         #dc2626;
  --color-error-dark:    #7f1d1d;
  --color-info-light:    #eff6ff;
  --color-info:          #2563eb;
  --color-info-dark:     #1e3a8a;

  /* ============================================================
     시맨틱 테마 토큰 (라이트 모드)
     ============================================================ */
  --bg-default:          var(--color-neutral-0);
  --bg-subtle:           var(--color-neutral-50);
  --bg-muted:            var(--color-neutral-100);
  --bg-inverse:          var(--color-neutral-900);

  --text-primary:        var(--color-neutral-900);
  --text-secondary:      var(--color-neutral-600);
  --text-muted:          var(--color-neutral-400);
  --text-inverse:        var(--color-neutral-0);
  --text-link:           var(--color-primary-600);
  --text-link-hover:     var(--color-primary-700);

  --border-default:      var(--color-neutral-200);
  --border-strong:       var(--color-neutral-400);
  --border-focus:        var(--color-primary-500);

  --brand-default:       var(--color-primary-600);
  --brand-hover:         var(--color-primary-700);
  --brand-active:        var(--color-primary-800);
  --brand-subtle:        var(--color-primary-50);
  --brand-text:          var(--color-neutral-0);

  /* ============================================================
     타이포그래피
     ============================================================ */
  --font-sans:    'Pretendard Variable', 'Pretendard', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
  --font-mono:    'JetBrains Mono', 'Fira Code', Consolas, monospace;

  --text-xs:   0.75rem;   /* 12px */
  --text-sm:   0.875rem;  /* 14px */
  --text-base: 1rem;      /* 16px */
  --text-lg:   1.125rem;  /* 18px */
  --text-xl:   1.25rem;   /* 20px */
  --text-2xl:  1.5rem;    /* 24px */
  --text-3xl:  1.875rem;  /* 30px */
  --text-4xl:  2.25rem;   /* 36px */

  --leading-tight:  1.25;
  --leading-snug:   1.375;
  --leading-normal: 1.5;
  --leading-relaxed:1.625;

  /* ============================================================
     간격 시스템 (4px 기준)
     ============================================================ */
  --space-1:  0.25rem;   /* 4px  */
  --space-2:  0.5rem;    /* 8px  */
  --space-3:  0.75rem;   /* 12px */
  --space-4:  1rem;      /* 16px */
  --space-5:  1.25rem;   /* 20px */
  --space-6:  1.5rem;    /* 24px */
  --space-8:  2rem;      /* 32px */
  --space-10: 2.5rem;    /* 40px */
  --space-12: 3rem;      /* 48px */
  --space-16: 4rem;      /* 64px */

  /* ============================================================
     그림자
     ============================================================ */
  --shadow-sm:  0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md:  0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg:  0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl:  0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

  /* ============================================================
     모서리 반경
     ============================================================ */
  --radius-sm:   0.25rem;  /* 4px  */
  --radius-md:   0.375rem; /* 6px  */
  --radius-lg:   0.5rem;   /* 8px  */
  --radius-xl:   0.75rem;  /* 12px */
  --radius-2xl:  1rem;     /* 16px */
  --radius-full: 9999px;
}

/* ============================================================
   다크 모드 토큰 오버라이드
   ============================================================ */
.dark {
  --bg-default:  var(--color-neutral-950);
  --bg-subtle:   var(--color-neutral-900);
  --bg-muted:    var(--color-neutral-800);
  --bg-inverse:  var(--color-neutral-0);

  --text-primary:   var(--color-neutral-50);
  --text-secondary: var(--color-neutral-400);
  --text-muted:     var(--color-neutral-600);
  --text-inverse:   var(--color-neutral-900);
  --text-link:      var(--color-primary-400);
  --text-link-hover:var(--color-primary-300);

  --border-default: var(--color-neutral-700);
  --border-strong:  var(--color-neutral-500);

  --brand-default: var(--color-primary-500);
  --brand-hover:   var(--color-primary-400);
  --brand-active:  var(--color-primary-300);
  --brand-subtle:  var(--color-primary-950);

  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.3);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.3);
}
```

### Tailwind Config 예시

```ts
// tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  darkMode: 'class',
  content: ['./src/**/*.{ts,tsx,vue}'],
  theme: {
    extend: {
      colors: {
        // CSS 변수 참조
        bg: {
          default: 'var(--bg-default)',
          subtle:  'var(--bg-subtle)',
          muted:   'var(--bg-muted)',
          inverse: 'var(--bg-inverse)',
        },
        text: {
          primary:   'var(--text-primary)',
          secondary: 'var(--text-secondary)',
          muted:     'var(--text-muted)',
          inverse:   'var(--text-inverse)',
          link:      'var(--text-link)',
        },
        border: {
          default: 'var(--border-default)',
          strong:  'var(--border-strong)',
          focus:   'var(--border-focus)',
        },
        brand: {
          default: 'var(--brand-default)',
          hover:   'var(--brand-hover)',
          active:  'var(--brand-active)',
          subtle:  'var(--brand-subtle)',
          text:    'var(--brand-text)',
        },
      },
      fontFamily: {
        sans: ['var(--font-sans)'],
        mono: ['var(--font-mono)'],
      },
      boxShadow: {
        sm: 'var(--shadow-sm)',
        md: 'var(--shadow-md)',
        lg: 'var(--shadow-lg)',
        xl: 'var(--shadow-xl)',
      },
      borderRadius: {
        sm: 'var(--radius-sm)',
        md: 'var(--radius-md)',
        lg: 'var(--radius-lg)',
        xl: 'var(--radius-xl)',
        '2xl': 'var(--radius-2xl)',
      },
    },
  },
  plugins: [],
};

export default config;
```

### ThemeProvider 예시

```tsx
// src/theme/ThemeProvider.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';

type Theme = 'light' | 'dark' | 'system';

interface ThemeContextValue {
  theme: Theme;
  resolvedTheme: 'light' | 'dark';
  setTheme: (theme: Theme) => void;
}

const ThemeContext = createContext<ThemeContextValue | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setThemeState] = useState<Theme>(() => {
    if (typeof window === 'undefined') return 'system';
    return (localStorage.getItem('theme') as Theme) ?? 'system';
  });

  const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');

  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

    function applyTheme() {
      const isDark =
        theme === 'dark' || (theme === 'system' && mediaQuery.matches);
      setResolvedTheme(isDark ? 'dark' : 'light');
      document.documentElement.classList.toggle('dark', isDark);
    }

    applyTheme();
    mediaQuery.addEventListener('change', applyTheme);
    return () => mediaQuery.removeEventListener('change', applyTheme);
  }, [theme]);

  function setTheme(next: Theme) {
    setThemeState(next);
    localStorage.setItem('theme', next);
  }

  return (
    <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error('useTheme must be used within ThemeProvider');
  return ctx;
}
```

## 사용 예시

**입력:**
> "초록색 계열 주 색상, Noto Sans KR 폰트로 SaaS 대시보드 테마 시스템 만들어줘. 다크모드 필수."

**출력:**
- `tokens.css` - 초록 팔레트(primary) + Neutral 팔레트, 라이트/다크 토큰
- `tailwind.config.ts` - CSS 변수 연동
- `ThemeProvider.tsx` - 시스템 설정 감지 + localStorage 지속

## 주의사항

- 색상 스케일은 50~950 단계로 일관되게 생성 (Tailwind 표준 준수)
- CSS 변수명은 시맨틱 레이어 분리: 팔레트 변수(`--color-*`) vs 테마 변수(`--bg-*`, `--text-*`)
- `prefers-color-scheme` 미디어 쿼리로 시스템 설정 자동 감지
- Tailwind `darkMode: 'class'` 설정 시 `<html>` 요소에 `.dark` 클래스로 제어
- 토큰 변경은 CSS 변수만 수정하면 전체 적용 — 컴포넌트 코드 수정 불필요
