ArchitectureDesign System (OKLCH v2)

Design System v2 (OKLCH 3계층 토큰)

SPEC #006 §2-3 · SPEC #012 정합. linkmusic-frontend-space/packages/ui/.

Overview

디자인 시스템 v2 — handoff 20-design-system-proposal.md v0.1 채택. OKLCH 색공간 + Toss-restraint (절제된 표현). 공통 토큰은 surface 3종이 공유, 밀도 / 스케일은 surface 별 독립.

3 계층 토큰

Layer 1 — Primitive (raw palette · 변경 거의 없음)

Layer 2 — Semantic (light · dark 2 mapping)

Layer 3 — Surface-specific (밀도 · 스케일 · 모션)

base.css (Tailwind v4 reset + tokens 활용)

Layer 1 — Primitive (tokens.css)

OKLCH ramp:

  • --n-0--n-12 — near-pure gray neutral (0=darkest, 12=lightest)
  • --brand-1--brand-5 — Pulse Violet (primary = --brand-3)
  • --energy-1--energy-3 — Signal Lime (예약 ramp — v2 미매핑)
  • --mag-1 · --mag-2 — Magenta (예약 — 앨범 아트만)
  • --success-1/2 · --warn-1/2 · --danger-1/2 · --info-1/2 — semantic hues
  • --imp-alert · --imp-alert-fg — 임퍼소네이션 빨간 배너 전용 토큰

Layer 2 — Semantic (semantic.css)

:root (light) + [data-theme="dark"] 2매핑:

  • --bg — 페이지 배경
  • --surface — 카드 · 모달 등 layered surface
  • --fg — text default
  • --fg-muted · --fg-soft
  • --primary · --primary-fg
  • --ring — focus ring
  • --border · --border-strong
  • --now-playing — 송출 중 표시 전용 색
  • --imp-alert — 임퍼소네이션 배너 색

Layer 3 — Surface scale

surface 3종이 같은 Layer 1·2 위에 자기 밀도를 얹는다:

토큰Surface 10 (admin)Surface 11 (본사 모드)Surface 12 (점장 모드)
--font-base14px (compact)15px (medium)17px (large)
--densitycompactmediumcomfortable
--radius0.375rem0.5rem0.625rem
--tap-min32px40px48px
--motion120ms subtle180ms reassuring200ms decisive

packages/ui/src/styles/surface-admin.css — Surface 10 layer. Surface 11·12 는 후속 SPEC.

Tailwind v4 통합

apps/admin/src/app/globals.css 의 cascade:

@import url("https://cdn.jsdelivr.net/.../pretendardvariable.css");  /* 폰트 최상단 */
@import "tailwindcss";                                                 /* v4 reset + utilities */
@import "@linkmusic/ui/styles/all.css";                                /* Layer 1→2→3→base */

@linkmusic/ui/styles/all.css:

@import "./tokens.css";       /* Layer 1 */
@import "./semantic.css";     /* Layer 2 */
@import "./surface-admin.css"; /* Layer 3 — surface 별 다른 파일 */
@import "./base.css";         /* element defaults */

ThemeProvider

packages/ui/src/lib/theme-provider.tsx:

  • <html data-theme="light|dark"> 토글
  • 사용자 선택 → cookie lm_theme 저장 (non-HttpOnly · SSR 사용)
  • 초기값 SSR: await readThemeFromCookie() → layout 의 <html data-theme=""> 미리 박힘 → 깜빡임 X

apps/admin/src/lib/theme-cookie.ts:

export const readThemeFromCookie = async (): Promise<Theme> => {
  const c = (await cookies()).get("lm_theme")?.value;
  if (c === "light" || c === "dark") return c;
  return "system";  // OS prefers-color-scheme 기본
};

Secure cookie 정책 — lm_theme 는 non-HttpOnly. Securelocation.protocol === "https:" 일 때만 ([[feedback-4]]).

컴포넌트 (@linkmusic/ui)

packages/ui/src/components/ (flat — atoms/·shells/ 하위 디렉토리 없음). export 단일 소스는 packages/ui/src/index.ts:

컴포넌트용도
Buttonprimary · secondary · ghost · danger (CVA variants)
Banner정보 · 경고 · 위험 · 성공. 임퍼소네이션 배너 별도 (--imp-alert)
StatusPillenum status 시각 (ACTIVE · SUSPENDED · …)
OrgAvatar본사/매장 헤더 아바타 (initial-based fallback)
Card카드 컨테이너 (Card·CardHeader·CardTitle·CardContent)
DialogRadix Dialog wrapper — 개별 export (DialogContent 등, namespace 없음)
DropdownMenuRadix DropdownMenu wrapper
Tabs / Tab필터 button group (role="group" + aria-pressed, Radix 미사용)
ToastRadix Toast wrapper (ToastProvider·ToastViewport 등)
Switchon/off 토글
Fieldlabel + input + error 묶음
Input / Label폼 primitive
FormSection폼 영역 grouping + title · description
AuthShell로그인 / 비밀번호 변경 등 인증 화면 셸
AuthCard인증 폼 카드

운영사 백오피스 셸 (OpsShell) 은 @linkmusic/ui 에 단일 컴포넌트로 없다. apps/admin(protected)/layout.tsxTopbar + Sidebar 를 조합한다. 마찬가지로 OpsStepper·PageHeader@linkmusic/ui 가 아니라 apps/admin/src/components/shell/ 소속이다 (등록 화면 stepper · 페이지 헤더). 자세한 구조는 Shells 참고.

Constraints

  • handoff 에 없는 시각 결정 금지 ([[memory: feedback_design_only_from_handoff]]). 새 컴포넌트 / 화면이 필요하면 사용자가 claude design 에 받아온 시안을 입력으로.
  • Tailwind v4 의 @import "tailwindcss" 는 base.css 위에.
  • font @import 는 apps globals.css 최상단 (@import 룰이 다른 rule 보다 앞서야 함).

Decisions

  • OKLCH 채택 — perceptual lightness (RGB 대비 brightness 일관)
  • Toss-restraint — 강조색 (lime · magenta) 은 송출 / now-playing 같은 의미 있는 자리만
  • shadcn/ui 미사용 — handoff 토큰을 직접 코드화 (custom)
  • Storybook 후속 — atoms 가 안정되면 도입

Roadmap

  • Surface 11·12 layer 추가 (surface-store-hq.css · surface-store.css)
  • Storybook (packages/ui 시각 카탈로그)
  • Tokens documentation site (이 docs 의 /features/design-system/tokens)

Toast · DropdownMenu 는 이미 @linkmusic/ui 에 존재하고 admin 에서 사용 중이다 (roadmap 항목 아님).

References

  • SPEC #006 §2-3 (v1 토큰)
  • SPEC #012 (v2 — Toss-restraint 재정의)
  • handoff 20-design-system-proposal.md v0.1
  • linkmusic-frontend-space/packages/ui/src/styles/
  • linkmusic-frontend-space/packages/ui/src/components/