# UI Design System — IBM Carbon

> 어떤 스택에서든 동일한 톤을 이식하기 위한 단일 소스. IBM Carbon Design System(carbondesignsystem.com)의 공개 토큰 체계를 웹 이식용으로 재해석한 문서다.
>
> **한 줄 정의**: **직각(radius 0)의 기하학, 2x 그리드, IBM Plex.** 블루(`#0f62fe`)를 유일한 인터랙션 컬러로, 레이어(layer) 개념의 그레이 표면과 촘촘한 스페이싱 토큰 위에 데이터 밀도가 높은 엔터프라이즈 제품을 짓는 시스템.

---

## 목차

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

---

## 1. 디자인 원칙

| 원칙 | 정의 |
|---|---|
| **직각 기하학** | `border-radius: 0`. 곡률 없는 모서리가 Carbon의 시각 서명이다. |
| **2x 그리드** | 모든 치수는 2의 배수, 스페이싱은 8배수 기반 토큰(2/4/8/12/16/24/32/48px). |
| **레이어 시스템** | 표면은 background → layer-01 → layer-02 → layer-03 순으로 쌓인다. 같은 레이어 위에 같은 레이어 금지. |
| **블루 = 인터랙션** | `#0f62fe`는 링크·주 버튼·포커스·선택의 색. 장식 금지. |
| **데이터 우선** | 표·리스트가 1급 시민. 행 높이·정렬·숫자 서체까지 데이터 가독성 기준으로 설계. |
| **의미는 4색** | 오류 red60, 성공 green50, 경고 yellow30, 정보 blue70 — 상태 외 사용 금지. |
| **생산적 모션** | 모션은 90~240ms의 짧은 기능적 전환만. 표현적 모션은 마케팅 영역에서만. |

---

## 2. 색상 토큰

### 2-1. 코어 토큰 (White 테마)

```css
:root {
  --background:        #ffffff;
  --layer-01:          #f4f4f4;  /* 카드·타일 1층 */
  --layer-02:          #ffffff;  /* 1층 위 2층 */
  --layer-03:          #f4f4f4;
  --field:             #f4f4f4;  /* 입력 배경 */

  --text-primary:      #161616;
  --text-secondary:    #525252;
  --text-placeholder:  #a8a8a8;
  --text-on-color:     #ffffff;
  --text-disabled:     #c6c6c6;

  --border-subtle:     #e0e0e0;
  --border-strong:     #8d8d8d;  /* 입력 하단 보더 */
  --border-interactive:#0f62fe;

  --link-primary:      #0f62fe;
  --interactive:       #0f62fe;
  --hover-primary:     #0353e9;
  --active-primary:    #002d9c;

  --support-error:     #da1e28;
  --support-success:   #24a148;
  --support-warning:   #f1c21b;
  --support-info:      #0043ce;

  --focus:             #0f62fe;
  --overlay:           rgba(22, 22, 22, 0.5);
}
```

### 2-2. 사용 규칙

| 역할 | 토큰 |
|---|---|
| 본문·제목 | `var(--text-primary)` |
| 라벨·보조 | `var(--text-secondary)` |
| 페이지 배경 | `var(--background)` |
| 타일·카드 | `var(--layer-01)` (그 위엔 layer-02) |
| 입력 배경 | `var(--field)` + 하단 `--border-strong` 1px |
| 링크·주 버튼 | `var(--interactive)` |
| 구분선 | `var(--border-subtle)` 1px |
| 오류 | `var(--support-error)` |

> **핵심 규칙**: 레이어는 교대로 쌓인다 — 흰 배경 위 회색 타일, 회색 타일 위 흰 카드. 같은 색 표면을 겹치지 않는다.

---

## 3. 타이포그래피

### 3-1. 폰트 — IBM Plex

```css
:root {
  --font-sans: 'IBM Plex Sans KR', 'IBM Plex Sans', 'Noto Sans KR', system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', 'Consolas', monospace;
}

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

```html
<link rel="stylesheet"
  href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+KR:wght@400;600&family=IBM+Plex+Mono&display=swap" />
```

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

| 토큰 | `font-size` / `line-height` | `font-weight` | 용도 |
|---|---|---|---|
| heading-05 | 32 / 40px | 400 | 페이지 제목 |
| heading-04 | 28 / 36px | 400 | 섹션 대제목 |
| heading-03 | 20 / 28px | 400 | 섹션 제목 |
| heading-02 | 16 / 24px | 600 | 카드 제목 |
| heading-01 | 14 / 20px | 600 | 그룹 라벨 |
| body-02 | 16 / 24px | 400 | 긴 본문 |
| body-01 | 14 / 20px | 400 | 기본 본문·표 |
| label-01 | 12 / 16px | 400 | 입력 라벨·헬퍼 |
| code-01 | 12 / 16px | 400 (mono) | 코드·ID |

- 큰 제목이 **Regular(400)** — IBM Plex의 기하학이 무게를 대신한다.
- 표·수치는 `body-01` + `font-variant-numeric: tabular-nums`.

---

## 4. 그리드 & 스페이싱

```css
:root {
  --spacing-01: 0.125rem;  /*  2px */
  --spacing-02: 0.25rem;   /*  4px */
  --spacing-03: 0.5rem;    /*  8px */
  --spacing-04: 0.75rem;   /* 12px */
  --spacing-05: 1rem;      /* 16px */
  --spacing-06: 1.5rem;    /* 24px */
  --spacing-07: 2rem;      /* 32px */
  --spacing-08: 2.5rem;    /* 40px */
  --spacing-09: 3rem;      /* 48px */
}

.container { max-width: 99rem; margin-inline: auto; padding-inline: 1rem; }

/* 16컬럼 2x 그리드 */
.grid {
  display: grid;
  grid-template-columns: repeat(16, 1fr);
  gap: 2rem;
}
```

- 스페이싱은 토큰 9단계만 사용 — `margin: 13px` 같은 값은 존재할 수 없다.
- 컴포넌트 높이 3규격: compact 32px / default 40px / large 48px.

---

## 5. 핵심 컴포넌트

### 5-1. 버튼 — 좌측 정렬 텍스트, 직각

```css
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;   /* 텍스트 좌측, 아이콘 우측 */
  min-height: 3rem;
  padding: 0 4rem 0 1rem;           /* 우측 아이콘 여백 */
  border-radius: 0;
  font-size: 0.875rem;
  cursor: pointer;
  transition: background-color 0.11s ease;
}
.btn--primary   { background: var(--interactive); color: var(--text-on-color); border: none; }
.btn--primary:hover  { background: var(--hover-primary); }
.btn--primary:active { background: var(--active-primary); }
.btn--secondary { background: #393939; color: var(--text-on-color); border: none; }
.btn--tertiary  { background: transparent; color: var(--interactive);
                  border: 1px solid var(--interactive); }
.btn--ghost     { background: transparent; color: var(--interactive); border: none; }
.btn--danger    { background: var(--support-error); color: var(--text-on-color); border: none; }
.btn:disabled   { background: var(--border-subtle); color: var(--text-disabled); }

.btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;   /* 안쪽 포커스 — Carbon 시그니처 */
  box-shadow: inset 0 0 0 3px #ffffff;
}
```

### 5-2. 텍스트 입력 — 하단 보더

```css
.text-input__label {
  display: block;
  font-size: 0.75rem;
  color: var(--text-secondary);
  margin-bottom: 0.5rem;
}
.text-input {
  width: 100%;
  height: 2.5rem;
  padding: 0 1rem;
  background: var(--field);
  border: none;
  border-bottom: 1px solid var(--border-strong);
  border-radius: 0;
  font-size: 0.875rem;
  color: var(--text-primary);
}
.text-input:focus { outline: 2px solid var(--focus); outline-offset: -2px; }
.text-input[aria-invalid="true"] { outline: 2px solid var(--support-error); outline-offset: -2px; }
.text-input__helper { font-size: 0.75rem; color: var(--text-secondary); margin-top: 0.25rem; }
```

### 5-3. 데이터 테이블 — 1급 시민

```css
.table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.table thead th {
  background: var(--layer-01);
  font-weight: 600;
  text-align: left;
  padding: 0 1rem;
  height: 3rem;
}
.table tbody td {
  padding: 0 1rem;
  height: 3rem;
  border-top: 1px solid var(--border-subtle);
  font-variant-numeric: tabular-nums;
}
.table tbody tr:hover { background: #e8e8e8; }
.table tbody tr[aria-selected="true"] { background: #e0e0e0; border-left: 3px solid var(--interactive); }
```

### 5-4. 태그 & 알림

```css
.tag {
  display: inline-flex;
  align-items: center;
  padding: 0 0.5rem;
  height: 1.5rem;
  border-radius: 62.4375rem;   /* 태그만 예외적으로 pill */
  font-size: 0.75rem;
  background: #e0e0e0;
  color: var(--text-primary);
}
.tag--blue { background: #d0e2ff; color: #0043ce; }
.tag--red  { background: #ffd7d9; color: #a2191f; }

.inline-notification {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: 3rem;
  padding: 0.75rem 1rem;
  border-left: 3px solid var(--support-info);
  background: #edf5ff;
}
.inline-notification--error   { border-color: var(--support-error);   background: #fff1f1; }
.inline-notification--success { border-color: var(--support-success); background: #defbe6; }
.inline-notification--warning { border-color: var(--support-warning); background: #fcf4d6; }
```

---

## 6. 인터랙션

- 전환은 90~110ms(마이크로) / 240ms(패널) — `cubic-bezier(0.2, 0, 0.38, 0.9)`.
- 포커스는 **2px 블루 아웃라인, offset -2px(안쪽)** — 모든 컴포넌트 공통.
- 행 호버·선택 등 데이터 인터랙션은 배경 전환만, 이동 모션 없음.
- 스켈레톤은 좌→우 시머(shimmer) 1.5s 루프.

---

## 7. 다크 모드 (Gray 100 테마)

Carbon은 테마가 4종(White/Gray 10/Gray 90/Gray 100)이다. 다크는 Gray 100.

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

  --background:        #161616;
  --layer-01:          #262626;
  --layer-02:          #393939;
  --layer-03:          #525252;
  --field:             #262626;

  --text-primary:      #f4f4f4;
  --text-secondary:    #c6c6c6;
  --text-placeholder:  #6f6f6f;
  --text-disabled:     #525252;

  --border-subtle:     #393939;
  --border-strong:     #6f6f6f;

  --link-primary:      #78a9ff;   /* blue40 */
  --interactive:       #0f62fe;   /* 주 버튼은 유지 */
  --hover-primary:     #0353e9;

  --support-error:     #fa4d56;
  --support-success:   #42be65;
  --support-warning:   #f1c21b;
  --support-info:      #4589ff;

  --overlay:           rgba(0, 0, 0, 0.65);
}
```

- 링크는 blue40(`#78a9ff`)으로 밝아지지만 **주 버튼 blue60은 유지** — 흰 텍스트 대비가 성립하기 때문.
- 레이어가 위로 갈수록 밝아진다 (#161616 → #262626 → #393939).

---

## 8. 접근성

- `--text-primary`(#161616) 대비 17.7:1, `--text-secondary`(#525252) 7.5:1 — AAA/AA.
- 포커스 표시는 IBM 접근성 요건(2px, 고대비)으로 컴포넌트에 내장 — 제거 불가 구조.
- 모든 컴포넌트가 키보드 조작 명세를 갖는다 (탭 순서·화살표 키·Esc).
- 상태 4색은 아이콘과 항상 짝 — 색만으로 오류/성공을 전달하지 않는다.
- 입력 라벨·헬퍼 텍스트 생략 금지, 오류는 헬퍼 위치에 교체 표시.

---

## 9. 안티패턴

| 금지 | 이유 |
|---|---|
| `border-radius` > 0 (태그 제외) | 직각이 Carbon의 서명 |
| 토큰 밖 스페이싱 값 | 2x 그리드 붕괴 |
| 같은 레이어 위 같은 레이어 | 교대 적층 원칙 |
| 블루를 장식·차트 기본색으로 | 블루 = 인터랙션 신호 |
| 제목에 Bold 남용 | 400/600 두 무게만 — 큰 제목은 400 |
| 버튼 텍스트 중앙 정렬 | 좌측 정렬 + 우측 아이콘이 Carbon 규격 |
| 표현적(bouncy) 모션 | 생산적 모션 90~240ms만 |
| 포커스 아웃라인 커스텀 제거 | 접근성 요건 내장 구조 파괴 |

---

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

### 10-1. Tailwind CSS v4

```css
@import "tailwindcss";

@theme {
  --color-background:     #ffffff;
  --color-layer-01:       #f4f4f4;
  --color-text-primary:   #161616;
  --color-text-secondary: #525252;
  --color-border-subtle:  #e0e0e0;
  --color-interactive:    #0f62fe;
  --color-error:          #da1e28;
  --color-success:        #24a148;
  --radius:               0;
}

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

### 10-2. Tailwind CSS v3

```js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        background:  'var(--background)',
        'layer-01':  'var(--layer-01)',
        'layer-02':  'var(--layer-02)',
        't-primary':   'var(--text-primary)',
        't-secondary': 'var(--text-secondary)',
        'border-subtle': 'var(--border-subtle)',
        interactive: 'var(--interactive)',
      },
      borderRadius: { none: '0' },
      spacing: { /* 2x 토큰만 사용하도록 팀 규칙화 */ },
    },
  },
};
```

### 10-3. React

공식 `@carbon/react`가 있다. 직접 구축 시에도 레이어·스페이싱·포커스 규칙을 토큰 층에서 강제한다.

---

## 11. 이식 가이드

### Step 1 — 토큰 복사

§2 White 테마 + §7 Gray 100 블록을 전역 CSS에 붙인다.

### Step 2 — IBM Plex 연결

IBM Plex Sans KR + Plex Mono 링크를 추가한다.

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

| 패턴 | 핵심 CSS |
|---|---|
| **직각 + 안쪽 포커스** | `border-radius: 0` + `outline: 2px solid #0f62fe; outline-offset: -2px` |
| **좌측 정렬 버튼** | `justify-content: space-between; padding-right: 4rem` |
| **하단 보더 입력** | `background: var(--field); border-bottom: 1px solid var(--border-strong)` |

### Step 4 — 레이어 검증

표면 적층이 배경↔레이어 교대인지, 스페이싱이 전부 토큰 값인지 검사한다.

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

```
새 컴포넌트가 필요하다
  → 데이터를 담는가? → 테이블·타일 변형으로 (행 높이 3rem 규격)
  → 어느 레이어에 놓이는가? → 배경↔레이어 교대 확인
  → 높이 3규격(32/40/48px) 중 선택
  → 포커스·키보드 명세를 정의했는가? 아니면 미완성
```
