<!-- AUTO-GENERATED by apps/docs/scripts/generate-design-md.ts — DO NOT EDIT BY HAND.
     Token values come from the design-system SSOT (tokens.generated.ts ← index.css).
     Regenerate: pnpm --filter docs generate:design -->

# k8ordo UI Design System

`@k8ordo/ui` のデザインシステム仕様。デザイントークン・タイポグラフィ・コンポーネントの単一の参照元です。人間にも LLM／エージェントにも読めるよう、`https://ordo.k8o.me/design` の Markdown 版として配信しています。

- パッケージ: `@k8ordo/ui`（npm, public）
- スタック: React + Tailwind CSS 4 + OKLCH カラー
- トークン定義の実体: `packages/ui/src/styles/{tokens,base,utilities}.css`

> 生のカラー値（`bg-teal-500` 等）は使わず、常にセマンティックトークン（`bg-primary-bg` 等）を使います。トークンはダークモードで自動的に再マッピングされます。

## 原則

**「柔らかな余白と静かな洗練」** — 色の華やかさではなく、余白のゆとりと形の柔らかさで個性を出すミニマルなデザイン。

**「触れるものは柔らかく、読むものは端正に」**

- 触れる要素（Button, Input, Card）は柔らかく — 大きな角丸、ピル型、ゆったりしたパディング
- 情報を示す要素（Tabs, Breadcrumb, Table）は端正に — 構造を明確に

中核となる規範:

- **60-30-10** — 60% ニュートラル（グレー系）、30% サポート（`bg-subtle` 等）、10% アクセント（primary）
- **穏やかな色** — パレットは OKLCH で鮮やかに保ちつつ、UI に出るトークンは抑えたトーンへマッピング
- **WCAG AAA** — fg / bg の組み合わせで 7:1 以上のコントラストを確保
- **静かな変化** — トランジションは 150–200ms、bounce / spring 系は使わない
- **余白で語る** — 余白の差で情報の関連度と階層を表現する
- **日本語最適化** — Noto Sans JP / M PLUS 2。Inter / Roboto / Open Sans は使わない

## カラー

### 設計

全色を OKLCH 色空間で定義。明度（L）を全色相で統一しているため、同じステップ番号同士のコントラストが揃う。Chroma は色相ごとに gamut 内で最適化。

明度スケール（全色相で共通）:

| Step | L | 意図 |
| --- | --- | --- |
| 50 | 0.975 | 最も薄い背景 |
| 100 | 0.955 | 薄い背景 |
| 200 | 0.925 | 控えめな背景 |
| 300 | 0.84 | サポートカラー |
| 400 | 0.75 | 中間トーン |
| 500 | 0.66 | コアカラー |
| 600 | 0.52 | やや暗い |
| 700 | 0.42 | 暗いトーン |
| 800 | 0.3 | テキスト用（AAA on white） |
| 900 | 0.25 | 最も暗い |
| 950 | 0.18 | 反転背景・最暗部 |

色相（H）と役割:

| 色相 | H | 役割 |
| --- | --- | --- |
| Gray | 235 | ニュートラル（sky blue tint・低彩度） |
| Red | 25 | error |
| Pink | 350 | group |
| Purple | 305 | group |
| Cyan | 210 | **Secondary** |
| Blue | 260 | info |
| Teal | 180 | **Primary** |
| Green | 150 | success |
| Yellow | 90 | warning |
| Orange | 55 | — |

### 生パレット（OKLCH）

各セルは `L C`（H は色相表の通り）。`--white: oklch(1 0 0)`。

| Step | Gray·235 | Red·25 | Pink·350 | Purple·305 | Cyan·210 | Blue·260 | Teal·180 | Green·150 | Yellow·90 | Orange·55 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 50 | `0.975 0.001` | `0.975 0.016` | `0.975 0.016` | `0.975 0.018` | `0.975 0.022` | `0.975 0.016` | `0.975 0.02` | `0.975 0.02` | `0.975 0.03` | `0.975 0.02` |
| 100 | `0.955 0.0015` | `0.945 0.042` | `0.945 0.038` | `0.945 0.042` | `0.955 0.04` | `0.945 0.04` | `0.955 0.042` | `0.945 0.052` | `0.945 0.08` | `0.945 0.05` |
| 200 | `0.925 0.003` | `0.9 0.084` | `0.9 0.078` | `0.9 0.082` | `0.9 0.098` | `0.9 0.078` | `0.9 0.11` | `0.9 0.1` | `0.9 0.148` | `0.9 0.098` |
| 300 | `0.84 0.004` | `0.84 0.15` | `0.84 0.145` | `0.84 0.155` | `0.84 0.155` | `0.84 0.145` | `0.84 0.16` | `0.84 0.175` | `0.84 0.2` | `0.84 0.165` |
| 400 | `0.75 0.005` | `0.75 0.225` | `0.75 0.225` | `0.75 0.24` | `0.75 0.18` | `0.75 0.21` | `0.75 0.175` | `0.75 0.235` | `0.75 0.2` | `0.75 0.22` |
| 500 | `0.66 0.006` | `0.66 0.27` | `0.66 0.27` | `0.66 0.29` | `0.66 0.17` | `0.66 0.255` | `0.66 0.165` | `0.66 0.245` | `0.66 0.18` | `0.66 0.24` |
| 600 | `0.52 0.006` | `0.575 0.255` | `0.575 0.26` | `0.575 0.295` | `0.575 0.15` | `0.575 0.275` | `0.575 0.145` | `0.575 0.22` | `0.575 0.158` | `0.575 0.235` |
| 700 | `0.42 0.003` | `0.49 0.22` | `0.49 0.23` | `0.49 0.265` | `0.49 0.128` | `0.49 0.26` | `0.49 0.12` | `0.49 0.18` | `0.49 0.135` | `0.49 0.2` |
| 800 | `0.3 0.002` | `0.41 0.18` | `0.41 0.19` | `0.41 0.22` | `0.41 0.105` | `0.41 0.215` | `0.41 0.098` | `0.41 0.14` | `0.41 0.11` | `0.41 0.165` |
| 900 | `0.25 0.0015` | `0.37 0.14` | `0.37 0.15` | `0.37 0.175` | `0.37 0.082` | `0.37 0.165` | `0.37 0.078` | `0.37 0.108` | `0.37 0.085` | `0.37 0.128` |
| 950 | `0.18 0.001` | `0.18 0.1` | `0.18 0.108` | `0.18 0.125` | `0.18 0.058` | `0.18 0.118` | `0.18 0.055` | `0.18 0.075` | `0.18 0.06` | `0.18 0.09` |

### セマンティックトークン

UI では必ず以下のトークンを使う（`{prefix}-{token}`、例: `text-fg-base`, `bg-bg-subtle`, `border-border-mute`）。Light / Dark はそのモードでエイリアスするパレット段階。

#### Foreground（テキスト）

| Token | Light | Dark |
| --- | --- | --- |
| `fg-base` | gray-900 | gray-50 |
| `fg-subtle` | gray-600 | gray-400 |
| `fg-mute` | gray-700 | gray-300 |
| `fg-inverse` | gray-50 | gray-900 |
| `fg-info` | blue-800 | blue-200 |
| `fg-success` | green-800 | green-200 |
| `fg-warning` | yellow-800 | yellow-200 |
| `fg-error` | red-800 | red-200 |

#### Background（サーフェス）

| Token | Light | Dark |
| --- | --- | --- |
| `bg-base` | white | gray-800 |
| `bg-raised` | white | gray-800 |
| `bg-surface` | gray-50 | gray-950 |
| `bg-subtle` | gray-100 | gray-900 |
| `bg-mute` | gray-200 | gray-700 |
| `bg-emphasize` | gray-300 | gray-600 |
| `bg-inverse` | gray-900 | white |
| `bg-info` | blue-100 | blue-900 |
| `bg-success` | green-100 | green-900 |
| `bg-warning` | yellow-100 | yellow-900 |
| `bg-error` | red-100 | red-900 |

#### Border

| Token | Light | Dark |
| --- | --- | --- |
| `border-base` | gray-400 | gray-600 |
| `border-subtle` | gray-100 | gray-700 |
| `border-mute` | gray-200 | gray-600 |
| `border-emphasize` | gray-500 | gray-500 |
| `border-inverse` | gray-700 | gray-300 |
| `border-info` | blue-500 | blue-400 |
| `border-success` | green-500 | green-400 |
| `border-warning` | yellow-500 | yellow-400 |
| `border-error` | red-500 | red-400 |

#### Primary（Teal）

| Token | Light | Dark |
| --- | --- | --- |
| `primary-fg` | teal-800 | teal-300 |
| `primary-bg` | teal-200 | teal-800 |
| `primary-bg-subtle` | teal-50 | teal-950 |
| `primary-bg-mute` | teal-100 | teal-900 |
| `primary-bg-emphasize` | teal-300 | teal-700 |
| `primary-border` | teal-500 | teal-500 |

#### Secondary（Cyan）

| Token | Light | Dark |
| --- | --- | --- |
| `secondary-fg` | cyan-800 | cyan-300 |
| `secondary-bg` | cyan-200 | cyan-800 |
| `secondary-bg-subtle` | cyan-50 | cyan-950 |
| `secondary-bg-mute` | cyan-100 | cyan-900 |
| `secondary-bg-emphasize` | cyan-300 | cyan-700 |
| `secondary-border` | cyan-500 | cyan-500 |

#### Group（データ可視化）

| Token | Light | Dark |
| --- | --- | --- |
| `group-primary` | teal-800 | teal-200 |
| `group-secondary` | cyan-800 | cyan-200 |
| `group-tertiary` | pink-800 | pink-200 |
| `group-quaternary` | purple-800 | purple-200 |

その他: `back-drop`（`rgb(0, 0, 0, 0.5)` オーバーレイ）, `transparent`。

### ダークモード

クラスベース（`html` に `.dark`）。すべてのセマンティックトークンが自動で再マッピングされるため、トークン利用時に `dark:` プレフィックスは不要。ダークモードは「ライトの反転」ではなく独立したトーンで設計する。

### やってはいけないこと

- グラデーション背景（`bg-gradient-to-*`）
- 生のパレット色を直接使う（`bg-teal-500`）— セマンティックトークンを使う
- 透明度で状態表現（`/90`, `/80`）— 専用トークンを使う
- ホバーに `bg-primary-bg` — `bg-bg-mute` を使う
- 鮮やかな色を広範囲に使う（アクセントは小面積で）

## タイポグラフィ

### フォントファミリー

```css
font-family: 'Noto Sans JP', 'M PLUS 2', sans-serif;
```

トークン: `--font-noto-sans-jp`, `--font-m-plus-2`。日本語テキストが主体のため和文フォントを優先する。**Inter / Roboto / Open Sans は使わない。**

### サイズスケール

| Token | size | line-height |
| --- | --- | --- |
| `text-xs` | 0.75rem | 1.333333 |
| `text-sm` | 0.875rem | 1.428571 |
| `text-md` | 1rem | 1.5 |
| `text-lg` | 1.125rem | 1.555556 |
| `text-xl` | 1.25rem | 1.4 |
| `text-2xl` | 1.5rem | 1.333333 |
| `text-3xl` | 1.875rem | 1.2 |
| `text-emphasize` | 3rem | 1 |
| `text-highlight` | 6rem | 1 |

### ウェイト

| Token | 値 |
| --- | --- |
| `font-medium` | 450 |
| `font-bold` | 700 |

`font-normal` (400) も使用。`font-semibold` (600) / `font-extrabold` (800) は使わない。`font-medium` は **450**（一般の 500 より軽い）。1 画面で 3 種類を超えて使わない。

### 行間 / 字間

| leading | 値 |
| --- | --- |
| `leading-none` | 1 |
| `leading-tight` | 1.25 |
| `leading-snug` | 1.375 |
| `leading-normal` | 1.5 |
| `leading-relaxed` | 1.625 |
| `leading-loose` | 2 |

| tracking | 値 |
| --- | --- |
| `tracking-none` | 0em |
| `tracking-normal` | 0.025em |

本文は `leading-relaxed` 推奨。日本語に `uppercase` / `tracking-widest` は使わない。テキストにグラデーションをかけない。

## スペーシング・レイアウト

4px ベース（`--spacing: 0.25rem`）。Tailwind 標準スケールを使用。

### パディング・余白の目安

| step | rem | px | 用途 |
| --- | --- | --- | --- |
| 1 | 0.25rem | 4px | 最小単位 |
| 2 | 0.5rem | 8px | 近い要素（`mt-2`） |
| 4 | 1rem | 16px | 標準余白 / コンパクトな padding |
| 6 | 1.5rem | 24px | 標準 padding（`p-6`） |
| 8 | 2rem | 32px | ゆったり padding（`p-8`）/ セクション間 |
| 10 | 2.5rem | 40px | 大きなカード内（`p-10`） |
| 12 | 3rem | 48px | ページレベルの区切り（`mt-12`） |

余白の差で関連度を表す（近い `mt-2` / 標準 `mt-4` / セクション間 `mt-8` / ページ間 `mt-12`）。カード間は `gap-6`、縦セクションは `gap-8`〜`gap-10`。

### ブレークポイント

| Token | 値 |
| --- | --- |
| `sm` | 40rem |
| `md` | 48rem |
| `lg` | 64rem |
| `xl` | 80rem |
| `2xl` | 96rem |

### ページ構造

ページ背景を `bg-bg-subtle`（薄いグレー）にし、コンテンツを白カード（`bg-bg-base`）で浮かせる。すべてをカードに入れない — 余白と `Separator` で十分なことが多い。カードのネスト（Card in Card）はしない。

カスタムユーティリティ: `grid-cols-auto-fill-*` / `grid-cols-auto-fit-*`（レスポンシブ列）, `writing-h` / `writing-v`（縦書き）, `z-overlay` / `z-modal` / `z-toast`。

## 角丸

| Token | 値 |
| --- | --- |
| `rounded-xs` | 0.125rem |
| `rounded-sm` | 0.375rem |
| `rounded-md` | 0.5rem |
| `rounded-lg` | 0.75rem |
| `rounded-xl` | 1rem |
| `rounded-2xl` | 1.25rem |

要素の性格で使い分ける（「触れるものは柔らかく、読むものは端正に」）:

| 用途 | 角丸 |
| --- | --- |
| Button / Avatar / IconButton / Badge / Progress | `rounded-full`（ピル型） |
| Input / Textarea / Select / Card / CheckboxCard / RadioCard | `rounded-xl` |
| Alert / Dialog / Modal | `rounded-lg` |
| Checkbox | `rounded-md` |

## エレベーション（シャドウ）

ふんわり柔らかい影で奥行きを表現する。`shadow-xl` 以上は使わない。

| Token | 値 |
| --- | --- |
| `shadow-2xs` | `0 1px rgb(0 0 0 / 0.05)` |
| `shadow-xs` | `0 1px 2px 0 rgb(0 0 0 / 0.05)` |
| `shadow-sm` | `0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)` |
| `shadow-md` | `0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)` |
| `shadow-lg` | `0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)` |
| `shadow-xl` | `0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)` |
| `shadow-2xl` | `0 25px 50px -12px rgb(0 0 0 / 0.25)` |

| inset | 値 |
| --- | --- |
| `inset-shadow-2xs` | `inset 0 1px rgb(0 0 0 / 0.05)` |
| `inset-shadow-xs` | `inset 0 1px 1px rgb(0 0 0 / 0.05)` |
| `inset-shadow-sm` | `inset 0 2px 4px rgb(0 0 0 / 0.05)` |

利用指針: Card（`variant="shadow"`）= `shadow-sm` / Modal・Dialog・Tooltip・Dropdown・ListBox = `shadow-md` / Button = なし / Card（`variant="outline"`）= `border border-border-mute`。

## モーション

「静かな変化」を原則とする。

| タイミング | 用途 |
| --- | --- |
| 100ms | 即時フィードバック（ボタンプレス） |
| 150–200ms | 標準トランジション（ホバー、フォーカス） |
| 300ms | 開閉アニメーション（限度） |

- 基本は `transition-colors duration-150 ease-out`
- **300ms を超えない。bounce / spring 系のイージングは使わない。**
- `prefers-reduced-motion: reduce` を尊重（Popover アニメーション・motion ライブラリが自動対応）
- 組み込み: `ao-anim-scale`（`:popover-open` で 0.18s scale）/ `ao-anim-fade`（0.15s opacity）

### インタラクティブ状態

| 状態 | スタイル |
| --- | --- |
| Hover | `hover:bg-bg-mute` |
| Focus | `focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-border-info` |
| Active | `active:bg-bg-emphasize` |
| Disabled | `opacity-50 cursor-not-allowed` |
| Selected | `bg-primary-bg-subtle` |
| Error | `border-border-error` + `text-fg-error` |

フォーカスは必ず `focus-visible`（`focus` ではない）を使い、リングは `ring-border-info` で統一。

## z-index

| Token | 値 |
| --- | --- |
| `z-overlay` | 1000 |
| `z-modal` | 1300 |
| `z-toast` | 1500 |

## コンポーネント

`@k8ordo/ui` から名前付きエクスポート。スタイルシートとプロバイダーが必要。
このガイドのトークンを自分のマークアップで使うには Tailwind ソース版の `tailwind.css` を
import する（Tailwind を持たないプロジェクトはビルド済みの `styles.css`）:

```tsx
import '@k8ordo/ui/tailwind.css';
import { UIProvider, Button, Card } from '@k8ordo/ui';

<UIProvider>
  <App />
</UIProvider>;
```

### Buttons

- **Button** — `size: 'sm'|'md'|'lg'`, `color: 'primary'|'gray'`, `variant: 'contained'|'outlined'|'skeleton'`, `fullWidth`, `startIcon`, `endIcon`, `disabled`
- **IconButton** — `label`（必須・aria-label）, `bg: 'transparent'|'base'|'primary'`, `size`
- **LinkButton** / **IconLink** — Button / IconButton のリンク版。`href`, `openInNewTab?`, `renderAnchor?`

### Data display

- **Accordion**（compound: `Root` / `Item`(`defaultOpen?`) / `Button` / `Panel`）
- **Avatar** — `src` / `name`（イニシャル）/ `fallback`, `size`
- **Badge** — `label`, `tone: 'neutral'|'info'|'success'|'warning'|'error'`, `variant: 'solid'|'outline'`, `size`, `interactive`
- **Card** — `width: 'full'|'fit'`, `variant: 'shadow'|'outline'`, `interactive`
- **Code** — `children: string`（コードブロック）
- **Heading** — `level: 'h1'..'h6'`（必須）, `id?`, `lineClamp?`
- **Table**（compound: `Root` / `Caption` / `Head` / `Body` / `Row` / `HeaderCell` / `Cell` / `EmptyState`）

### Feedback

- **Alert** — `tone: 'info'|'success'|'warning'|'error'`, `message: string | string[]`
- **Progress** — `value`, `max`（必須）, `min?`, `label?`
- **Skeleton** — `shape: 'rect'|'circle'`, `size`, `animate`
- **Spinner** — `size`, `label?`（aria-live）
- **Toast** — `ToastProvider` + `useToast()`（`open(tone, message, options?)` / `close(id)` / `closeAll()`）

### Form

ラベルは入力の上に配置。エラーは入力直下に `text-fg-error text-sm`。バリデーションは送信時が基本。

- **FormControl** — フィールドのラッパー。`label`（必須）, `helpText?`, `errorText?`, `required?`, `renderInput`
- **TextField** / **Textarea** / **PasswordInput** / **NumberField** — テキスト系入力
- **Select** / **Autocomplete** — `options` ベースの選択
- **Checkbox** / **CheckboxGroup** / **CheckboxCard** — 複数選択
- **Radio** / **RadioCard** — 単一選択（`options` ベース）
- **Switch** — `label`（必須）, controlled `checked: boolean`
- **Slider** — `min`/`max`/`step`, controlled `value`
- **FileField**（compound: `Root` / `Trigger` / `ItemList`）
- **Form** — `<form>` ラッパー

状態 prop は `disabled` / `invalid` / `required`、controlled は `value`（Checkbox / Switch は `checked`）+ `onChange`、uncontrolled は `defaultValue` / `defaultChecked`。

### Layout

- **Stack** — フレックスレイアウト
- **Grid** — グリッドレイアウト
- **Separator** — `color: 'base'|'mute'|'subtle'`, `orientation: 'horizontal'|'vertical'`
- **ScrollLinked** — スクロール進捗バー（`container?`）

### Navigation

- **Anchor** — テキストリンク。外部リンクに自動で新規タブアイコン。`href`, `openInNewTab?`, `renderAnchor?`
- **Breadcrumb**（compound: `List` / `Item` / `Link`(`current?`) / `Separator`）
- **Pagination** — ページネーション
- **Tabs**（compound: `Root`(`ids` / `defaultSelectedId?`) / `List` / `Tab` / `Panel`）

### Overlays

- **Modal** — `isOpen?`/`onClose?`/`defaultOpen?`, `side: 'center'|'bottom'|'right'|'left'`（`ModalSide`）
- **Dialog**（compound: `Root` / `Header` / `Content`）
- **Drawer** — `title`, `isOpen`, `onClose`, `side: 'left'|'right'`（`DrawerSide`。Modal の `side` の部分集合）
- **Popover**（compound: `Root` / `Trigger` / `Content`、`placement`, `type`）
- **Tooltip**（compound: `Root` / `Trigger` / `Content`、`placement`）
- **DropdownMenu**（compound: `Root`(`placement`) / `Trigger`(`label`) / `IconTrigger`(`icon`, `label`) / `Content` / `Item`(`label`)）
- **ListBox**（compound: `Root` / `Trigger` / `Content`、`options` / `value` / `onSelect`）

### Icons

すべてのアイコンが `size` prop を受け取る（`'xs'|'sm'|'md'|'lg'|'xl'|'2xl'|'3xl'`、デフォルト `md`）。`xs`=12px, `sm`=16px, `md`=24px, `lg`=32px, `xl`=40px, `2xl`=48px, `3xl`=56px。特殊: `ChevronIcon`（`direction` 必須）, `AlertIcon`（`status` 必須）。

### Providers

- **UIProvider** — アプリルートで 1 回
- **PortalRootProvider** / **usePortalRoot** — ポータルのルート指定

## ボイス

- 簡潔で端正な日本語。誇張やマーケティング的な装飾を避ける
- 状態・操作は明確に（「保存する」「キャンセル」のように動作を示す）
- 色だけに頼らず、アイコンやテキストを併用して状態を伝える

## インストール

```bash
npm install @k8ordo/ui
```

```tsx
// Tailwind CSS 4 のプロジェクト（トークンを自分のマークアップでも使える）
import '@k8ordo/ui/tailwind.css';
// Tailwind を持たないプロジェクトはビルド済み CSS を使う
// import '@k8ordo/ui/styles.css';
import { UIProvider } from '@k8ordo/ui';
```

- Docs: <https://ordo.k8o.me>
- npm: `@k8ordo/ui`
