PackagesUIArchitecture
Primitives
スタイルの定義と管理を担う最小構成単位。
責務
Primitivesは 「どう見えるか」(what)を定義する 最小構成単位である。
- HTMLネイティブ要素にデザイントークンベースのスタイルを適用する
- CVAでvariant(見た目のバリエーション)を定義・管理する
- CSS擬似クラス(
:hover,:focus-visible,:disabled等)による見た目の変化を定義する - Radix UI Slotによる
asChildパターンで、レンダリングする要素の差し替えを可能にする classNameを受け取り、利用者側でのスタイル拡張を許容する
Primitivesが決めること
| 対象 | 例 |
|---|---|
| 色・背景色 | variant="destructive" のときの bg-destructive |
| サイズ | size="sm" のときの h-8 px-3 text-sm |
| インタラクション時の見た目 | hover時の色変化、focus時のリング、disabled時の透明度 |
| 角丸・シャドウ | rounded-lg, shadow-md |
| 内部レイアウト | inline-flex items-center gap-2 |
Primitivesが決めないこと
| 対象 | 決める層 |
|---|---|
いつ disabled にするか | Elements |
いつ variant="destructive" を使うか | Elements |
| どのPrimitivesを組み合わせるか | Elements |
| 外部レイアウト(margin、配置) | 利用者(Elements / アプリケーション) |
責務外
Primitivesが行ってはならないことを定義する。
- ビジネスロジックや状態管理を持たない — ローディング状態の制御やバリデーションはElementsの責務である
- 他のPrimitivesを内部で組み合わせない — Primitivesは単一のHTML要素(またはRadixプリミティブ)に対応する
'use client'を宣言しない — Primitives自体はサーバーコンポーネントとして使用可能な状態を保つ- 用途を限定する命名をしない —
SubmitButtonではなくButtonのように、汎用的な名前にする - 外部にエクスポートしない — アプリケーションコードから直接使用させない
状態の視覚定義
すべてのインタラクティブなPrimitivesは、以下の状態に対する見た目を定義しなければならない。状態の見た目はCSS擬似クラスまたはCVAで実装し、Elementsに判断を委ねない。
| 状態 | 実装方法 | 視覚表現の例 |
|---|---|---|
| default | CVAベーススタイル | 通常の見た目 |
| hover | hover: | 背景色を一段濃くする |
| active / pressed | active: | さらに濃くする、またはわずかに縮小する |
| focus | focus-visible: | フォーカスリング(ring-ring)を表示する |
| disabled | disabled: / aria-disabled: | 透明度を下げる(opacity-50)+ pointer-events-none |
| error | aria-invalid: | ボーダーを border-destructive に変更する |
loading 状態の見た目(Spinner表示等)はElementsの責務である。Primitivesは disabled の見た目だけを定義し、Elementsが isLoading 時に disabled を渡すことでローディング中の見た目を実現する。
// Primitiveのベーススタイル例(Button)
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-lg transition-colors ' +
'focus-visible:ring-3 focus-visible:ring-ring ' +
'disabled:pointer-events-none disabled:opacity-50 ' +
'aria-invalid:border-destructive aria-invalid:ring-destructive/20',
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground ' +
'hover:bg-primary-hover active:bg-primary-active',
// ...
},
},
},
);要件
| 要件 | 詳細 |
|---|---|
| CVA variant定義 | 見た目のバリエーションはすべてCVAで管理する |
| 状態の視覚定義 | インタラクティブ要素は default / hover / active / focus / disabled / error の見た目を定義する |
| asChild対応 | Radix UI Slotを使用し、要素の差し替えを可能にする |
| data属性の付与 | data-slot, data-variant, data-size を付与する |
| Props拡張 | React.ComponentProps<"element"> を拡張する |
| className合成 | cn() で内部クラスと外部classNameをマージする |
| フォーカス管理 | focus-visible でフォーカスリングを表示する |
| 外部余白の禁止 | margin を持たない。内部余白(padding, gap)のみ定義する |