# 컴포넌트 스펙 모든 컴포넌트는 `cn()` 유틸리티(`src/utils/cn.ts`)를 사용하고, CSS 변수 토큰을 통해 색상을 지정한다. --- ## Button ### 변형 (Variant) | Variant | 배경 | 텍스트 | 테두리 | 용도 | |---------|------|--------|--------|------| | `primary` | `--color-primary` | white | none | 주요 CTA | | `secondary` | `--color-secondary` | `--color-secondary-text` | none | 보조 액션 | | `accent` | `--color-accent` | `--color-accent-text` | none | 포인트 액션 | | `outline` | transparent | `--color-primary` | `--color-primary` | 조용한 강조 | | `ghost` | transparent | `--color-text-secondary` | none | 최소 강조 | | `danger` | `--color-danger` | white | none | 삭제, 경고 | ### 사이즈 | Size | padding | font-size | height | border-radius | |------|---------|-----------|--------|---------------| | `sm` | `px-3 py-1.5` | `text-sm` | 32px | `rounded-md` | | `md` | `px-4 py-2` | `text-base` | 40px | `rounded-lg` | | `lg` | `px-6 py-3` | `text-lg` | 48px | `rounded-lg` | ### 상태 - **disabled**: `opacity-50 cursor-not-allowed pointer-events-none` - **loading**: 스피너 아이콘 표시, 텍스트 유지, disabled 처리 - **hover**: 각 variant에 맞는 `-hover` 토큰 적용 - **active**: 각 variant에 맞는 `-active` 토큰 적용 - **focus-visible**: `ring-2 ring-[var(--color-primary)] ring-offset-2` ### 구현 예시 ```tsx const buttonVariants = { primary: 'bg-[var(--color-primary)] text-white hover:bg-[var(--color-primary-hover)] active:bg-[var(--color-primary-active)]', secondary: 'bg-[var(--color-secondary)] text-[var(--color-secondary-text)] hover:bg-[var(--color-secondary-hover)]', accent: 'bg-[var(--color-accent)] text-[var(--color-accent-text)] hover:bg-[var(--color-accent-hover)]', outline: 'border border-[var(--color-primary)] text-[var(--color-primary)] hover:bg-[var(--color-primary-subtle)]', ghost: 'text-[var(--color-text-secondary)] hover:bg-[var(--color-bg-surface)] hover:text-[var(--color-text-primary)]', danger: 'bg-[var(--color-danger)] text-white hover:opacity-90', }; ``` --- ## Badge 상태, 카테고리, 태그 표시에 사용한다. ### 변형 | Variant | 배경 | 텍스트 | |---------|------|--------| | `default` | `--color-bg-elevated` | `--color-text-primary` | | `primary` | `--color-primary-subtle` | `--color-primary-text` | | `success` | #DCFCE7 | #166534 | | `warning` | #FEF9C3 | #854D0E | | `danger` | #FEE2E2 | #991B1B | | `info` | #E0F2FE | #075985 | ### 스펙 - 패딩: `px-2 py-0.5` - 폰트: `text-xs font-medium` - 모양: `rounded-full` (pill) 또는 `rounded-md` (square) --- ## Card 콘텐츠를 담는 기본 컨테이너. ### 구조 ```html

제목

부제목

``` ### 변형 | Variant | 설명 | |---------|------| | `default` | border + shadow-sm | | `elevated` | shadow-md, 모달 내부 카드 | | `flat` | border만, shadow 없음 | | `interactive` | hover 시 shadow-md + scale-[1.01] | --- ## Input ### 기본 스펙 ```html

도움말 텍스트

``` ### 상태 | 상태 | 테두리 색상 | |------|-------------| | 기본 | `--color-border` | | 포커스 | `--color-primary` (ring) | | 오류 | `--color-danger` | | 성공 | `--color-success` | | 비활성 | `--color-border` + opacity-50 | ### 사이즈 | Size | padding | font-size | height | |------|---------|-----------|--------| | `sm` | `px-3 py-1.5` | `text-sm` | 32px | | `md` | `px-3 py-2` | `text-base` | 40px | | `lg` | `px-4 py-3` | `text-lg` | 48px | --- ## Modal ### 구조 ```html

제목

...
``` ### 사이즈 | Size | max-width | |------|-----------| | `sm` | `max-w-sm` (384px) | | `md` | `max-w-md` (448px) | | `lg` | `max-w-lg` (512px) | | `xl` | `max-w-xl` (576px) | ### 접근성 - `role="dialog"`, `aria-modal="true"`, `aria-labelledby` 필수 - 열릴 때 첫 번째 포커스 가능 요소로 포커스 이동 - Escape 키로 닫기 - Backdrop 클릭 시 닫기 (선택적) - 열린 동안 body 스크롤 잠금 --- ## Toast ### 위치 `fixed bottom-4 right-4 z-[60]` (Modal보다 위) ### 변형 | Variant | 아이콘 | 색상 | |---------|--------|------| | `success` | CheckCircle | `--color-success` | | `warning` | AlertTriangle | `--color-warning` | | `error` | XCircle | `--color-danger` | | `info` | Info | `--color-info` | ### 구조 ```html
``` ### 동작 - 기본 자동 닫힘: 4000ms - 진입: `translate-y-2 opacity-0` → `translate-y-0 opacity-100` - 퇴장: `translate-y-0 opacity-100` → `translate-y-2 opacity-0` - 트랜지션: `duration-200 ease-out`