# 機能設計書 15-カスタムバリアント定義（@custom-variant）

## 概要

本ドキュメントは、`@custom-variant` ディレクティブによるカスタムバリアント定義機能の設計について記述する。セレクタベースまたは `@slot` ベースのカスタムバリアントを定義できる。

### 本機能の処理概要

`@custom-variant` ディレクティブは、ユーザが独自のバリアント（条件付きスタイル修飾子）をCSS内で定義するための仕組みである。2つの定義形式をサポートする。

**業務上の目的・背景**：Tailwind CSSの組み込みバリアント（hover, focus, responsive等）では対応できない独自の条件付きスタイルを定義する必要がある。例えば、特定のクラスが親要素に存在する場合にのみ適用されるスタイルや、カスタムメディアクエリに基づくスタイルなどが該当する。`@custom-variant` はこのニーズにCSS内で対応する。

**機能の利用シーン**：カスタムのセレクタ条件でバリアントを定義する場合（例: `@custom-variant hocus (&:hover, &:focus)`）、複雑なネスト構造のバリアントを定義する場合（`@slot` ベース）、レガシーの `@variant` ディレクティブとの互換処理を行う場合に使用される。

**主要な処理内容**：
1. `@custom-variant` ディレクティブの検出とバリデーション
2. セレクタベース定義: `@custom-variant name (selector1, selector2)` 形式の処理
3. ボディベース定義: `@custom-variant name { @slot; }` 形式の処理
4. レガシー `@variant` からの `@custom-variant` への変換
5. カスタムバリアント間の依存関係解決（トポロジカルソート）
6. Variants レジストリへの登録

**関連システム・外部連携**：Variants（variants.ts）に登録される。`@variant` ディレクティブとの互換処理を含む。トポロジカルソート（topological-sort.ts）で依存関係を解決する。

**権限による制御**：権限による制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに直接の関連画面なし。全画面で@custom-variantで定義されたカスタムバリアントが利用可能 |

## 機能種別

定義処理 / CSS変換処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string | Yes | バリアント名 | IS_VALID_VARIANT_NAMEに一致する必要がある |
| selector | string | セレクタベース時Yes | カンマ区切りのセレクタリスト `(selector1, selector2)` | 括弧で囲まれ、空セレクタ不可 |
| node.nodes | AstNode[] | ボディベース時Yes | @slot を含むバリアントのボディ | セレクタと同時指定不可 |

### 入力データソース

- CSSファイル内の `@custom-variant` ディレクティブ（パース済みAST）
- レガシー `@variant` ディレクティブからの自動変換

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| registerFn | (designSystem: DesignSystem) => void | Variants レジストリへの登録関数 |
| dependencies | Set<string> | 依存する他のバリアント名の集合 |

### 出力先

- DesignSystem.variants に登録される
- `@custom-variant` ノード自体はASTから除去される

## 処理フロー

### 処理シーケンス

```
1. @custom-variant ディレクティブの検出
   ├─ ネストチェック（トップレベルのみ許可）
   ├─ 名前のバリデーション（IS_VALID_VARIANT_NAME）
   └─ セレクタとボディの同時指定チェック

2. セレクタベース定義の処理
   ├─ セレクタリストのパース（カンマ区切り）
   ├─ セレクタをatRuleとstyleRuleに分類
   └─ variants.static() で登録関数を生成

3. ボディベース定義の処理
   ├─ ボディ内の @variant 依存関係を検出
   └─ variants.fromAst() で登録関数を生成

4. 依存関係の解決
   ├─ customVariantDependencies に依存関係を記録
   └─ トポロジカルソートで登録順序を決定

5. Variants への登録と @custom-variant ノードの除去
```

### フローチャート

```mermaid
flowchart TD
    A["@custom-variant 検出"] --> B{ネストされている?}
    B -->|Yes| C["エラー: cannot be nested"]
    B -->|No| D{名前が有効?}
    D -->|No| E["エラー: invalid variant name"]
    D -->|Yes| F{セレクタとボディの両方あり?}
    F -->|Yes| G["エラー: cannot have both"]
    F -->|No| H{ボディなし?}
    H -->|Yes| I{セレクタあり?}
    I -->|No| J["エラー: no selector or body"]
    I -->|Yes| K["セレクタベース処理"]
    H -->|No| L["ボディベース処理（@slot）"]
    K --> M["customVariants に登録関数を追加"]
    L --> M
    M --> N["@custom-variant ノード除去"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-15-01 | トップレベル制約 | `@custom-variant` はCSSのトップレベルにのみ配置可能 | 常時 |
| BR-15-02 | 排他的定義 | セレクタとボディの同時指定は不可 | 常時 |
| BR-15-03 | セレクタ必須 | ボディなしの場合はセレクタが必須 | ボディが空の場合 |
| BR-15-04 | 空セレクタ禁止 | セレクタリスト内に空のセレクタは不可 | セレクタベース定義時 |
| BR-15-05 | @variant互換 | レガシーの `@variant` がトップレベルで `@slot` を含む場合、`@custom-variant` に変換される | レガシーCSS処理時 |
| BR-15-06 | 依存関係解決 | ボディ内の `@variant` ディレクティブは依存関係として記録され、トポロジカルソートで登録順序が決定される | ボディベース定義時 |

### 計算ロジック

セレクタの分類ロジック: セレクタが `@` で始まる場合はatRuleパラメータとして扱い、それ以外はstyleRuleセレクタとして扱う。compoundsForSelectors 関数で合成可能性が判定される。

## データベース操作仕様

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | Error | `@custom-variant` がネストされている場合 | エラーメッセージをスロー |
| - | Error | 名前がIS_VALID_VARIANT_NAMEに不一致 | `defines an invalid variant name` をスロー |
| - | Error | セレクタとボディの両方が指定された場合 | `cannot have both a selector and a body` をスロー |
| - | Error | セレクタもボディもない場合 | `has no selector or body` をスロー |
| - | Error | セレクタリストが不正な場合 | `selector is invalid` をスロー |

### リトライ仕様

リトライ処理は存在しない。

## トランザクション仕様

トランザクション管理は存在しない。

## パフォーマンス要件

コンパイル時に1回のみ実行される。カスタムバリアント数は通常少数のため性能への影響は軽微。

## セキュリティ考慮事項

CSS仕様の範囲内で処理される。

## 備考

- `@variant` ディレクティブは後方互換性のために自動的に `@custom-variant` に変換される場合がある（トップレベルで `@slot` 使用時、またはボディなし時）
- カスタムバリアント間の依存関係はトポロジカルソートで解決される

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: エントリーポイントを理解する

`@custom-variant` ディレクティブの検出処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/tailwindcss/src/index.ts` | 309-341行目: レガシー `@variant` から `@custom-variant` への変換ロジック |
| 1-2 | index.ts | `packages/tailwindcss/src/index.ts` | 350-448行目: `@custom-variant` の検出・バリデーション・登録 |

**主要処理フロー**:
1. **350-353行目**: `@custom-variant` ノードの検出とネストチェック
2. **356-361行目**: 名前のバリデーション
3. **364-366行目**: セレクタとボディの排他チェック
4. **368-415行目**: セレクタベース定義の処理
5. **433-445行目**: ボディベース定義の処理
6. **447-448行目**: ノードの除去

#### Step 2: セレクタベース定義を理解する

セレクタの分類と登録関数の生成を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/tailwindcss/src/index.ts` | 374-414行目: セレクタリストのパース、atRule/styleRule分類、variants.static()による登録 |

#### Step 3: ボディベース定義を理解する

`@slot` を使用したバリアント定義の仕組みを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/tailwindcss/src/index.ts` | 433-445行目: ボディ内の @variant 依存検出、variants.fromAst()による登録 |
| 3-2 | variants.ts | `packages/tailwindcss/src/variants.ts` | 86-109行目: fromAst()メソッド。substituteAtSlot によるノード置換 |

### プログラム呼び出し階層図

```
compile (index.ts)
    │
    ├─ walk(ast) で @variant / @custom-variant 検出
    │      ├─ @variant → @custom-variant 変換（309-341行目）
    │      └─ @custom-variant 処理（350-448行目）
    │             ├─ バリデーション
    │             ├─ セレクタベース → variants.static() 登録関数生成
    │             └─ ボディベース → variants.fromAst() 登録関数生成
    │
    ├─ customVariants Map に登録関数を格納
    ├─ customVariantDependencies Map に依存関係を格納
    │
    ├─ topologicalSort(customVariants, dependencies)
    │      └─ 登録順序の決定
    │
    └─ designSystem.variants.static() or .fromAst()
```

### データフロー図

```
[入力]                                    [処理]                      [出力]

@custom-variant hocus             ───▶ セレクタ分類          ───▶ variants.static() 登録
  (&:hover, &:focus);                   atRule/styleRule

@custom-variant name {            ───▶ variants.fromAst()   ───▶ variants.static() 登録
  &:hover { @slot; }                    @slot 置換
}

レガシー @variant                  ───▶ @custom-variant 変換  ───▶ 同上の処理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/tailwindcss/src/index.ts` | ソース | @custom-variant ディレクティブの検出・バリデーション・処理 |
| variants.ts | `packages/tailwindcss/src/variants.ts` | ソース | Variantsクラス。fromAst()メソッド、compoundsForSelectors関数 |
| topological-sort.ts | `packages/tailwindcss/src/utils/topological-sort.ts` | ソース | カスタムバリアントの依存関係解決 |
| ast.ts | `packages/tailwindcss/src/ast.ts` | ソース | styleRule, rule 関数。ASTノード生成 |
| segment.ts | `packages/tailwindcss/src/utils/segment.ts` | ソース | セレクタリストのカンマ分割 |
| walk.ts | `packages/tailwindcss/src/walk.ts` | ソース | ASTウォーカー。@slot/@variant の検出 |
