# 機能設計書 43-useCssVars()

## 概要

本ドキュメントは、Vue 3のSFCにおいてJavaScriptのリアクティブなデータをCSSカスタムプロパティ（CSS変数）としてDOMにバインドするランタイムヘルパー `useCssVars()` の機能設計を記述する。

### 本機能の処理概要

`useCssVars()` は、SFCの `<style>` ブロック内で `v-bind()` を使用した場合にSFCコンパイラが自動的に挿入するランタイムヘルパー関数である。リアクティブなJavaScript変数の値をCSS変数として要素のstyleプロパティに設定し、値が変更された際に自動的にDOMを更新する。

**業務上の目的・背景**：CSSの値をJavaScriptの状態と動的に連動させたい場合（テーマカラーの切り替え、レスポンシブなスタイル制御など）に、宣言的かつリアクティブな方法でCSSとJSを橋渡しする必要がある。このフックはSFCの `v-bind()` CSS機能の実行時基盤として機能する。

**機能の利用シーン**：SFCの `<style>` ブロック内で `color: v-bind(themeColor)` のようにJavaScriptの変数を参照するCSS記法を使用する場合に、コンパイラが自動的にこのヘルパーを挿入する。開発者が直接呼び出すことは通常ない。

**主要な処理内容**：
1. getter関数からCSS変数のキーと値のマッピングを生成
2. コンポーネントのVNodeツリーを走査してDOM要素にCSS変数を設定
3. Teleportされた要素にもCSS変数を伝播
4. カスタム要素の場合はホスト要素に直接設定
5. MutationObserverでDOM変更を監視し、変更時にCSS変数を再設定

**関連システム・外部連携**：@vue/compiler-sfcによるSFCコンパイル時のヘルパー挿入、DOM API（MutationObserver, style.setProperty）

**権限による制御**：権限制御は存在しない。ブラウザ環境でのみ動作する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに該当なし |

## 機能種別

DOMスタイル操作 / リアクティブバインディング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| getter | (ctx: any) => Record<string, unknown> | Yes | コンポーネントプロキシからCSS変数マッピングを生成する関数 | なし |

### 入力データソース

- コンポーネントインスタンスのproxy（リアクティブコンテキスト）
- instance.subTree（VNodeツリー）
- instance.ce（カスタム要素参照、存在する場合）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| CSS変数 | --{key}: {value} | DOMのstyleプロパティに設定されるCSS変数 |

### 出力先

コンポーネントが管理するDOM要素のstyleプロパティ。Teleportされた要素を含む。

## 処理フロー

### 処理シーケンス

```
1. ブラウザ環境チェック
   └─ __BROWSER__ || __TEST__ でなければ即座にreturn
2. getCurrentInstance() でコンポーネントインスタンスを取得
   └─ インスタンスが存在しない場合: DEVモードで警告 → return
3. updateTeleports 関数を instance.ut に設定
   └─ Teleport先のDOM要素にCSS変数を設定する関数
4. setVars 関数を定義
   └─ getter(instance.proxy) でCSS変数マッピングを取得
   └─ カスタム要素: instance.ce に直接設定
   └─ 通常要素: VNodeツリーを走査して設定
   └─ updateTeleports を呼び出し
5. onBeforeUpdate ライフサイクルフックを登録
   └─ queuePostFlushCb(setVars) で更新後にCSS変数を再設定
6. onMounted ライフサイクルフックを登録
   └─ watch(setVars, NOOP, { flush: 'post' }) でリアクティブ監視
   └─ MutationObserver で DOM変更を監視
   └─ onUnmounted で MutationObserver を切断
```

### フローチャート

```mermaid
flowchart TD
    A[useCssVars 呼び出し] --> B{ブラウザ環境?}
    B -->|No| C[return]
    B -->|Yes| D[getCurrentInstance]
    D --> E{instance存在?}
    E -->|No| F[DEV警告 + return]
    E -->|Yes| G[updateTeleports関数をinstance.utに設定]
    G --> H[setVars関数を定義]
    H --> I[onBeforeUpdate登録]
    I --> J[onMounted登録]
    J --> K[watch + MutationObserver設定]

    subgraph setVars実行時
    L[getter実行] --> M{カスタム要素?}
    M -->|Yes| N[ホスト要素にCSS変数設定]
    M -->|No| O[VNodeツリー走査]
    O --> P[各DOM要素にCSS変数設定]
    N --> Q[updateTeleports実行]
    P --> Q
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | ブラウザ限定 | ブラウザ環境（__BROWSER__ \|\| __TEST__）でのみ動作 | 常時 |
| BR-43-02 | CSS変数プレフィックス | CSS変数名は自動的に`--`プレフィックスが付与される | 常時 |
| BR-43-03 | VNodeツリー走査 | HOC（Higher-Order Component）を貫通してルート要素を探索 | 通常コンポーネント |
| BR-43-04 | Suspense対応 | Suspense内の場合はactiveBranchを参照 | __FEATURE_SUSPENSE__有効時 |
| BR-43-05 | Fragment対応 | FragmentタイプのVNodeは子ノードを再帰的に走査 | VNodeがFragment型の場合 |
| BR-43-06 | Static対応 | StaticタイプのVNodeはelからanchorまでの全兄弟ノードに設定 | VNodeがStatic型の場合 |

### 計算ロジック

CSS変数の値は `normalizeCssVarValue()` を通じて正規化される。設定は `style.setProperty(`--${key}`, value)` で行われる。

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

データベース操作は存在しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告（DEVのみ） | コンポーネントインスタンス外で呼び出した場合 | コンポーネントのsetup()内で使用すること |

### リトライ仕様

リトライ仕様は存在しない。MutationObserverによりDOM変更時に自動的に再適用される。

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

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

## パフォーマンス要件

- VNodeツリーの走査があるため、深いネスト構造では走査コストが発生する
- onBeforeUpdateでqueuePostFlushCbを使用することで、同一更新サイクル内での不要な再適用を防止
- watchのflush: 'post'オプションにより、DOM更新完了後にCSS変数を設定

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

- CSS変数の値はnormalizeCssVarValueで正規化されるが、CSSインジェクションのリスクに注意
- ユーザー入力値を直接CSS変数に設定する場合は適切なサニタイズが必要

## 備考

- このフックは通常、開発者が直接呼び出すものではなく、SFCコンパイラが `v-bind()` CSS構文を処理する際に自動的にコードに挿入する
- `CSS_VAR_TEXT` シンボルはCSS変数テキストのキャッシュに使用される
- DEVモードでは `instance.getCssVars` にgetter関数が設定され、開発ツールからの参照に使用される
- Teleportされた要素は `[data-v-owner="${instance.uid}"]` セレクタで検索される

---

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

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

### 推奨読解順序

#### Step 1: データ構造を理解する

CSS変数の設定に関わるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | CSS_VAR_TEXTシンボル（15行目）: CSS変数テキストのキャッシュキー |
| 1-2 | useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | getter関数の型定義（21行目）: (ctx: any) => Record<string, unknown> |

**読解のコツ**: この実装はVNodeツリー構造の理解が前提となる。VNodeのshapeFlag（ShapeFlags）によってELEMENT、COMPONENT、SUSPENSE等の判定が行われる。

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

useCssVars()のメイン処理フローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | useCssVars関数（20-67行目）: ブラウザチェック、インスタンス取得、ライフサイクルフック登録 |

**主要処理フロー**:
1. **23行目**: `!__BROWSER__ && !__TEST__`の場合はearly return
2. **25-31行目**: getCurrentInstance()でインスタンスを取得、なければ警告
3. **34-38行目**: updateTeleports関数の定義（instance.utに設定）。Teleport先DOMの更新
4. **44-52行目**: setVars関数の定義。getter実行後、カスタム要素かVNodeツリーかで分岐
5. **56-58行目**: onBeforeUpdateでqueuePostFlushCb(setVars)を登録
6. **60-66行目**: onMountedでwatch + MutationObserver設定

#### Step 3: VNodeツリー走査を理解する

setVarsOnVNode関数の再帰走査ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | setVarsOnVNode関数（69-97行目）: VNodeの種別に応じた走査 |
| 3-2 | useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | setVarsOnNode関数（99-110行目）: DOM要素へのCSS変数設定 |

**主要処理フロー**:
- **70-78行目**: Suspense対応 - activeBranchを参照、pendingBranchがあればeffectsに追加
- **81-83行目**: コンポーネントVNodeの場合、subTreeを再帰的に走査
- **85-86行目**: ELEMENT型の場合、setVarsOnNodeでCSS変数を設定
- **87-88行目**: Fragment型の場合、子ノードを再帰走査
- **89-96行目**: Static型の場合、elからanchorまでの全兄弟ノードに設定
- **99-109行目**: nodeType === 1（Element）の場合のみ、style.setPropertyでCSS変数を設定

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

```
useCssVars(getter)
    │
    ├─ getCurrentInstance()
    │
    ├─ instance.ut = updateTeleports (Teleport先DOM更新)
    │      └─ document.querySelectorAll(`[data-v-owner="${uid}"]`)
    │             └─ setVarsOnNode(node, vars)
    │
    ├─ setVars()
    │      ├─ getter(instance.proxy) → vars
    │      ├─ [カスタム要素] setVarsOnNode(instance.ce, vars)
    │      └─ [通常] setVarsOnVNode(instance.subTree, vars)
    │             ├─ [Suspense] activeBranch → 再帰
    │             ├─ [Component] subTree → 再帰
    │             ├─ [Element] setVarsOnNode(el, vars)
    │             ├─ [Fragment] children → 再帰
    │             └─ [Static] el..anchor → setVarsOnNode
    │
    ├─ onBeforeUpdate → queuePostFlushCb(setVars)
    │
    └─ onMounted
           ├─ watch(setVars, NOOP, { flush: 'post' })
           ├─ new MutationObserver(setVars)
           └─ onUnmounted → ob.disconnect()
```

### データフロー図

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

getter(proxy)          ──▶ Record<string, unknown>
                             │
                             ▼
normalizeCssVarValue   ──▶ 値の正規化
                             │
                             ▼
setVarsOnNode          ──▶ style.setProperty(`--${key}`, value) ──▶ DOM style属性
                             │
                             ▼
CSS_VAR_TEXT           ──▶ cssTextキャッシュ保存
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| useCssVars.ts | `packages/runtime-dom/src/helpers/useCssVars.ts` | ソース | useCssVars()の実装（110行） |
| index.ts | `packages/runtime-dom/src/index.ts` | ソース | useCssVarsのエクスポート |
