Use when bootstrapping or aligning a React/Vite/TS frontend to the Marketing AX design system — LG Red theme, sidebar layout, Card/Badge/Button/Toast/Modal components, and global styles.
Use this skill when the user asks to:
npm install clsx react-router-dom
| 패키지 | 용도 | 사용처 |
|---|---|---|
clsx |
className 조합 (cn() 유틸리티) |
| 모든 UI 컴포넌트 |
react-router-dom | NavLink, useNavigate, Outlet | AppShell 사이드바, GearMenu, 라우터 |
| 패키지 | 비고 |
|---|---|
react | 18+ |
react-dom | 18+ |
typescript | |
vite | |
@vitejs/plugin-react |
전체 토큰은 references/tokens.css 참조. 아래는 용도별 정리.
LG Red (브랜드)
| Token | Value | Usage |
|---|---|---|
--lg-red-900 | #6d000f | 사이드바 그라디언트 끝, 배경 radial-gradient |
--lg-red-800 | #8a0013 | 예비 |
--lg-red-700 | #a50019 | 사이드바 그라디언트 시작, 프라이머리 호버 |
--lg-red-600 | #c60021 | 프라이머리 색상 (버튼, 링크) |
--lg-red-500 | #d7182a | 차트 색상 (Recharts fill/stroke) |
--lg-red-400 | #e84a5b | 예비 (밝은 강조) |
--lg-red-300 | #f07a86 | 예비 |
--lg-red-100 | #fde8eb | 프라이머리 뱃지 배경, danger 뱃지 배경 |
Neutral (그레이스케일)
| Token | Value | Usage |
|---|---|---|
--neutral-950 | #111111 | 카드 그림자 기준색 (rgba) |
--neutral-900 | #1d1d1f | 본문 텍스트, 제목 |
--neutral-700 | #4b4b52 | 보조 텍스트 (subtitle, description) |
--neutral-500 | #7a7a84 | 비활성 텍스트, placeholder, 날짜 |
--neutral-200 | #e7e7eb | 보더, 구분선 |
--neutral-100 | #f4f4f7 | 서브틀 배경, secondary 버튼 배경 |
--white | #ffffff | 카드 배경, 버튼 텍스트, 사이드바 텍스트 |
Semantic (상태)
| Token | Value | Usage |
|---|---|---|
--success | #157347 | 성공 뱃지 텍스트 |
--warning | #b26a00 | 경고 뱃지 텍스트 |
--danger | #b42318 | 위험 뱃지 텍스트, danger 버튼 배경 |
컴포넌트 내 하드코딩 색상 (tokens.css 외)
| 색상 | 위치 | 용도 |
|---|---|---|
#e7f6ee | badge-success 배경 | 연한 초록 |
#fff4e1 | badge-warning 배경 | 연한 주황 |
#fbd6db | nav-item 기본 텍스트 | 사이드바 비활성 링크 |
#e0dce2 | card:hover border | 호버 시 보더 색 |
#fef2f2 | toast destructive 배경 | 연한 빨강 |
#fca5a5 | toast destructive 보더 | 밝은 빨강 |
#b91c1c | toast destructive 제목 | 진한 빨강 |
#991b1b | toast destructive 설명 | 더 진한 빨강 |
| 용도 | 폰트 |
|---|---|
| UI 본문 | Noto Sans KR → Pretendard → Apple SD Gothic Neo → Segoe UI |
| 영문 보조 | Inter (300~800 weight) |
| 코드/데이터 | JetBrains Mono (400, 600, 700) |
| 한국어 세리프 | Noto Serif KR (특수 표시용) |
| 요소 | 값 |
|---|---|
| 사이드바 폭 | 260px |
| 카드 border-radius | 14px |
| 버튼 border-radius | 10px (compact: 8px) |
| 뱃지 border-radius | 999px (pill) |
| 반응형 breakpoint | max-width: 1100px |
| 모달 기본 폭 | 480px (max 90vw) |
| 토스트 최대 폭 | 380px |
| 레이어 | z-index |
|---|---|
| Toast | 9999 |
| Modal overlay | 1000 |
| Dropdown menu | 100 |
| Topbar | implicit (DOM order) |
frontend/
├── index.html ← Google Fonts 로딩
├── src/
│ ├── main.tsx ← tokens.css + globals.css import
│ ├── shared/
│ │ ├── lib/cn.ts ← clsx wrapper
│ │ ├── styles/
│ │ │ ├── tokens.css ← CSS custom properties
│ │ │ └── globals.css ← 레이아웃, 컴포넌트 기본 스타일
│ │ └── ui/
│ │ ├── app-shell.tsx ← 사이드바 + 톱바 + 본문 레이아웃
│ │ ├── card.tsx
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── field.tsx ← FieldLabel, TextInput, TextArea
│ │ ├── step-indicator.tsx ← 단계 표시 (pending/active/done)
│ │ ├── toast.tsx ← 포탈 기반 토스트
│ │ └── modal.tsx ← 포탈 기반 모달
│ └── app/
│ └── router.tsx ← AppShell 안에 라우트
Vite 프로젝트 생성 (이미 있으면 건너뜀):
npm create vite@latest my-project -- --template react-ts
cd my-project
의존성 설치:
npm install
npm install clsx react-router-dom
Vite 기본 파일 정리 — 아래 파일 삭제 (Marketing AX 스타일로 대체):
src/App.tsx — 삭제src/App.css — 삭제src/index.css — 삭제@/ alias 설정 (두 곳 모두 필수):
vite.config.ts — references/vite-config-alias.ts (Vite 번들러용)tsconfig.app.json — references/tsconfig-paths.json (TypeScript 컴파일러/IDE용)index.html 교체 — references/index-html.html (__PROJECT_TITLE__ 수정)
디렉토리 생성 + 파일 복사:
src/shared/lib/cn.ts ← references/cn-util.ts
src/shared/styles/tokens.css ← references/tokens.css
src/shared/styles/globals.css ← references/globals.css
src/shared/ui/card.tsx ← references/components/card.tsx
src/shared/ui/badge.tsx ← references/components/badge.tsx
src/shared/ui/button.tsx ← references/components/button.tsx
src/shared/ui/field.tsx ← references/components/field.tsx
src/shared/ui/toast.tsx ← references/components/toast.tsx
src/shared/ui/modal.tsx ← references/components/modal.tsx
src/shared/ui/step-indicator.tsx ← references/components/step-indicator.tsx
src/shared/ui/app-shell.tsx 생성 — references/app-shell-skeleton.tsx를 기반으로 프로젝트에 맞게 수정
BRAND_TITLE 변경mainNav 배열을 프로젝트 메뉴로 수정src/main.tsx 작성 — Vite 기본 코드를 아래로 교체:
import React from 'react'
import { createRoot } from 'react-dom/client'
import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom'
import '@/shared/styles/tokens.css' // ← 순서 중요: tokens 먼저
import '@/shared/styles/globals.css' // ← globals 다음
import { AppShell } from '@/shared/ui/app-shell'
const router = createBrowserRouter([
{
path: '/',
element: <AppShell><Outlet /></AppShell>, // ← Outlet으로 자식 라우트 렌더링
children: [
{ index: true, element: <div>홈</div> },
// 프로젝트별 라우트 추가
],
},
])
createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)
핵심:
<AppShell><Outlet /></AppShell>구조. AppShell이 사이드바+톱바를 그리고,<Outlet />이 현재 라우트의 페이지 컴포넌트를 렌더링한다.
<Card>카드 내용</Card>
<Card className="stack">여러 요소를 gap 0.8rem으로 배치</Card>
.stack 클래스 조합으로 내부 요소 간격 조절<Badge tone="success">성공</Badge>
<Badge tone="danger">실패</Badge>
<Badge tone="warning">대기</Badge>
<Badge tone="primary">중요</Badge>
<Badge>기본</Badge>
<Button>기본 (Primary)</Button>
<Button variant="secondary">보조</Button>
<Button variant="danger">위험</Button>
<Button variant="ghost">투명</Button>
<Toast
open={!sseConnected}
variant="destructive"
title="서버 연결 끊김"
description="실시간 연결이 끊어졌습니다. 데이터가 자동 갱신되지 않을 수 있습니다."
/>
open prop으로 표시/숨김 제어destructive 변형: 빨간 배경 + 경고 아이콘<Modal open={showModal} onClose={() => setShowModal(false)} title="확인">
<p>정말 삭제하시겠습니까?</p>
<div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 16 }}>
<Button variant="ghost" onClick={() => setShowModal(false)}>취소</Button>
<Button variant="danger" onClick={handleDelete}>삭제</Button>
</div>
</Modal>
3가지 상태: pending / active / done
import { StepIndicator } from '@/shared/ui/step-indicator'
{/* 카드 안에 수평 배치 + 화살표 구분자 */}
<Card style={{ display: 'flex', gap: 16, alignItems: 'center', padding: '0.65rem 1rem' }}>
<StepIndicator step={1} label="입력" active={false} done />
<span style={{ color: 'var(--neutral-300)' }}>→</span>
<StepIndicator step={2} label="처리 중" active done={false} />
<span style={{ color: 'var(--neutral-300)' }}>→</span>
<StepIndicator step={3} label="완료" active={false} done={false} />
</Card>
상태별 스타일:
| 상태 | 원형 배경 | 원형 텍스트 | 라벨 색상 |
|---|---|---|---|
| pending | var(--neutral-200) | var(--neutral-500) + 숫자 | var(--neutral-500) |
| active | var(--lg-red-700) | #fff + 숫자 | var(--neutral-900) |
| done | var(--success, #22c55e) | #fff + 체크마크(✓) | var(--success) |
치수:
borderRadius: '50%'fontSize: 12, fontWeight: 700<small>, fontWeight: 600gap: 6gap: 16 (카드 flex)→ (→), color: var(--neutral-300)<FieldLabel>라벨</FieldLabel>
<TextInput placeholder="입력" />
<TextArea placeholder="긴 텍스트" />
본문 배경은 단색이 아닌 LG Red 기반 미묘한 그라디언트: