--- id: frontend-tailwind-architecture title: Tailwind CSS — 디자인 토큰 / 컴포넌트 / 조직 category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [frontend, tailwind, css, design-tokens, vibe-coding] tech_stack: { language: "TS / CSS / Tailwind", applicable_to: ["Web"] } applied_in: [] aliases: [tailwind, utility-first, cva, tw-merge, design system] --- # Tailwind Architecture > Utility-first. **className spam 이 아닌 컴포넌트 추출 + cva 변형**. `tailwind.config.ts` 의 theme 가 디자인 시스템. v4 = CSS-first config. ## 📖 핵심 개념 - Utility-first: 미리 만든 작은 클래스 조합. - Theme: 색 / 크기 / 폰트 = 디자인 토큰. - `cva`: variants + compoundVariants 로 component variant 정의. - `tw-merge`: 충돌하는 클래스 (`p-2 p-4`) 자동 정리. ## 💻 코드 패턴 ### theme = 디자인 토큰 (v3) ```ts // tailwind.config.ts import type { Config } from 'tailwindcss'; export default { content: ['./src/**/*.{ts,tsx}'], theme: { extend: { colors: { brand: { 50: '#eff6ff', 500: '#3b82f6', 900: '#1e3a8a' }, surface: 'hsl(var(--surface))', // CSS var = dark mode 쉬움 }, borderRadius: { sm: '4px', md: '8px', lg: '16px' }, fontFamily: { sans: ['Inter', 'system-ui'] }, spacing: { 18: '4.5rem' }, }, }, plugins: [require('@tailwindcss/forms')], } satisfies Config; ``` ### v4 — CSS-first ```css /* app.css */ @import "tailwindcss"; @theme { --color-brand-500: #3b82f6; --radius-md: 8px; --font-sans: Inter, system-ui; } ``` ### Component with cva ```tsx import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/cn'; const button = cva( 'inline-flex items-center justify-center rounded-md font-medium transition focus-visible:ring-2', { variants: { variant: { primary: 'bg-brand-500 text-white hover:bg-brand-600', secondary: 'bg-surface text-foreground hover:bg-muted', ghost: 'hover:bg-muted', }, size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4', lg: 'h-12 px-6 text-lg', }, fullWidth: { true: 'w-full' }, }, defaultVariants: { variant: 'primary', size: 'md' }, } ); type Props = React.ButtonHTMLAttributes & VariantProps; export function Button({ className, variant, size, fullWidth, ...rest }: Props) { return