324 lines
22 KiB
Markdown
324 lines
22 KiB
Markdown
# Theme & Token Architecture
|
||
|
||
## AI Theme 기본 적용 정책 (2026-10-07 사용자 확정)
|
||
|
||
사용자가 별도로 "테마를 적용해줘", "Semantic Token을 사용해줘"라고 요청하지 않아도 모든 AI 신규·수정 UI는 Theme을 기본 적용한다. Theme은 Component 탐색·설계 단계부터 포함되는 구현 조건이다. 전체 구현 순서는 [AI/Component 구현 원칙](shadcn-기반-구현-원칙.md)을 따른다.
|
||
|
||
- 설계 계층은 **Primitive → Semantic → Component Mapping**을 유지한다.
|
||
- 값 선택 순서는 **Existing Semantic Token → 해당 Existing Component Token → Existing CSS Variable → 필요한 경우 최소 Semantic 확장**이다. 이는 탐색 순서이며 Component Mapping 계층을 제거하거나 다른 Component의 내부 Token을 직접 공유하는 규칙이 아니다.
|
||
- 기존 Token이 있는 색상·spacing·radius·typography·값을 먼저 고정값으로 작성하지 않는다. 같은 Token을 여러 파일에 중복 정의하지 않는다. 정의되지 않은 속성은 기존 shadcn 기본 구현과 구조적 값의 계약을 유지하고 임의 Token을 양산하지 않는다.
|
||
- 특정 Reference 색상을 그대로 재현하라는 요청에도 먼저 의미가 맞는 기존 Theme / Semantic Token에 연결한다. 정확한 값이 기존 Theme과 다르면 Theme 값 변경의 필요성과 영향을 확인한다. 독립 Reference 검증 표현은 Story 범위와 Production Theme 대상 여부를 먼저 확인하고 예외 사유를 기록한다.
|
||
|
||
```css
|
||
/* 금지: UI에서 Theme 색상을 고정 */
|
||
.status { color: #00C896; }
|
||
/* 기존 의미에 맞는 Token 연결 예: 실제 진행 control 색 역할일 때 */
|
||
.status { color: var(--hm-sem-state-active); }
|
||
```
|
||
|
||
위 예는 상태 역할이 실제로 일치할 때만 적용한다. Dashboard 업무 Status는 기존 mapping을 먼저 확인하며 모든 업무 상태를 Control Active에 합치지 않는다. 문서의 Token·색상 예시는 현재 코드/계약과 대조하고 존재하지 않는 Token을 실제 값처럼 사용하지 않는다.
|
||
|
||
### Theme 사용과 Theme Source 수정
|
||
|
||
| 작업 | 기본 처리 |
|
||
|---|---|
|
||
| 기존 Token / CSS Variable을 UI에서 사용 | 별도 승인 없이 기본 적용 |
|
||
| `themes/default.json`, Primitive / Semantic 값 자체 변경 | 요청 충족에 필요한 명시적 사유와 영향을 확인한 뒤 진행 |
|
||
| 새 Semantic / 공개 Runtime Token 추가 | 기존 계약으로 해결 불가능한 경우 최소 확장; 소비처·fallback·호환성 및 package 변경 범위 확인 |
|
||
|
||
Theme 적용 요청을 Theme Source 수정 허가로 해석하지 않는다. UI를 연결하기 위해 Default/Primitive 값을 임의 변경하지 않는다. Color Source of Truth는 `themes/*.json`이며 생성된 fallback CSS/데이터를 별도 원본처럼 수동 수정하지 않는다.
|
||
|
||
### Runtime과 독립 fallback
|
||
|
||
```text
|
||
themes/*.json
|
||
→ Runtime Theme Loader
|
||
→ CSS Custom Properties
|
||
→ Semantic
|
||
→ Component Mapping
|
||
→ UI
|
||
```
|
||
|
||
앱 초기 CSS에서 기존 `@hm-design/ui/theme.css` 적용을 확인하고, Theme 주입이 없으면 HM Default를 사용한다. 이미 적용된 Theme 환경과 공개 Runtime API를 재사용하며 별도 Theme 엔진을 만들지 않는다. Global / Scoped / Portal 경계와 partial / invalid / cleanup 계약은 [Runtime Theme](runtime-theme.md)를 따른다.
|
||
|
||
기존 공개 계약이 지원하는 color-only 변경은 Theme JSON만 별도 배포해 반영할 수 있어야 하며 UI package 재배포를 요구하지 않도록 Component에 값을 고정하지 않는다. 새 Token·CSS 규칙·Runtime 계약 변경은 package 변경이며 Theme JSON만으로 처리할 수 없다. 실제 배포 경로는 [Theme delivery](theme-delivery.md)를 따른다.
|
||
|
||
Primary와 On Accent는 독립 fallback을 가진 sibling Semantic이다. Primary override가 On Accent를 변경하게 만들거나 둘을 alias하지 않는다. Active / Focus / Destructive 및 Global / Scoped의 기존 독립성도 유지한다. 같은 Default 값이라는 이유로 fallback을 합치거나 새 상호 의존성을 만들지 않는다.
|
||
|
||
Theme 미적용 prototype / 독립 POC 등의 예외는 사용자의 명시적 요청과 기존 시스템에 영향 없는 별도 범위가 있을 때만 [AI 구현 예외 기준](shadcn-기반-구현-원칙.md#10-명시적-예외)에 따라 적용한다. 이 확정 정책은 아래 과거 예시의 상충 문구보다 우선한다.
|
||
|
||
## 목적
|
||
|
||
HMDesignSystem은 shadcn/ui를 기반으로 하며 하나의 HM 기본 테마를 운영한다.
|
||
|
||
HM Default는 기본 제공 Theme preset이다. 외부 서비스 Theme는 같은 공개 Runtime 계약을 사용해 값만 교체한다. Theme selector UI, Light/Dark 엔진은 만들지 않는다. 실제 계약/API와 scope 정책은 [Runtime Theme](runtime-theme.md)를 따른다.
|
||
|
||
디자인 변경 시 가능하면 React 컴포넌트 코드를 수정하지 않고 Theme / Token CSS 수정만으로 반영할 수 있는 구조를 유지한다.
|
||
|
||
---
|
||
|
||
## 기본 구조
|
||
|
||
HMDesignSystem의 스타일 값은 기본적으로 다음 구조를 따른다.
|
||
|
||
```text
|
||
Primitive
|
||
→ Semantic (공통 Theme 역할)
|
||
→ Component Mapping Token
|
||
→ Component
|
||
```
|
||
|
||
컬러 Token은 Primitive → Semantic → Component Mapping의 3계층으로 구분한다. 공통 Theme에는 typography/radius/geometry도 포함하며, 선택적 공개 palette 사용과 컴포넌트 고유 geometry는 현재 구현 계약을 따른다.
|
||
|
||
---
|
||
|
||
## Global Theme Token
|
||
|
||
여러 컴포넌트에서 공통으로 사용하는 디자인 정책을 관리한다.
|
||
|
||
주요 관리 대상:
|
||
|
||
- Color
|
||
- Typography
|
||
- Radius
|
||
- Shadow
|
||
- Control Height
|
||
- 공통 Spacing
|
||
- 공통 상태 표현
|
||
|
||
현재 HM 공통 Token 예(컬러 원본은 `themes/default.json`, 아래 CSS는 생성 결과의 일부):
|
||
|
||
```css
|
||
:root, [data-hm-theme] {
|
||
--hm-sem-action-primary: var(--hm-color-primary);
|
||
--hm-sem-bg-surface: var(--hm-color-surface-default);
|
||
--hm-sem-stroke-input-default: var(--hm-color-border-control);
|
||
--hm-radius-input: var(--hm-radius-control-md);
|
||
}
|
||
```
|
||
|
||
공통 디자인 정책이 변경되는 경우 Global Theme Token을 수정하여 관련된 여러 컴포넌트에 함께 반영할 수 있어야 한다.
|
||
|
||
---
|
||
|
||
## Component Mapping Token
|
||
|
||
각 컴포넌트는 공통 Theme 역할을 Component Mapping Token을 통해 사용한다.
|
||
|
||
예:
|
||
|
||
```css
|
||
--hm-button-radius: var(--hm-radius-button);
|
||
--hm-input-radius: var(--hm-radius-input);
|
||
--hm-select-radius: var(--hm-radius-input);
|
||
|
||
--hm-input-border-color: var(--hm-sem-stroke-input-default);
|
||
--hm-select-border-color: var(--hm-sem-stroke-input-default);
|
||
--hm-card-border-color: var(--hm-sem-stroke-input-default);
|
||
```
|
||
|
||
이 구조를 통해 공통 변경과 개별 변경을 모두 지원한다.
|
||
|
||
예를 들어:
|
||
|
||
```css
|
||
--hm-radius-input: 6px;
|
||
```
|
||
|
||
로 변경하면 이를 사용하는 Input, Select 등이 함께 변경된다. Button radius는 별도 Mapping을 따른다.
|
||
|
||
반대로 특정 컴포넌트만 변경해야 할 경우:
|
||
|
||
```css
|
||
--hm-input-radius: 8px;
|
||
```
|
||
|
||
처럼 Component Mapping Token만 변경할 수 있다.
|
||
|
||
---
|
||
|
||
## Component 적용 원칙
|
||
|
||
컴포넌트 내부에는 실제 색상값이나 공통 디자인 값을 가능한 직접 작성하지 않는다.
|
||
|
||
피해야 하는 예:
|
||
|
||
```tsx
|
||
className="bg-[#2563eb] border-[#d1d5db] rounded-[8px]"
|
||
```
|
||
|
||
대신 Theme / Component Mapping Token을 CSS selector에서 사용한다.
|
||
|
||
예:
|
||
|
||
```css
|
||
[data-slot="input"] {
|
||
border-color: var(--hm-input-border-color);
|
||
border-radius: var(--hm-input-radius);
|
||
}
|
||
```
|
||
|
||
shadcn의 기존 구조와 CVA variant 구조는 가능한 유지한다.
|
||
|
||
HMDesignSystem을 위해 shadcn 컴포넌트를 완전히 별도 구조로 재작성하지 않는다.
|
||
|
||
---
|
||
|
||
## 공통화 기준
|
||
|
||
현재 값이 같다는 이유만으로 무조건 하나의 Component Token으로 합치지 않는다.
|
||
|
||
반대로 모든 값을 컴포넌트별 Token으로 만들지도 않는다.
|
||
|
||
판단 기준은 다음과 같다.
|
||
|
||
### Global Theme Token
|
||
|
||
여러 컴포넌트에서 동일한 디자인 정책으로 함께 변경되어야 하는 값
|
||
|
||
예:
|
||
|
||
- 기본 Border Color
|
||
- 기본 Radius
|
||
- Primary Color
|
||
- Control Height
|
||
- 기본 Typography
|
||
|
||
### Component Mapping Token
|
||
|
||
특정 컴포넌트에서 향후 독립적으로 변경될 가능성이 있는 값
|
||
|
||
예:
|
||
|
||
- Button Primary Background
|
||
- Input Border
|
||
- Dialog Background
|
||
- Checkbox Checked Color
|
||
|
||
---
|
||
|
||
## State 관리
|
||
|
||
실제로 존재하는 상태에 대해서만 Token을 구성한다.
|
||
|
||
Control Active, Persistent Selected Surface, Navigation Current/Open은 서로 구분한다. Focus는 Active와 sibling Semantic이고 GNB/LNB는 contextual navigation focus 예외를 유지한다. Invalid는 Destructive border, Invalid + Focus는 Destructive border + Focus indication이다. 컴포넌트별 적용 범위는 해당 Component/Pattern 문서와 아래 HM Token 연결 절을 따른다.
|
||
|
||
예:
|
||
|
||
- default
|
||
- hover
|
||
- active / pressed
|
||
- focus
|
||
- selected / checked
|
||
- disabled
|
||
- error / invalid
|
||
|
||
size × variant × state × property 형태로 무조건 세분화하여 Token 수를 늘리지 않는다.
|
||
|
||
실제 디자인 차이가 있는 경우에만 분리한다.
|
||
|
||
---
|
||
|
||
## 단일 Theme 원칙
|
||
|
||
HM Default preset은 하나이며 Light/Dark preset이나 사용자 Theme selector는 없다. 공개 Runtime API는 동일 계약의 외부 값과 독립 Scoped override를 지원한다. 아래 제한은 전환 UI/제품별 Theme catalog를 새로 설계하지 않는다는 의미이며 구현된 Global/Scoped 적용을 금지하지 않는다.
|
||
|
||
현재 필요하지 않은 구조:
|
||
|
||
- ThemeProvider 기반 테마 전환
|
||
- Light / Dark Theme Switching
|
||
- 별도 Multi Theme 전환 UI/catalog
|
||
- 별도 Multi Brand catalog
|
||
- 사용자 Theme Selector
|
||
- Runtime Theme Selector
|
||
|
||
향후 확장은 가능하게 유지하되, 미래 가능성을 위해 현재 구조를 복잡하게 만들지 않는다.
|
||
|
||
---
|
||
|
||
## 작업 원칙
|
||
|
||
새로운 컴포넌트를 추가할 때 다음 순서로 판단한다.
|
||
|
||
1. 기존 Global Theme Token으로 표현 가능한지 확인
|
||
2. 공통 디자인 정책이라면 기존 Global Theme Token 재사용
|
||
3. 독립 변경 가능성이 있다면 Component Mapping Token 추가
|
||
4. 컴포넌트에는 실제 디자인 값을 직접 작성하지 않음
|
||
5. Storybook에서도 실제 소비 프로젝트와 동일한 Component API 사용
|
||
|
||
HMDesignSystem의 핵심 원칙은 다음과 같다.
|
||
|
||
**공통 디자인 정책은 Theme Token 하나를 수정하여 관련 컴포넌트에 함께 반영할 수 있어야 한다.**
|
||
|
||
동시에:
|
||
|
||
**특정 컴포넌트만 변경해야 할 경우 Component Mapping Token을 통해 독립적으로 override할 수 있어야 한다.**
|
||
|
||
---
|
||
|
||
## 현재 구현 기준
|
||
|
||
### Token 이름과 위치
|
||
|
||
위 예시의 Token 이름은 개념 설명용이다. 외부 ui_viz Reference (ZIP 미포함, 읽기 전용)는 초기 디자인 참고 자료다. 현재 Production 코드와 실제 Storybook을 1차 기준으로 Component/Pattern 문서의 HM 결정과 대조한다. 실제 저장소 기준은 다음과 같다.
|
||
|
||
| 계층 | 위치 | 이름 |
|
||
|---|---|---|
|
||
| Primitive | `packages/ui/src/theme/primitives.css` | `--hm-<family>-<step>` — ui_viz Primitive 이름 그대로 |
|
||
| 공통 Theme (Global/Semantic) | `packages/ui/src/theme/default.css` | `--hm-color-*`, `--hm-radius-*`, `--hm-control-height-*`, `--hm-font-*`, `--hm-sem-*` |
|
||
| shadcn/Tailwind adapter | `default.css` 하단 | `--primary`, `--border`, `--radius` 등을 HM Token에 연결 |
|
||
| Component Mapping | `packages/ui/src/theme/<component>.css` | `--hm-<component>-<속성>` |
|
||
|
||
- **Primitive**: ui_viz와 다른 이름을 만들지 않고 변환 대응표도 두지 않는다. ui_viz에 Primitive로 등록되지 않은 값(예: Button 범위 Component 값)은 Primitive로 올리지 않는다.
|
||
- **Component Mapping**: ui_viz에 등록된 Component Token(예: `button.radius`, `button.height.md`)은 그 속성 이름을 그대로 쓴다(`--hm-button-radius`, `--hm-button-height-md`). ui_viz의 CSS 변수는 `--button-radius`처럼 접두어가 없지만, HM은 모든 Token에 `--hm-`을 붙인다. Tailwind v4의 `--color-*`·`--radius-*` 같은 theme namespace와 소비 앱 변수와의 충돌을 피하기 위해서다. ui_viz에 없는 값은 같은 형식으로 정의한다.
|
||
- **파일**: 컴포넌트 전용 Mapping Token과 `[data-slot]` selector 스타일은 `packages/ui/src/theme/<component>.css`에 두고 `packages/ui/src/theme.css`가 import한다. 공통 값은 `default.css`에 둔다. Demo/App 페이지 스타일은 넣지 않는다. 컴포넌트 값은 Tailwind arbitrary value(`bg-[var(...)]`)가 아니라 이 CSS의 selector가 Mapping Token을 참조해 적용한다.
|
||
- Mapping Token의 기본값은 공통 Theme Token을 참조하고(`--hm-button-radius: var(--hm-radius-button)`), 해당 컴포넌트만 따로 바꿀 수 있다.
|
||
- **Focus Color**(`default.css`): `--hm-color-border-focus` → `--hm-sem-stroke-input-focus` → `--ring` 한 줄 체인이 일반 Focus 색(Input·Textarea·Select·Button·Checkbox·Tabs·Slider·Menubar 등)의 공통 소스다. 현재 **시험 적용값**은 Light 2(`--hm-green-light-2`, `#266651`)이며 이전 값은 `--hm-color-primary`(`#123328`)였다. 사용자 최종 확정 전이다. Semantic 값·이름, Runtime 계약과 Theme JSON 구조는 유지한다.
|
||
- 2026-10-06 Focus 정합: Slider Hover/Drag halo는 기존 Primary 20% / 4·6px를 유지하고, 키보드 Focus halo만 Focus Semantic 20%로 분리한다. Focus + Hover는 4px Focus, Focus + Drag는 6px Focus가 우선하며 Disabled는 후광이 없다. Range·Thumb border는 Active Semantic을 유지한다.
|
||
- Menubar 상위 Trigger Highlighted/Open 면은 Accent 100%를 유지하고 키보드 Focus에 Navigation Menu와 같은 3px `--ring` 50% 독립 ring을 더한다. Open + Focus는 Accent 면과 Focus ring을 함께 표시한다. Item Highlighted는 Accent 45%, Disabled는 기존 규칙을 유지한다. Navigation Menu는 변경하지 않는다. 일반 Story의 computed style로 검증하고 Designer 전용 button outline을 근거로 사용하지 않는다.
|
||
- **Active State Semantic**(`default.css`, 2026-10-06): `--hm-sem-state-active`(→ `--hm-sem-palette-green-light-2` → `--hm-green-light-2`, `#266651`)와 `--hm-sem-on-state-active`(→ `--hm-color-text-inverse`, 흰색, 대비 약 6.8:1). Checked·Selected control·ON·Active fill을 포괄하며 Navigation/Pagination Current는 포함하지 않는다. Checkbox checked, Radio selected(border·dot), Switch on, Slider range·thumb border, Progress indicator, Calendar single selected·range start/end의 `--hm-<component>-*`가 이를 참조한다. Badge default와 Pagination current는 대상이 아니며 Primary Green(`--hm-sem-action-primary`)을 쓴다. 이 한 줄을 바꾸면 위 컴포넌트가 함께 바뀌고 Focus는 영향을 받지 않는다.
|
||
- Focus chain(`--hm-color-border-focus`)과 **sibling**이다. 현재 값은 둘 다 Light 2지만 의미가 다르므로 어느 쪽도 다른 쪽을 참조하지 않는다. 같은 색일 때 fill(Active)과 바깥 ring(Focus)의 형태로 구분한다.
|
||
- Progress는 현재 Active와 색 정책이 같아 같은 Semantic을 쓴다. 의미가 달라지면 별도 Semantic으로 분리한다.
|
||
- 대상 아님: Toggle / Toggle Group(on = `--accent` 면, 별도 정책), GNB·LNB·Navigation Menu current(기존 Strong Brand 언어), Error/Destructive, Button.
|
||
- **Persistent Selected Surface**: Toggle / Toggle Group의 Selected 및 Selected + Hover = `--accent` 100% / `--accent-foreground`(Git baseline `d101530` 매핑, 2026-10-06 원복). 아이콘은 전경을 따르고 border는 기존 default 투명 / outline input stroke를 유지한다. Active override에 영향받지 않고 Accent override를 따른다. Focus override는 별도 ring만 바꾼다.
|
||
- Color 역할 정리: Hover / Soft surface = `--accent` 45% (Select·Command·Combobox·Menu Item highlighted, Calendar hover·range middle, Pagination hover. Table row hover·selected는 2026-10-10 추가 수정 요청부터 `--accent` 20%(`--hm-table-row-hover-background`, `docs/components/table.md`)) / Persistent Selected Surface = `--accent` 100%(Toggle·Toggle Group) / Menubar Top Trigger Highlighted·Open 및 Navigation Menu Trigger·Open = `--accent` 100% + On Accent / Active Control fill = `--hm-sem-state-active`(위 적용 대상) / Focus = Focus chain / Strong Brand·Navigation·Badge default·Pagination current = Primary Green(`--hm-sem-action-primary`, `#164434`) / Error = Destructive.
|
||
- Runtime 공개 계약(`packages/ui/src/theme/contract.ts`)에 Active/on Active를 포함했다. `src/theme/preview.ts`는 패키지 Runtime API의 호환 어댑터다.
|
||
- **Floating Surface Semantic**(`default.css`): `--hm-sem-floating-background|border-width|border-color|radius|shadow`. Dropdown Menu·Context Menu·Popover·Hover Card·Menubar content·Navigation Menu panel의 `--hm-<component>-*`가 이를 참조한다. Toast(Sonner)는 notification surface라 이를 쓰지 않고 `--hm-toast-*`를 따로 둔다(Component Token은 유지하며 컴포넌트별 override 가능). Date Picker·Combobox는 Popover를 조합하므로 별도 mapping이 없다. Tooltip(어두운 면)·Dialog/Alert Dialog/Sheet/Drawer(modal)·Select·Card·Command는 대상이 아니다.
|
||
|
||
> **[임시 — 정리되면 삭제] 현재 코드와 위 기준의 차이**
|
||
> - Primitive 이름: `--hm-warm-neutral-{light,mid,dark}`는 ui_viz `hm-neutral-warm-*`, `--hm-beige-light`는 `hm-neutral-beige-light`, `--hm-green-tint`(#BDDFD4)는 `hm-fg-on-green-secondary`이다. ui_viz Primitive에 없는 이름: `--hm-neutral-gray-500`(#999999, ui_viz는 `color.placeholder`), `--hm-warm-neutral-pale`(#DEDBD2, ui_viz는 Primitive 없이 `color.stroke.input.default`), `--hm-green-tone-1~4`(Button 범위 gradient 값), `--hm-warm-neutral-secondary`(#EDE8DB), `--hm-warm-neutral-muted`(#F4F1EA, 두 값은 ui_viz 문서에서 찾지 못함). Button 작업으로 ui_viz 이름 그대로 추가한 Primitive: `--hm-brown-light-2/-1`, `--hm-brown-dark-1`, `--hm-orange-light-4`, `--hm-orange-default`.
|
||
> - Button Mapping Token은 ui_viz 이름(`--hm-button-radius`, `--hm-button-height-sm/md/lg`, `--hm-button-brand-{green,brown,orange}-{background,border}`, `--hm-button-solid-*` 등)을 따른다. Gray(`#767676`/`#5A5A5A`), Brown·Orange gradient의 일부 stop은 ui_viz가 Button 범위 Component 값으로 정의한 값이라 초기에는 Primitive 없이 `button.css`에 두었으나 현재는 `default.css`의 공개 Theme palette로 이동했다(아래 Runtime 절).
|
||
> - `--hm-radius-button`과 `--hm-radius-input`(Input의 `--hm-input-radius`, Select의 `--hm-select-radius`가 참조)은 공통 `--hm-radius-control-md`(4px)를 참조하며 컨테이너도 같은 값을 쓴다. `--hm-radius-select`는 Select 작업 후 참조하는 곳이 없다. ui_viz는 `radius.input.default` 4px와 `radius.container.default` 4px를 값이 같아도 alias하지 않는다. ui_viz는 Button radius를 Button 범위 Component 값(`button.radius` 4px)으로 두고 다른 컴포넌트로 확대 해석하지 않는다고 적는다.
|
||
|
||
### HM 값과 shadcn 기본값
|
||
|
||
- ui_viz에 `Confirmed` 규격이 있는 속성 → Theme Token 적용
|
||
- 정의가 없거나 `TBD`·`HOLD` 등 미확정인 속성 → 새 Token을 만들지 않고 shadcn 기본 구현 유지
|
||
|
||
### Light / Dark
|
||
|
||
현재 HM Theme Token에는 Light/Dark 구분이 없다. 하나의 HM Theme 값만 정의하며, Dark 전용 값이나 Light/Dark 분기를 추가하지 않는다. 실제 디자인 요구로 Light/Dark가 정의되면 그 시점에 Token 구조를 검토한다.
|
||
|
||
|
||
## 2026-10-06 Runtime 구조 적용
|
||
|
||
이 절과 [Runtime Theme](runtime-theme.md)가 위 초기 시제품 상태 설명보다 우선한다. 모든 Component Mapping은 기존 이름을 유지하고 Semantic / 선택적 Theme palette를 소비한다. Button의 이전 로컬 HEX는 Default preset의 공개 팔레트로 옮겼다. 외부 주입은 공개 Semantic/palette만 허용하고 Component별 Token은 입력 계약에 포함하지 않는다. Default 값과 외부 값은 같은 계약을 사용한다. Accent 45%는 내부 surface-highlight Semantic으로 공통화했다. 전체 계약과 Default 값은 [계약표](runtime-theme-contract.md), 실제 적용·제한은 [Runtime Theme](runtime-theme.md)를 참고한다.
|
||
|
||
# 후속: 디자인팀 컬러 Runtime 편집 — 2026-10-06
|
||
|
||
현재 Storybook의 `Theme/Designer Playground/Editor`에서 JSON URL/파일을 Preview하고 상위 컬러 17개 역할을 편집·Reset·Export/Import할 수 있다. Component Token은 직접 수정하지 않는다. 운영 컬러 원본은 `themes/default.json`/서비스 JSON이며 package fallback CSS/데이터는 생성한다. Production은 `@hm-design/ui/theme`의 URL Loader/Apply를 사용한다. Theme artifact는 UI package와 별도 배포한다. 상세 절차/제한은 [디자인팀 Theme 가이드](designer-theme-guide.md), [Theme delivery](theme-delivery.md), Runtime 적용·제한은 [Runtime Theme](runtime-theme.md)를 참고한다. 기존 디자인 정책/과거 기록은 유지한다.
|
||
|
||
<a id="font-weight-policy"></a>
|
||
|
||
## Font Weight 정책 — 2026-10-14
|
||
|
||
HMDesign의 Font Weight는 400(Regular), 500(Medium), 700(Bold)의 3단계만 사용한다. Font Weight 600은 사용하지 않으며, 카드 제목은 700을 기본으로 한다.
|
||
|
||
- 400: 본문·입력·기본 텍스트. 기존 `--hm-input-font-weight: 400`을 유지한다.
|
||
- 500: 버튼·필드 라벨·직위 배지·일반 강조. `--hm-font-weight-medium: 500`.
|
||
- 700: 제목·카드 제목·직원 이름·그룹 제목·강한 강조. `--hm-font-weight-bold: 700`.
|
||
- `--hm-card-title-font-weight`는 이름을 유지하고 `var(--hm-font-weight-bold)`를 참조한다. 기본 Card와 `size="sm"` Card 제목에 700을 적용한다.
|
||
- 신규 컴포넌트·Storybook·AI 생성 코드에 같은 정책을 적용한다. Tailwind `font-normal` / `font-medium` / `font-bold`만 사용하며 `font-semibold`나 750·800 등 추가 단계는 사용하지 않는다.
|
||
- 폰트 패밀리·크기·행간·자간은 변경하지 않는다. `@font-face`의 100–900은 가변 폰트의 지원 범위이며 UI 사용 단계가 아니다. 폰트 파일과 라이선스는 보존한다.
|
||
- Typography는 Runtime Theme JSON 공개 allowlist 입력이 아니다. 공개 API·외부 CSS 커스터마이징 계약은 유지하며 과거 보고서의 600 실측값은 당시 검증 기록으로 보존한다.
|