쇼케이스 (/design-system.html): - 별도 Vite entry (System Flow 패턴 재사용, 메인 SPA 분리) - 10개 섹션: Intro / Token / Typography / Badge / Button / Form / Card / Layout / Catalog (19+) / Guide - 추적 ID 체계 (TRK-CATEGORY-SLUG): - hover 시 툴팁 + "ID 복사 모드"에서 클릭 시 클립보드 복사 - URL hash 딥링크 (#trk=TRK-BADGE-critical-sm) 스크롤+하이라이트 - 산출문서/논의에서 특정 변형 정확히 참조 가능 - Dark/Light 테마 토글로 양쪽 시각 검증 신규 공통 컴포넌트: - Button (@shared/components/ui/button.tsx) - 5 variant × 3 size = 15 변형 - primary/secondary/ghost/outline/destructive × sm/md/lg - Input / Select / Textarea / Checkbox / Radio - Input · Select 공통 inputVariants 공유 (sm/md/lg × default/error/success) - PageContainer / PageHeader / Section (shared/components/layout/) - PageContainer: size sm/md/lg + fullBleed (지도/풀화면 예외) - PageHeader: title + description + icon + demo 배지 + actions 슬롯 - Section: Card + CardHeader + CardTitle + CardContent 단축 variants.ts 확장: - buttonVariants / inputVariants / pageContainerVariants CVA 정의 - Button/Input/Select는 variants.ts에서 import하여 fast-refresh 경고 회피 빌드 검증 완료: - TypeScript 타입 체크 통과 - ESLint 통과 (경고 0) - vite build: designSystem-*.js 54KB (메인 SPA와 분리) 이 쇼케이스가 확정된 후 실제 40+ 페이지 마이그레이션 진행 예정.
161 lines
6.8 KiB
TypeScript
161 lines
6.8 KiB
TypeScript
import { TrkSectionHeader, Trk } from '../lib/Trk';
|
|
|
|
/** 시맨틱 색상 토큰 목록. theme.css :root 정의 기준 */
|
|
const SURFACE_TOKENS = [
|
|
{ id: 'background', label: '--background', desc: '페이지 최하위 배경' },
|
|
{ id: 'card', label: '--card', desc: 'Card 컴포넌트 배경' },
|
|
{ id: 'surface-raised', label: '--surface-raised', desc: '카드 내부 섹션' },
|
|
{ id: 'surface-overlay', label: '--surface-overlay', desc: '모달/오버레이' },
|
|
{ id: 'muted', label: '--muted', desc: '약한 배경 (inactive/disabled)' },
|
|
{ id: 'popover', label: '--popover', desc: '드롭다운/툴팁 배경' },
|
|
];
|
|
|
|
const TEXT_TOKENS = [
|
|
{ id: 'text-heading', label: '--text-heading', example: '제목 Heading', desc: 'h1~h4, 강조 텍스트' },
|
|
{ id: 'text-label', label: '--text-label', example: '본문 Label', desc: '일반 본문, 라벨' },
|
|
{ id: 'text-hint', label: '--text-hint', example: '보조 Hint', desc: '설명, 힌트, placeholder' },
|
|
{ id: 'text-on-vivid', label: '--text-on-vivid', example: '컬러풀 위 텍스트', desc: '-600/-700 진한 배경 위 (버튼)', bg: '#2563eb' },
|
|
{ id: 'text-on-bright', label: '--text-on-bright', example: '밝은 위 텍스트', desc: '-300/-400 밝은 배경 위 (배지)', bg: '#60a5fa' },
|
|
];
|
|
|
|
const BRAND_COLORS = [
|
|
{ id: 'primary', label: '--primary', desc: '기본 강조색 (파랑)' },
|
|
{ id: 'destructive', label: '--destructive', desc: '위험/삭제 (빨강)' },
|
|
{ id: 'ring', label: '--ring', desc: 'focus ring' },
|
|
{ id: 'border', label: '--border', desc: '경계선' },
|
|
];
|
|
|
|
const CHART_COLORS = [
|
|
{ id: 'chart-1', label: '--chart-1' },
|
|
{ id: 'chart-2', label: '--chart-2' },
|
|
{ id: 'chart-3', label: '--chart-3' },
|
|
{ id: 'chart-4', label: '--chart-4' },
|
|
{ id: 'chart-5', label: '--chart-5' },
|
|
];
|
|
|
|
const RADIUS_SCALE = [
|
|
{ id: 'sm', label: 'radius-sm', cls: 'rounded-sm', px: 'calc(radius - 4px)' },
|
|
{ id: 'md', label: 'radius-md', cls: 'rounded-md', px: 'calc(radius - 2px)' },
|
|
{ id: 'lg', label: 'radius-lg', cls: 'rounded-lg', px: '0.5rem (default)' },
|
|
{ id: 'xl', label: 'radius-xl', cls: 'rounded-xl', px: 'calc(radius + 4px)' },
|
|
{ id: 'full', label: 'rounded-full', cls: 'rounded-full', px: '9999px' },
|
|
];
|
|
|
|
const SPACING_SCALE = [
|
|
{ id: '1', size: 4 }, { id: '2', size: 8 }, { id: '3', size: 12 },
|
|
{ id: '4', size: 16 }, { id: '5', size: 20 }, { id: '6', size: 24 },
|
|
{ id: '8', size: 32 }, { id: '10', size: 40 },
|
|
];
|
|
|
|
export function TokenSection() {
|
|
return (
|
|
<>
|
|
<TrkSectionHeader
|
|
id="TRK-SEC-token"
|
|
title="테마 · 시맨틱 토큰"
|
|
description="모든 색상/여백은 CSS 변수로 관리. 다크/라이트 모드는 값만 바뀌고 이름은 동일."
|
|
/>
|
|
|
|
{/* Surface 토큰 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-4">Surface / 배경</h3>
|
|
<div className="ds-grid ds-grid-3">
|
|
{SURFACE_TOKENS.map((t) => (
|
|
<Trk key={t.id} id={`TRK-TOKEN-${t.id}`} className="ds-sample">
|
|
<div
|
|
className={`h-16 rounded-md border border-slate-700/40 mb-2`}
|
|
style={{ background: `var(--${t.id})` }}
|
|
/>
|
|
<div className="text-xs text-heading font-semibold">{t.label}</div>
|
|
<div className="text-[10px] text-hint">{t.desc}</div>
|
|
<div className="ds-sample-label">TRK-TOKEN-{t.id}</div>
|
|
</Trk>
|
|
))}
|
|
</div>
|
|
|
|
{/* 텍스트 토큰 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-6">Text / 텍스트</h3>
|
|
<div className="ds-grid ds-grid-3">
|
|
{TEXT_TOKENS.map((t) => (
|
|
<Trk key={t.id} id={`TRK-TOKEN-${t.id}`} className="ds-sample">
|
|
<div
|
|
className="h-16 rounded-md flex items-center justify-center text-sm font-semibold mb-2"
|
|
style={{
|
|
background: t.bg ?? 'var(--surface-overlay)',
|
|
color: `var(--${t.id})`,
|
|
}}
|
|
>
|
|
{t.example}
|
|
</div>
|
|
<div className="text-xs text-heading font-semibold">{t.label}</div>
|
|
<div className="text-[10px] text-hint">{t.desc}</div>
|
|
<div className="ds-sample-label">TRK-TOKEN-{t.id}</div>
|
|
</Trk>
|
|
))}
|
|
</div>
|
|
|
|
{/* 브랜드 색 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-6">Brand / 기능색</h3>
|
|
<div className="ds-grid ds-grid-4">
|
|
{BRAND_COLORS.map((t) => (
|
|
<Trk key={t.id} id={`TRK-TOKEN-${t.id}`} className="ds-sample">
|
|
<div
|
|
className="h-12 rounded-md border border-slate-700/40 mb-2"
|
|
style={{ background: `var(--${t.id})` }}
|
|
/>
|
|
<div className="text-xs text-heading font-semibold">{t.label}</div>
|
|
<div className="text-[10px] text-hint">{t.desc}</div>
|
|
</Trk>
|
|
))}
|
|
</div>
|
|
|
|
{/* Chart 색 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-6">Chart / 차트 팔레트</h3>
|
|
<Trk id="TRK-TOKEN-chart-palette" className="ds-sample">
|
|
<div className="flex gap-2">
|
|
{CHART_COLORS.map((t) => (
|
|
<div key={t.id} className="flex-1 text-center">
|
|
<div
|
|
className="h-12 rounded-md border border-slate-700/40 mb-1"
|
|
style={{ background: `var(--${t.id})` }}
|
|
/>
|
|
<div className="text-[10px] text-hint font-mono">{t.label}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="ds-sample-label">TRK-TOKEN-chart-palette</div>
|
|
</Trk>
|
|
|
|
{/* Radius 스케일 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-6">Radius / 모서리 반경</h3>
|
|
<Trk id="TRK-TOKEN-radius-scale" className="ds-sample">
|
|
<div className="flex gap-4 items-end">
|
|
{RADIUS_SCALE.map((r) => (
|
|
<div key={r.id} className="flex-1 text-center">
|
|
<div className={`h-16 bg-blue-500 ${r.cls} mb-1`} />
|
|
<div className="text-[11px] text-heading">{r.label}</div>
|
|
<div className="text-[9px] text-hint font-mono">{r.px}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Trk>
|
|
|
|
{/* Spacing 스케일 */}
|
|
<h3 className="text-sm font-semibold text-heading mb-2 mt-6">Spacing / 간격 스케일</h3>
|
|
<Trk id="TRK-TOKEN-spacing-scale" className="ds-sample">
|
|
<div className="space-y-1.5">
|
|
{SPACING_SCALE.map((s) => (
|
|
<div key={s.id} className="flex items-center gap-3">
|
|
<code className="text-[10px] text-hint font-mono w-12">p-{s.id}</code>
|
|
<div
|
|
className="h-4 bg-blue-500 rounded-sm"
|
|
style={{ width: `${s.size}px` }}
|
|
/>
|
|
<span className="text-[10px] text-hint font-mono">{s.size}px</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Trk>
|
|
</>
|
|
);
|
|
}
|