Tokens — OKLCH 3계층
SPEC #006 · #012 · architecture/design-system 정합. 토큰 본체는 packages/ui/src/styles/.
Overview
OKLCH 색공간 + 3 계층 cascade. apps/admin/src/app/globals.css 에서 import.
Cascade
/* apps/admin/src/app/globals.css */
@import url("https://cdn.jsdelivr.net/.../pretendardvariable.css"); /* font 최상단 */
@import "tailwindcss";
@import "@linkmusic/ui/styles/all.css"; /* Layer 1→2→3→base */@linkmusic/ui/styles/all.css:
@import "./tokens.css"; /* Layer 1: primitive */
@import "./semantic.css"; /* Layer 2: semantic light/dark */
@import "./surface-admin.css"; /* Layer 3: Surface 10 scale */
@import "./base.css"; /* element defaults using tokens */Layer 1 — Primitive (tokens.css)
| 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 · --success-2 | semantic success |
--warn-1 · --warn-2 | semantic warning |
--danger-1 · --danger-2 | semantic danger |
--info-1 · --info-2 | semantic info |
--imp-alert · --imp-alert-fg | 임퍼소네이션 빨간 배너 전용 토큰 (스케일 아님) |
OKLCH 표현 — perceptual lightness 일관:
--brand-3: oklch(0.58 0.22 295); /* ★ primary */Layer 2 — Semantic (semantic.css)
:root(light) + [data-theme="dark"] 2 mapping (system 은 dark 와 동일 값을 prefers-color-scheme 으로):
| 토큰 | light | dark |
|---|---|---|
--bg | --n-10 | --n-0 |
--surface | --n-12 | --n-1 |
--surface-2 | --n-9 | --n-2 |
--surface-3 | --n-8 | --n-3 |
--fg | --n-1 | --n-9 |
--fg-muted | --n-5 | --n-6 |
--fg-soft | --n-4 | --n-5 |
--border | --n-8 | --n-3 |
--border-strong | --n-7 | --n-4 |
--primary | --brand-3 | --brand-3 |
--primary-strong | --brand-2 | --brand-2 |
--primary-fg | --n-12 | oklch(0.99 0 0) |
--ring | --brand-3 | --brand-4 |
--success | --success-1 | --success-2 |
--warn | --warn-1 | --warn-2 |
--danger | --danger-1 | --danger-2 |
--info | --info-1 | --info-2 |
--now-playing | --brand-3 | --brand-4 |
--imp-alert 는 light/dark 무관 단일 primitive 토큰 — semantic 매핑 없이 임퍼소네이션 배너가 직접 참조.
Layer 3 — Surface scale (surface-admin.css)
Surface 10 = compact density:
:root {
--font-base: 14px;
--density: compact;
--radius: 0.375rem;
--tap-min: 32px;
--motion: 120ms;
}Surface 11·12 layer 는 후속 SPEC (각자 surface-store-hq.css · surface-store.css).
ThemeProvider
// packages/ui/src/lib/theme-provider.tsx
'use client';
export const ThemeProvider = ({ initialTheme, children }) => {
const [theme, setTheme] = useState(initialTheme);
useEffect(() => {
document.documentElement.setAttribute("data-theme", theme);
}, [theme]);
// setTheme 호출 시 쿠키 기록 (Max-Age = 1년):
// const maxAge = 60 * 60 * 24 * 365;
// const secure = location.protocol === "https:" ? "; Secure" : "";
// document.cookie = `lm_theme=${theme}; Path=/; Max-Age=${maxAge}; SameSite=Lax${secure}`;
// ...
};SSR — apps/admin/src/lib/theme-cookie.ts 의 readThemeFromCookie() 가 <html data-theme> 미리 박음 →
hydration 깜빡임 X.
Constraints
@import url(...font)는 apps globals.css 최상단 — CSS @import 룰 순서 ([[feedback-9]]).- Tailwind v4
@import "tailwindcss"다음에 ui all.css — base layer 충돌 방지. - Layer 1 변경 거의 없음 — palette 자체. semantic mapping 만 자유 조정.
--imp-alert토큰은 정의만, 실 사용은 임퍼소네이션 배너 한정.
Roadmap
- Surface 11·12 layer
- color-mix() 폴리필 검토 (구 브라우저 — v1 Chrome/Edge 최신만 지원이라 불필요)
- 토큰 시각 카탈로그 (Storybook)
References
- SPEC #006 §2-3 · SPEC #012
- handoff
20-design-system-proposal.mdv0.1 linkmusic-frontend-space/packages/ui/src/styles/