feat(ui): flat "quiet instrument" token v4 with a single raw-value SSOT

- Add theme-raw.ts (no MUI/DOM deps) as the one source of theme values.
  Each theme declares only surfaces, 3-tier text, borders, accent and
  overlay shadow; legacy glass/gradient/glow/crt keys are derived as
  flat values so existing call sites lose effects without edits.
- Tertiary text now clears 4.5:1 on every surface in all six themes
  (label/inactive/dimLabel/muted alias it). Filled buttons use
  accent.solid + accent.ink (5.17:1 on dark/light, was 3.68:1).
- theme-vars.ts derives popup CSS from the same raw values instead of a
  hand copy, and defines the previously undefined --d3-overlay-strong
  and --d3-accent-glow-dim.
- MUI: no ripple, global focus-visible ring and reduced-motion rule,
  flat Card, status colors per theme, shape radius 16 -> 6.
- DS: flatten MetalCard/DoubleBezelCard/ScreenPanel/TiltCard, tone-based
  PhysicalButton (500 weight, no lift), TactileBadge with zero tracking
  and keyboard support, glow-free Led/StatRing, idle wave amplitude 0.
- PhosphorText drops mono/uppercase variants: mono has no Hangul glyphs,
  which spaced out Korean labels.
- design.md: record the 2026-09-28 redesign brief, audit, IA and tokens.
This commit is contained in:
Yun Chan 2026-09-28 18:26:03 +09:00
parent c3367bc611
commit 2428ede03d
15 changed files with 969 additions and 1515 deletions

View file

@ -1,11 +1,13 @@
'use client'
// src/renderer/components/ds/PhosphorText.tsx
// v2 "Midnight Glass": 클린 타이포그래피 — 산세리프(Pretendard) 기반.
// 헤드라인/수치는 프라이머리 화이트, 메타/라벨류만 모노+대문자 유지.
// packages/ui/src/components/ds/PhosphorText.tsx
// v4 "조용한 계기": 타이포 변형 13종 — 전부 산세리프(Pretendard), 대문자 변환 없음.
// 위계는 크기와 명도(text 3단)로 만든다. 한글 라벨에 모노·양수 자간을 쓰지 않는다
// (모노 폰트에 한글 글리프가 없어 "누 름 유 지"처럼 벌어졌다 — 2026-09-28 실측).
// 수치는 tabular-nums 로 흔들리지 않게.
import { Typography, type TypographyProps } from '@mui/material'
import { d3roPalette, d3roFontSans, d3roFontMono, d3roTypo } from '../../theme'
import { d3roPalette, d3roFontSans, d3roTypo } from '../../theme'
type PhosphorVariant =
| 'hero' | 'title' | 'value' | 'heading'
@ -19,24 +21,30 @@ interface VariantDef {
fontWeight: number
letterSpacing: string
lineHeight: number
textTransform?: 'uppercase' | 'none'
mono?: boolean
}
const def = (key: keyof typeof d3roTypo, color: string): VariantDef => ({
fontSize: d3roTypo[key].size,
fontWeight: d3roTypo[key].weight,
letterSpacing: d3roTypo[key].spacing,
lineHeight: d3roTypo[key].line,
color,
})
const VARIANTS: Record<PhosphorVariant, VariantDef> = {
hero: { fontSize: d3roTypo.hero.size, color: d3roPalette.text.primary, fontWeight: d3roTypo.hero.weight, letterSpacing: d3roTypo.hero.spacing, lineHeight: d3roTypo.hero.line },
title: { fontSize: d3roTypo.title.size, color: d3roPalette.text.primary, fontWeight: d3roTypo.title.weight, letterSpacing: d3roTypo.title.spacing, lineHeight: d3roTypo.title.line },
value: { fontSize: d3roTypo.value.size, color: d3roPalette.text.primary, fontWeight: d3roTypo.value.weight, letterSpacing: d3roTypo.value.spacing, lineHeight: d3roTypo.value.line },
heading: { fontSize: d3roTypo.heading.size, color: d3roPalette.text.primary, fontWeight: d3roTypo.heading.weight, letterSpacing: d3roTypo.heading.spacing, lineHeight: d3roTypo.heading.line },
body: { fontSize: d3roTypo.body.size, color: d3roPalette.text.secondary, fontWeight: d3roTypo.body.weight, letterSpacing: d3roTypo.body.spacing, lineHeight: d3roTypo.body.line },
compact: { fontSize: d3roTypo.compact.size, color: d3roPalette.text.primary, fontWeight: d3roTypo.compact.weight, letterSpacing: d3roTypo.compact.spacing, lineHeight: d3roTypo.compact.line },
small: { fontSize: d3roTypo.small.size, color: d3roPalette.text.inactive, fontWeight: d3roTypo.small.weight, letterSpacing: d3roTypo.small.spacing, lineHeight: d3roTypo.small.line },
meta: { fontSize: d3roTypo.meta.size, color: d3roPalette.text.inactive, fontWeight: d3roTypo.meta.weight, letterSpacing: d3roTypo.meta.spacing, lineHeight: d3roTypo.meta.line, textTransform: 'uppercase', mono: true },
label: { fontSize: d3roTypo.label.size, color: d3roPalette.text.label, fontWeight: d3roTypo.label.weight, letterSpacing: d3roTypo.label.spacing, lineHeight: d3roTypo.label.line, textTransform: 'uppercase' },
dim: { fontSize: d3roTypo.label.size, color: d3roPalette.text.inactive, fontWeight: d3roTypo.body.weight, letterSpacing: d3roTypo.label.spacing, lineHeight: d3roTypo.label.line },
engrave: { fontSize: d3roTypo.engrave.size, color: d3roPalette.text.dimLabel, fontWeight: d3roTypo.engrave.weight, letterSpacing: d3roTypo.engrave.spacing, lineHeight: d3roTypo.engrave.line, textTransform: 'uppercase', mono: true },
micro: { fontSize: d3roTypo.micro.size, color: d3roPalette.text.dimLabel, fontWeight: d3roTypo.micro.weight, letterSpacing: d3roTypo.micro.spacing, lineHeight: d3roTypo.micro.line, textTransform: 'uppercase', mono: true },
nano: { fontSize: d3roTypo.nano.size, color: d3roPalette.text.inactive, fontWeight: d3roTypo.nano.weight, letterSpacing: d3roTypo.nano.spacing, lineHeight: d3roTypo.nano.line, textTransform: 'uppercase', mono: true },
hero: def('hero', d3roPalette.text.primary),
title: def('title', d3roPalette.text.primary),
value: def('value', d3roPalette.text.primary),
heading: def('heading', d3roPalette.text.primary),
body: def('body', d3roPalette.text.secondary),
compact: def('compact', d3roPalette.text.primary),
small: def('small', d3roPalette.text.tertiary),
meta: def('meta', d3roPalette.text.tertiary),
label: def('label', d3roPalette.text.tertiary),
dim: { ...def('label', d3roPalette.text.tertiary), fontWeight: d3roTypo.body.weight },
engrave: def('engrave', d3roPalette.text.tertiary),
micro: def('micro', d3roPalette.text.tertiary),
nano: def('nano', d3roPalette.text.tertiary),
}
interface PhosphorTextProps extends Omit<TypographyProps, 'variant'> {
@ -51,14 +59,14 @@ export function PhosphorText({ variant = 'value', component = 'span', sx, ...pro
component={component}
{...props}
sx={{
fontFamily: v.mono ? d3roFontMono : d3roFontSans,
fontFamily: d3roFontSans,
fontSize: v.fontSize,
fontWeight: v.fontWeight,
color: v.color,
letterSpacing: v.letterSpacing,
lineHeight: v.lineHeight,
fontVariantNumeric: 'tabular-nums',
textTransform: v.textTransform ?? 'none',
textTransform: 'none',
...sx,
}}
/>