# UI Design System — Astryx

> 어떤 스택에서든 동일한 톤을 이식하기 위한 단일 소스. Meta의 오픈소스 디자인 시스템 Astryx(astryx.atmeta.com, `@astryxdesign/core`) 공개 문서를 웹 이식용으로 재해석한 문서다.
>
> **한 줄 정의**: **"모든 변형·상태·패턴에 카피-레디 예제를."** 블루(`#0064e0`)를 단일 액션 컬러로, 다크 블루 틴트의 텍스트(`#0a1317`)와 쿨 그레이 뉴트럴 위에 **채팅·AI 패턴을 1급 컴포넌트로 포함**하는 90여 종 카탈로그. 모든 색은 시맨틱 토큰이며, 테마 빌더로 램프 전체를 교체할 수 있는 구조가 전제다.

---

## 목차

1. [디자인 원칙](#1-디자인-원칙)
2. [색상 토큰](#2-색상-토큰)
3. [타이포그래피](#3-타이포그래피)
4. [레이아웃 & 형태](#4-레이아웃--형태)
5. [핵심 컴포넌트](#5-핵심-컴포넌트)
6. [인터랙션](#6-인터랙션)
7. [다크 모드](#7-다크-모드)
8. [접근성](#8-접근성)
9. [안티패턴](#9-안티패턴)
10. [프레임워크 어댑터](#10-프레임워크-어댑터)
11. [이식 가이드](#11-이식-가이드)

---

## 1. 디자인 원칙

| 원칙 | 정의 |
|---|---|
| **Copy-Ready** | 모든 컴포넌트는 변형·상태·패턴별로 즉시 복사해 쓸 수 있는 완성 예제로 제공된다. 반쪽짜리 데모 금지. |
| **시맨틱 토큰 + 테마** | 컴포넌트는 역할 토큰만 참조한다. 브랜드 램프를 통째로 갈아끼우는 테마 빌더가 전제이므로 하드코딩은 시스템 파괴다. |
| **채팅은 1급 시민** | Chat Composer·Message·Tool Calls가 코어 카탈로그에 포함 — AI 대화 UI가 부속이 아니라 기본 패턴이다. |
| **App Shell 우선** | 화면은 Top Nav + Side Nav + 콘텐츠의 App Shell 골격에서 시작한다. 페이지마다 다른 골격 금지. |
| **단일 블루 액션** | `#0064e0` 하나가 링크·주 버튼·선택·포커스를 담당. 의미색(레드·그린·옐로)은 상태 전달 전용. |
| **밀도 있는 도구 UI** | 테이블·트리·커맨드 팔레트 등 프로덕티비티 컴포넌트가 풍부 — 32~36px 컨트롤의 업무 밀도. |

---

## 2. 색상 토큰

### 2-1. 브랜드 & 시맨틱

```css
:root {
  --primary:         #0064e0;   /* 블루 — 액션·링크·포커스 */
  --primary-hover:   #0055bd;
  --primary-light:   #2694fe;   /* 차트·보조 강조 */
  --primary-surface: #e7f1fd;
  --primary-deep:    #053659;   /* 네이비 — 강조 면 */

  --negative:  #e3193b;  --negative-strong: #f5394f;
  --positive:  #0d8626;  --positive-strong: #0b991f;
  --warning:   #e2a400;
  --notice:    #f27902;

  --accent-purple: #7952ff;  /* AI·프리미엄 맥락 */
  --accent-pink:   #e638b3;
}
```

### 2-2. 뉴트럴 — 쿨 그레이 램프

```css
:root {
  --bg:          #ffffff;
  --surface:     #f2f4f6;   /* 캔버스·섹션 배경 */
  --surface-2:   #e9edf0;

  --text:        #0a1317;   /* 다크 블루 틴트 블랙 */
  --text-sub:    #4e606f;   /* 보조 텍스트 */
  --text-hint:   #aaafb5;   /* 플레이스홀더 */

  --border:      #dfe2e5;   /* 구분선 */
  --border-mid:  #ccd3db;   /* 컨트롤 보더 */
}
```

### 2-3. 사용 규칙

| 역할 | 토큰 |
|---|---|
| 본문·제목 | `var(--text)` |
| 보조 설명·메타 | `var(--text-sub)` |
| 캔버스 배경 | `var(--surface)` (카드가 `--bg`) |
| 구분선 | `var(--border)` 1px |
| 컨트롤 보더 | `var(--border-mid)` 1px |
| 주 버튼·링크·선택·포커스 | `var(--primary)` |
| 오류·파괴적 액션 | `var(--negative)` |
| 성공 | `var(--positive)` |
| AI 맥락 강조 | `var(--accent-purple)` |

> **핵심 규칙**: 토큰은 테마로 교체될 수 있다는 전제로 쓴다 — "이 파랑이 예쁘니까 hex로 박자"는 순간 테마 빌더와 결별한다.

---

## 3. 타이포그래피

### 3-1. 폰트 — 시스템 스택

플랫폼 네이티브 렌더링을 우선한다. 한국어 폴백으로 Noto Sans KR을 연결한다.

```css
:root {
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
               'Apple SD Gothic Neo', 'Noto Sans KR', sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Consolas, monospace;
}

body { font-family: var(--font-sans); color: var(--text); background: var(--surface); }
code, kbd, .code { font-family: var(--font-mono); }
```

### 3-2. 타입 스케일

| 역할 | `font-size` | `font-weight` | 용도 |
|---|---|---|---|
| Heading XL | 28px | 700 | 페이지 제목 |
| Heading LG | 22px | 700 | 섹션 제목 |
| Heading MD | 17px | 600 | 카드·패널 제목 |
| Heading SM | 15px | 600 | 그룹 라벨 |
| Body | 14px | 400 | 기본 본문·테이블 |
| Body LG | 16px | 400 | 문서형 본문 |
| Meta | 12px | 400 | 타임스탬프·캡션 |
| Kbd/Code | 12px | 400 (mono) | 단축키·코드 |

- **기본 본문 14px** — 도구 UI 밀도 기준. 문서·채팅 본문만 16px.
- Kbd·Timestamp·Citation·Token 등 마이크로 텍스트 컴포넌트가 별도 규격으로 존재한다.

---

## 4. 레이아웃 & 형태

```css
:root {
  --radius-sm:  0.25rem;   /* 4px — 뱃지·kbd */
  --radius-md:  0.5rem;    /* 8px — 버튼·입력·카드 */
  --radius-lg:  0.75rem;   /* 12px — 다이얼로그·팝오버 */
  --radius-full: 62.4375rem;

  --shadow-popover: 0 4px 12px rgba(10, 19, 23, 0.12);
  --shadow-dialog:  0 12px 32px rgba(10, 19, 23, 0.18);
}

/* App Shell — 모든 화면의 골격 */
.shell {
  display: grid;
  grid-template-rows: 3.5rem 1fr;
  grid-template-columns: 16rem 1fr;
  min-height: 100vh;
}
.top-nav  { grid-column: 1 / -1; background: var(--bg); border-bottom: 1px solid var(--border); }
.side-nav { background: var(--bg); border-right: 1px solid var(--border); }
.content  { background: var(--surface); padding: 1.5rem; overflow: auto; }
.content__card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-md); }
```

- 스택 레이아웃(HStack/VStack)과 Grid가 배치의 기본 프리미티브 — 임의 마진 대신 스택 gap.

---

## 5. 핵심 컴포넌트

카탈로그는 10개 카테고리 90여 종: **Action**(Button·Dropdown·Segmented Control·Toolbar), **Chat**(Composer·Message·Tool Calls), **Container**(Card·Carousel·Collapsible), **Content**(Avatar·Code·Markdown·Timestamp·Token), **Data Input**(Field·Power Search·Typeahead·Tokenizer), **Feedback**(Badge·Banner·Skeleton·Spinner), **Layout**(App Shell·Stack·Grid), **Navigation**(Breadcrumbs·Side Nav·Tabs·Mega Menu), **Overlay**(Command Palette·Dialog·Toast·Hover Card), **Table & List**(Table·Tree List·Metadata List).

### 5-1. 채팅 메시지 — 시그니처

```html
<div class="chat">
  <div class="msg msg--user">
    <p class="msg__body">이번 분기 지표를 요약해 줘</p>
    <span class="msg__time">오후 2:41</span>
  </div>
  <div class="msg msg--assistant">
    <div class="msg__tool">
      <span class="msg__tool-icon">⚙</span> query_metrics 실행 중…
    </div>
    <p class="msg__body">이번 분기 핵심 지표는 다음과 같습니다…</p>
    <div class="msg__citation">출처: Q3 리포트</div>
  </div>
</div>
```

```css
.msg { max-width: 42rem; margin-bottom: 1rem; }
.msg--user .msg__body {
  background: var(--primary);
  color: #ffffff;
  border-radius: var(--radius-lg);
  padding: 0.625rem 0.875rem;
  display: inline-block;
}
.msg--assistant .msg__body {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0.75rem 1rem;
}
.msg__tool {
  font-size: 0.75rem;
  color: var(--text-sub);
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 0.375rem 0.625rem;
  margin-bottom: 0.375rem;
  font-family: var(--font-mono);
}
.msg__citation { font-size: 0.75rem; color: var(--primary); margin-top: 0.375rem; }
.msg__time { font-size: 0.6875rem; color: var(--text-hint); }
```

### 5-2. 버튼

```css
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-height: 2.25rem;             /* 36px */
  padding: 0 0.875rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.btn--primary  { background: var(--primary); color: #fff; border: none; }
.btn--primary:hover { background: var(--primary-hover); }
.btn--secondary { background: var(--bg); color: var(--text);
                  border: 1px solid var(--border-mid); }
.btn--secondary:hover { background: var(--surface); }
.btn--ghost    { background: transparent; color: var(--text); border: none; }
.btn--danger   { background: var(--negative); color: #fff; border: none; }
.btn:disabled  { background: var(--surface-2); color: var(--text-hint); border: none; }
```

### 5-3. 커맨드 팔레트

```css
.cmdk {
  width: min(36rem, 90vw);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-dialog);
  overflow: hidden;
}
.cmdk__input {
  width: 100%;
  padding: 0.875rem 1rem;
  border: none;
  border-bottom: 1px solid var(--border);
  font-size: 0.9375rem;
  outline: none;
}
.cmdk__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.625rem 1rem;
  font-size: 0.875rem;
  cursor: pointer;
}
.cmdk__item[aria-selected="true"] { background: var(--primary-surface); color: var(--primary); }
.cmdk__kbd {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.125rem 0.375rem;
}
```

### 5-4. 테이블 & 상태 뱃지

```css
.table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.table th {
  text-align: left; font-weight: 600; color: var(--text-sub);
  padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border-mid);
}
.table td { padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--border); }
.table tr:hover { background: var(--surface); }

.badge {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  border-radius: var(--radius-full);
  font-size: 0.75rem; font-weight: 500;
}
.badge--positive { background: #e2f4e4; color: var(--positive); }
.badge--negative { background: #fde8ec; color: var(--negative); }
.badge--warning  { background: #fdf3d7; color: #8a6400; }
.badge--neutral  { background: var(--surface-2); color: var(--text-sub); }
```

### 5-5. 아바타 + 상태 점

```css
.avatar { position: relative; width: 2rem; height: 2rem; border-radius: 50%; }
.avatar__dot {
  position: absolute; right: -1px; bottom: -1px;
  width: 0.625rem; height: 0.625rem;
  border-radius: 50%;
  border: 2px solid var(--bg);
  background: var(--positive-strong);
}
.avatar-group { display: flex; }
.avatar-group .avatar { margin-left: -0.5rem; border: 2px solid var(--bg); }
```

---

## 6. 인터랙션

- 전환 `0.12~0.2s ease` — 도구 UI는 즉답이 기본.
- 커맨드 팔레트(⌘K)·단축키가 1급 인터랙션 — 모든 주요 동작에 Kbd 표기를 병행.
- 스트리밍 응답(채팅)은 커서 깜빡임 + 점진 렌더링, 완료 후 툴 콜 접기.
- 포커스 링: `outline: 2px solid var(--primary); outline-offset: 2px` — 전 컴포넌트 공통.
- `prefers-reduced-motion` 시 스트리밍 커서·전환 애니메이션 제거.

---

## 7. 다크 모드

테마 램프를 다크 구간으로 교체한다 — 시맨틱 토큰 구조 덕에 컴포넌트 수정은 0.

```css
.dark {
  color-scheme: dark;

  --bg:        #16191c;
  --surface:   #0f1113;
  --surface-2: #23272b;

  --text:      #e8ebee;
  --text-sub:  #9aa4ad;
  --text-hint: #5c666f;

  --border:     #2b3036;
  --border-mid: #3a4148;

  --primary:         #2694fe;   /* 블루 한 단계 밝게 */
  --primary-hover:   #4da6ff;
  --primary-surface: #0d2946;

  --negative: #f5394f;
  --positive: #0b991f;
  --warning:  #e2a400;

  --accent-purple: #9b7bff;
  --accent-pink:   #f06cc8;

  --shadow-popover: 0 4px 12px rgba(0, 0, 0, 0.4);
  --shadow-dialog:  0 12px 32px rgba(0, 0, 0, 0.5);
}
```

- 캔버스(#0f1113)보다 카드(#16191c)가 밝아지는 적층 — 라이트와 방향 반전.
- 유저 말풍선은 다크에서도 블루 유지, 어시스턴트 말풍선은 카드 표면.

---

## 8. 접근성

- `--text`(#0a1317) 대비 17.4:1, `--text-sub`(#4e606f) 6.6:1 — AA 이상.
- `--text-hint`(#aaafb5)는 플레이스홀더 전용 (대비 2.2:1).
- 커맨드 팔레트·드롭다운·트리는 완전한 키보드 명세(화살표·Home/End·타이핑 점프) 필수.
- 채팅 스트리밍은 `aria-live="polite"`, 툴 콜 상태는 텍스트로 병기.
- 상태 뱃지·상태 점은 색+텍스트/`aria-label` 이중 전달.
- 단축키는 Kbd 컴포넌트로 시각 표기 + `aria-keyshortcuts` 속성.

---

## 9. 안티패턴

| 금지 | 이유 |
|---|---|
| 시맨틱 토큰 우회 hex 하드코딩 | 테마 빌더와 결별 — 시스템 파괴 |
| 블루 외 색을 액션에 사용 | 단일 액션 컬러 원칙 |
| 의미색을 장식으로 사용 | 레드/그린/옐로는 상태 전달 전용 |
| App Shell 밖 임의 골격 | 화면 골격 통일 원칙 |
| 스택 대신 임의 마진 | HStack/VStack gap이 배치 기본 |
| 미완성 데모 예제 | Copy-Ready — 모든 변형·상태 완비 |
| 채팅 UI를 커스텀 조립 | Chat 컴포넌트군이 1급 시민 |
| 키보드 명세 없는 오버레이 | 팔레트·다이얼로그는 키보드 우선 |

---

## 10. 프레임워크 어댑터

### 10-1. React (공식)

```bash
npm install @astryxdesign/core
```

공식 React 컴포넌트를 개별 경로에서 임포트한다. 토큰 오버라이드는 테마 층에서만.

### 10-2. Tailwind CSS v4

```css
@import "tailwindcss";

@theme {
  --color-bg:        #ffffff;
  --color-surface:   #f2f4f6;
  --color-text:      #0a1317;
  --color-text-sub:  #4e606f;
  --color-border:    #dfe2e5;
  --color-primary:   #0064e0;
  --color-negative:  #e3193b;
  --color-positive:  #0d8626;
  --radius-md:       0.5rem;
  --radius-lg:       0.75rem;
}

@custom-variant dark (&:where(.dark, .dark *));
```

### 10-3. Tailwind CSS v3

```js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        bg:        'var(--bg)',
        surface:   'var(--surface)',
        text:      'var(--text)',
        'text-sub':'var(--text-sub)',
        border:    'var(--border)',
        primary:   'var(--primary)',
        negative:  'var(--negative)',
        positive:  'var(--positive)',
      },
      borderRadius: { md: '0.5rem', lg: '0.75rem' },
    },
  },
};
```

---

## 11. 이식 가이드

### Step 1 — 토큰 복사

§2 라이트 + §7 다크 블록을 전역 CSS에 붙인다. 브랜드가 다르면 램프만 교체 — 역할 구조는 유지.

### Step 2 — 시스템 폰트 스택

`-apple-system` 스택 + Noto Sans KR 폴백, 코드·단축키는 모노스페이스.

### Step 3 — 시그니처 3종 적용

| 패턴 | 핵심 CSS |
|---|---|
| **App Shell** | Top Nav 56px + Side Nav 256px + `--surface` 캔버스 위 카드 |
| **채팅 메시지** | 유저=블루 말풍선 / 어시스턴트=카드 + 툴 콜·인용 마이크로 UI |
| **커맨드 팔레트** | ⌘K 오버레이 + kbd 표기 + `aria-selected` 하이라이트 |

### Step 4 — 상태 완비 검증

새 컴포넌트는 모든 변형×상태 조합의 예제가 있어야 완성이다 — Copy-Ready 기준.

### 신규 컴포넌트 결정 트리

```
새 컴포넌트가 필요하다
  → 90여 종 카탈로그의 조합으로 가능한가?
    yes → 조합 사용 (Stack + Card + Badge 등)
    no  → 시맨틱 토큰만으로 색 구성이 되는가?
      yes → 추가하되 키보드 명세 + 전체 상태 예제 완비
      no  → 토큰 램프 확장부터 재검토
  → 대화형 UI인가? → Chat 컴포넌트군 확장으로
```
