# UI Design System — Microsoft Fluent

> 어떤 스택에서든 동일한 톤을 이식하기 위한 단일 소스. Microsoft Fluent 2 Design System(fluent2.microsoft.design)의 공개 체계를 웹 이식용으로 재해석한 문서다.
>
> **한 줄 정의**: **빛·깊이·모션을 절제해 쓰는 생산성 시스템.** 브랜드 블루(`#0f6cbd`)와 뉴트럴 그레이 램프, 4px 라운드, 얇은 그림자 3단계로 정보 밀도 높은 업무 도구를 만든다. 컨트롤은 작고(32px), 간격은 촘촘하며, 모든 상태(rest/hover/pressed/selected/disabled)가 토큰으로 정의된다.

---

## 목차

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

---

## 1. 디자인 원칙

| 원칙 | 정의 |
|---|---|
| **생산성 밀도** | 컨트롤 기본 높이 32px, 여백은 촘촘하게 — 한 화면에 더 많은 작업이 보인다. |
| **상태의 토큰화** | rest/hover/pressed/selected/disabled 모든 상태 색이 토큰으로 존재. 임의 상태 색 금지. |
| **얕은 깊이** | 그림자 3단계(2/8/16)로 카드→팝업→다이얼로그를 구분. 과한 부유감 금지. |
| **브랜드 램프** | 블루도 램프(10~160 단계)에서 파생 — hover는 램프의 이웃 단계로 이동한다. |
| **빠른 모션** | 100~200ms의 감속 커브. 업무 흐름을 막는 모션은 없다. |
| **작은 라운드** | 4px 기본, 큰 표면(카드·다이얼로그) 8px. 원형은 아바타·뱃지만. |
| **시스템 일관성** | Windows·Office·Teams가 같은 토큰을 공유한다는 전제 — 앱 임의 변형 최소화. |

---

## 2. 색상 토큰

### 2-1. 브랜드 & 뉴트럴

```css
:root {
  /* Brand ramp 발췌 */
  --brand:            #0f6cbd;  /* brand-80 — 주 버튼·링크 */
  --brand-hover:      #115ea3;  /* brand-70 */
  --brand-pressed:    #0e4775;  /* brand-60 */
  --brand-selected:   #115ea3;
  --brand-background2:#ebf3fc;  /* 옅은 브랜드 면 */

  /* Neutral */
  --bg:               #ffffff;  /* background-1 */
  --bg-2:             #fafafa;
  --bg-3:             #f5f5f5;  /* 캔버스·사이드바 */
  --bg-4:             #f0f0f0;

  --fg:               #242424;  /* foreground-1 본문 */
  --fg-2:             #424242;  /* 보조 */
  --fg-3:             #616161;  /* 힌트·라벨 */
  --fg-disabled:      #bdbdbd;

  --stroke:           #d1d1d1;  /* 컨트롤 보더 */
  --stroke-subtle:    #e0e0e0;  /* 구분선 */

  /* 상태 배경 (뉴트럴 컨트롤) */
  --subtle-hover:     #f5f5f5;
  --subtle-pressed:   #e0e0e0;
  --subtle-selected:  #ebebeb;

  /* Semantic */
  --danger:           #c50f1f;
  --danger-bg:        #fdf3f4;
  --success:          #107c10;
  --success-bg:       #f1faf1;
  --warning:          #bc4b09;
  --warning-bg:       #fff9f5;
}
```

### 2-2. 그림자

```css
:root {
  --shadow-2:  0 1px 2px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);   /* 카드 */
  --shadow-8:  0 4px 8px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);   /* 메뉴·팝오버 */
  --shadow-16: 0 8px 16px rgba(0,0,0,.14), 0 0 2px rgba(0,0,0,.12);  /* 다이얼로그 */
}
```

### 2-3. 사용 규칙

| 역할 | 토큰 |
|---|---|
| 본문·제목 | `var(--fg)` |
| 보조·설명 | `var(--fg-2)` |
| 라벨·메타 | `var(--fg-3)` |
| 앱 캔버스 | `var(--bg-3)` (콘텐츠 카드가 `--bg`) |
| 주 버튼·링크 | `var(--brand)` |
| 뉴트럴 컨트롤 hover | `var(--subtle-hover)` |
| 컨트롤 보더 | `var(--stroke)` 1px |
| 오류 텍스트/면 | `var(--danger)` / `var(--danger-bg)` |

> **핵심 규칙**: hover/pressed는 반드시 램프의 정의된 이웃 토큰으로 — 밝기 임의 조절 금지.

---

## 3. 타이포그래피

### 3-1. 폰트 — Segoe UI

```css
:root {
  --font-sans: 'Segoe UI Variable', 'Segoe UI', 'Noto Sans KR',
               'Malgun Gothic', system-ui, sans-serif;
}

body { font-family: var(--font-sans); color: var(--fg); background: var(--bg-3); }
```

```html
<!-- 비 Windows 환경 한글 폴백 -->
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;600;700&display=swap" />
```

### 3-2. 타입 스케일 (Fluent 2 ramp)

| 토큰 | `font-size` / `line-height` | `font-weight` |
|---|---|---|
| Display | 68 / 92px | 600 |
| Large Title | 40 / 52px | 600 |
| Title 1 | 28 / 36px | 600 |
| Title 2 | 24 / 32px | 600 |
| Title 3 | 20 / 28px | 600 |
| Subtitle 1 | 20 / 26px | 600 |
| Subtitle 2 | 16 / 22px | 600 |
| Body 1 (기본) | 14 / 20px | 400 |
| Body 1 Strong | 14 / 20px | 600 |
| Body 2 | 12 / 16px | 400 |
| Caption 1 | 12 / 16px | 400 |
| Caption 2 | 10 / 14px | 400 |

- **기본 본문이 14px** — 생산성 도구의 밀도 기준. 문서형 콘텐츠만 16px.
- 강조는 Semibold(600) — Bold(700)는 거의 쓰지 않는다.

---

## 4. 레이아웃 & 형태

```css
:root {
  --radius-sm: 0.125rem;  /* 2px — 체크박스 */
  --radius-md: 0.25rem;   /* 4px — 버튼·입력 */
  --radius-lg: 0.5rem;    /* 8px — 카드·다이얼로그 */
  --radius-xl: 0.75rem;
  --gap-xs: 0.25rem; --gap-sm: 0.5rem; --gap-md: 0.75rem; --gap-lg: 1rem;
}

/* 앱 셸: 사이드바 + 캔버스 */
.shell { display: grid; grid-template-columns: 17.5rem 1fr; min-height: 100vh; }
.sidebar { background: var(--bg-3); border-right: 1px solid var(--stroke-subtle); }
.canvas  { background: var(--bg-3); padding: 1.5rem; }
.canvas__card { background: var(--bg); border-radius: var(--radius-lg); box-shadow: var(--shadow-2); }
```

---

## 5. 핵심 컴포넌트

### 5-1. 버튼 — 32px 컴팩트

```css
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  min-height: 2rem;              /* 32px */
  padding: 0 0.75rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.1s ease, border-color 0.1s ease;
}
.btn--primary {
  background: var(--brand);
  color: #ffffff;
  border: 1px solid transparent;
}
.btn--primary:hover  { background: var(--brand-hover); }
.btn--primary:active { background: var(--brand-pressed); }

.btn--default {                   /* 뉴트럴이 기본형 */
  background: var(--bg);
  color: var(--fg);
  border: 1px solid var(--stroke);
}
.btn--default:hover { background: var(--subtle-hover); }

.btn--subtle {                    /* 툴바용 투명 버튼 */
  background: transparent;
  color: var(--fg);
  border: 1px solid transparent;
}
.btn--subtle:hover { background: var(--subtle-hover); }

.btn:disabled { background: var(--bg-4); color: var(--fg-disabled); border-color: transparent; }
```

### 5-2. 입력 — 하단 강조 보더

```css
.input {
  width: 100%;
  min-height: 2rem;
  padding: 0 0.625rem;
  background: var(--bg);
  border: 1px solid var(--stroke);
  border-bottom-color: #8a8a8a;         /* 하단만 진하게 */
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  color: var(--fg);
}
.input:focus-within, .input:focus {
  outline: none;
  border-bottom: 2px solid var(--brand);  /* 포커스 시 하단 브랜드 라인 */
}
```

### 5-3. 리스트/트리 내비게이션 (사이드바)

```css
.nav-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.25rem;
  padding: 0 0.75rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  color: var(--fg-2);
  cursor: pointer;
  position: relative;
}
.nav-item:hover { background: var(--subtle-hover); }
.nav-item[aria-current="page"] {
  background: var(--subtle-selected);
  color: var(--fg);
  font-weight: 600;
}
.nav-item[aria-current="page"]::before {   /* 좌측 브랜드 인디케이터 */
  content: '';
  position: absolute;
  left: 0; top: 25%;
  width: 3px; height: 50%;
  border-radius: 62.4375rem;
  background: var(--brand);
}
```

### 5-4. 메시지 바

```css
.message-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.25rem;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--stroke-subtle);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  background: var(--bg-2);
}
.message-bar--error   { background: var(--danger-bg);  border-color: #eeacb2; }
.message-bar--success { background: var(--success-bg); border-color: #9fd89f; }
.message-bar--warning { background: var(--warning-bg); border-color: #fdcfb4; }
```

### 5-5. Persona (아바타 + 상태)

```css
.persona { display: flex; align-items: center; gap: 0.625rem; }
.persona__avatar {
  width: 2rem; height: 2rem;
  border-radius: 50%;
  position: relative;
}
.persona__presence {
  position: absolute;
  right: -2px; bottom: -2px;
  width: 0.625rem; height: 0.625rem;
  border-radius: 50%;
  border: 2px solid var(--bg);
  background: var(--success);   /* available */
}
.persona__name { font-size: 0.875rem; font-weight: 600; }
.persona__sub  { font-size: 0.75rem; color: var(--fg-3); }
```

---

## 6. 인터랙션

- 전환 100~200ms, 감속 커브 `cubic-bezier(0.33, 0, 0.67, 1)`.
- 메뉴·팝오버는 8px 아래에서 페이드+슬라이드 등장, `--shadow-8`.
- 포커스 링: `outline: 2px solid var(--fg); border-radius: inherit` — 브랜드색이 아닌 **전경색 링**(Windows 관례).
- 키보드 우선: 모든 상호작용에 단축키·화살표 내비게이션을 계획한다.

---

## 7. 다크 모드

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

  --bg:      #292929;   /* background-1 */
  --bg-2:    #1f1f1f;
  --bg-3:    #141414;   /* 캔버스 */
  --bg-4:    #0a0a0a;

  --fg:          #ffffff;
  --fg-2:        #d6d6d6;
  --fg-3:        #adadad;
  --fg-disabled: #5c5c5c;

  --stroke:        #666666;
  --stroke-subtle: #333333;

  --brand:         #479ef5;   /* brand-100 — 다크에서 밝게 */
  --brand-hover:   #62abf5;
  --brand-pressed: #2886de;
  --brand-background2: #082338;

  --subtle-hover:    #383838;
  --subtle-pressed:  #2e2e2e;
  --subtle-selected: #333333;

  --danger:  #f1707b;  --danger-bg:  #3b1e1e;
  --success: #6ccb5f;  --success-bg: #1e2e1e;
  --warning: #f7963d;  --warning-bg: #35291d;
}
```

- 브랜드가 램프의 밝은 구간(brand-100 `#479ef5`)으로 이동 — 짙은 캔버스 위 대비 확보.
- 캔버스(#141414)보다 카드(#292929)가 밝다 — 표면이 위로 갈수록 밝아지는 적층.

---

## 8. 접근성

- `--fg`(#242424) 대비 15.5:1, `--fg-3`(#616161) 5.9:1 — AA 이상.
- 기본 텍스트 14px이므로 보조 정보는 12px 미만으로 내리지 않는다.
- 포커스 링은 전경색 2px — 고대비 모드(`forced-colors`)에서도 성립.
- 모든 컴포넌트에 키보드 명세 필수 — 메뉴는 화살표, 다이얼로그는 포커스 트랩+Esc.
- Windows 고대비 모드 대응: `forced-colors: active`에서 시스템 색으로 대체되도록 시맨틱 HTML 유지.

---

## 9. 안티패턴

| 금지 | 이유 |
|---|---|
| hover 밝기 임의 조절 | 램프의 정의된 이웃 토큰으로만 이동 |
| 48px 큰 컨트롤 남발 | 32px 컴팩트가 생산성 기준 |
| Bold(700) 강조 | Semibold(600)가 강조 무게 |
| 그림자 3단계 외 값 | 2/8/16 규격만 |
| 브랜드색 포커스 링 | 전경색 링이 Windows 관례 |
| 느린 모션(300ms+) | 업무 흐름 차단 |
| 캔버스와 같은 색 카드 | 캔버스(bg-3) 위 카드(bg) 대비 유지 |
| 키보드 명세 없는 컴포넌트 | 접근성 미완성 |

---

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

### 10-1. Tailwind CSS v4

```css
@import "tailwindcss";

@theme {
  --color-bg:      #ffffff;
  --color-canvas:  #f5f5f5;
  --color-fg:      #242424;
  --color-fg-2:    #424242;
  --color-fg-3:    #616161;
  --color-stroke:  #d1d1d1;
  --color-brand:   #0f6cbd;
  --color-danger:  #c50f1f;
  --radius-md:     0.25rem;
  --radius-lg:     0.5rem;
}

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

### 10-2. Tailwind CSS v3

```js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        bg:     'var(--bg)',
        canvas: 'var(--bg-3)',
        fg:     'var(--fg)',
        'fg-2': 'var(--fg-2)',
        stroke: 'var(--stroke)',
        brand:  'var(--brand)',
        danger: 'var(--danger)',
      },
      borderRadius: { md: '0.25rem', lg: '0.5rem' },
      boxShadow: {
        2:  'var(--shadow-2)',
        8:  'var(--shadow-8)',
        16: 'var(--shadow-16)',
      },
    },
  },
};
```

### 10-3. React

공식 Fluent UI React(`@fluentui/react-components`)가 토큰을 내장한다. 직접 구축 시 상태 토큰(hover/pressed/selected)까지 변수로 정의하는 것이 Fluent다움의 핵심.

---

## 11. 이식 가이드

### Step 1 — 토큰 복사

§2 라이트 + §7 다크 블록, 그림자 3종을 전역 CSS에 붙인다.

### Step 2 — 폰트 연결

Segoe UI 스택 + Noto Sans KR 폴백을 설정한다.

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

| 패턴 | 핵심 CSS |
|---|---|
| **32px 컴팩트 버튼** | `min-height: 2rem; border-radius: 4px; font-weight: 600` |
| **하단 강조 입력** | 하단 보더만 진하게 → 포커스 시 브랜드 2px |
| **사이드바 선택 표시** | `subtle-selected` 배경 + 좌측 3px 브랜드 인디케이터 |

### Step 4 — 상태 토큰 검증

hover/pressed/selected가 전부 정의된 토큰 값인지 검사한다 — 임의 밝기 조절이 발견되면 실패.

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

```
새 컴포넌트가 필요하다
  → 5상태(rest/hover/pressed/selected/disabled) 토큰을 정의했는가?
    no → 미완성 — 토큰부터
  → 높이는 32px 기준인가? (터치 전용 화면만 40px+)
  → 그림자는 2/8/16 중 하나인가?
  → 키보드 명세가 있는가?
```
