# 双框架（Vue + React）支持 — 路线 A Pilot 设计

> **状态**：✅ 已执行（2026-06-29，subagent-driven 全 8 task）。结论：路线 A 三组件验证通过、无需 route-B。Findings + 全量决策论证 → [`docs/internal/retrospection/dual-framework-react-pilot-findings-2026-06-29.md`](../../internal/retrospection/dual-framework-react-pilot-findings-2026-06-29.md)。
> **优先级约束**：本设计受 [`FIGMA_AS_SOURCE_OF_TRUTH.md`](../../FIGMA_AS_SOURCE_OF_TRUTH.md) 约束——React 端与 Vue 端一样，必须 1:1 还原 Figma，且只能用 deterministic render-verification 验证，不允许 AI 自由发挥。
> **关联**：[`PROJECT_GOAL.md`](../../PROJECT_GOAL.md)（一句话目标当前只承诺 Vue；本设计是把目标扩成 Vue+React 双框架的第一步探针）。

---

## 1. 背景与问题

### 1.1 触发

owner 发起 `/design-sync`（把设计系统同步到 claude.ai/design）时撞墙：claude.ai/design 的设计 agent 用 **React** 搭 UI，import 的组件必须是 React 组件；而 TVU 设计系统是**纯 Vue 3 库**（v0.10.1，26 组件）。design-sync 转换器产出的预览是 `.tsx`、强依赖 `@types/react`、全程 React 运行时——Vue 组件无法被其消费。

由此 owner 把目标升级为战略级决策：**让 TVU 设计系统同时支持 Vue + React，双一等公民**，既服务 AI 设计工具（claude.ai/design），也服务未来真实的 React 产品；后续 Figma 库更新时两端同步更新。

### 1.2 核心难题

如何让 Vue 和 React 两端都 1:1 还原 Figma，又**不至于永远维护两套会互相漂移的实现**。

关键事实：
- **tokens / CSS 变量 / 图标 SVG 本就框架无关**（`src/tokens/variables.css` 的 `:root` 自定义属性 + `dist/icons/`），两端天然共享。真正需要双份的只有"组件模板 + 逻辑"这一层。
- 组件样式全部走全局 CSS 自定义属性（`var(--brand)` 等）。**CSS 自定义属性会穿透 shadow DOM 边界继承**——因此即使把 Vue 组件编译成带 shadow DOM 的 custom element，`--token` 主题（含 light/dark）大概率仍然生效。这显著降低了路线 A 的主题风险。
- 真实导出 API 在 `src/canonical/*.vue`（如 Button→`canonical/ButtonBridge.vue`），不是 `src/components/` 原件。
- Vue 版本 **3.5.32**（已装），具备较新的 `defineCustomElement` 能力。

### 1.3 已存在但不够用的路径

STATUS ⑱/㉑ 记录的 "Claude Design 包同步"（`export:claude-design-bundle`）已能喂 claude.ai/design，但给的是**指导 + token**，不是可 import 的真实组件。本设计要补的正是"真实组件"那一层。

---

## 2. 方案选型（已决策）

| 路线 | 做法 | 取舍 | 结论 |
|---|---|---|---|
| **A** | Vue `defineCustomElement` 编译成 Web Component + 薄 React 包装 | ✅ 复用全部 Vue 成果，单一实现，Figma 验证单源；⚠️ custom-element 互操作怪癖（shadow DOM / slot / 复杂 props / v-model） | **选中（先 pilot）** |
| B | 平行手写原生 React 库，两端共用 Figma 验证闸 | ✅ React 最地道；⚠️ 永远维护两套，每次 Figma 改动双改 | **A 的 fallback**（A 扛不住的组件退到 B） |
| C | 重写成框架无关核心（Lit）+ Vue/React 双包装 | ✅ 最干净多框架单源；⚠️ 推倒重写 26 个成熟 Vue 组件 | **否决**（成本/风险最高，浪费现有投资） |

**决策**：先用 pilot 验证路线 A 的可行性（owner 拍板 2026-06-29），通过 ROI gate 后再决定是否全量推进；扛不住的组件退回路线 B。该"先实跑验证再押注"符合项目 `baseline 实跑前不信估值` / `BRIDGE-MOCKUP-007 ROI gate` 纪律。

---

## 3. Pilot 范围

### 3.1 选 3 个代表性 canonical 组件（精准打三大风险轴）

| 组件 | canonical 源 | 验证的风险轴 |
|---|---|---|
| **Button** | `src/canonical/ButtonBridge.vue` | 变体扫描 + 事件（`click`）——冒烟基线 |
| **Input** | `src/canonical/InputBoxLine.vue` | token 主题 + 交互态（focus / error / disabled）+ **v-model 双向绑定**（React × custom element 已知痛点） |
| **FormItem** | `src/canonical/FormItem.vue`（组合包 Input） | **slot / 组合互操作**——最难的一类 |

这三者覆盖：变体渲染、双向绑定/交互态/主题、slot 组合——pilot 的全部互操作面。

### 3.2 明确不做（YAGNI）

- 不做剩余 23 个组件。
- 不建生产级 React npm 发布流程（entry / exports / 版本 / changelog）——pilot 通过后单独设计。
- 不改 Figma、不引入新主题、不动已发布的 Vue 包与其构建/发布管线。

---

## 4. 架构与组成

### 4.1 单元划分

四个职责清晰、可独立理解/测试的单元：

1. **Web Component 构建入口**（新增，独立于主 lib build）
   - 用 Vue 3.5 `defineCustomElement` 把 3 个 canonical 组件编译成原生自定义元素。
   - 输入：3 个 canonical `.vue` + 共享 tokens CSS。
   - 输出：一个独立 bundle，注册 `<tvu-button>` / `<tvu-input>` / `<tvu-form-item>`（命名待 spec review 时定）。
   - 依赖：Vue 运行时、`tokens/variables.css`。
   - 关键工程问题（pilot 要解）：组件自身 `<style>` 是否随 custom element 正确注入；shadow DOM 模式（默认开 vs light-DOM）对全局 token 主题的实际影响。

2. **React 包装层**（新增）
   - 每个组件一个薄 React 组件，渲染对应 custom element，映射 props / events / v-model / slot 成地道 React API。
   - 输入：custom element + 手写/生成的 props 类型。
   - 输出：可 `import { Button } from '<react-pkg>'` 的 React 组件 + `.d.ts`。
   - 依赖：React 19、Web Component 构建入口的产物。
   - 关键工程问题：v-model（`modelValue` + `update:modelValue`）→ React 受控 `value`/`onChange`；事件 → React `on*` props；slot → React `children` / 命名 slot。

3. **render-verification 扩展**（改现有）
   - 把现有 manifest-based render verification（`figma-data/render-verification-manifest.json` + `pnpm test:render-verification` + `audit:render-drift-gate`）扩到也对这 3 个组件的 **React 渲染**跑同一套 getComputedStyle vs Figma 期望值比较。
   - 输入：现有 manifest 中这 3 个组件的节点条目（已有，Vue 在用）。
   - 输出：React 端 per-variant PASS/FAIL（与 Vue 同口径）。
   - 依赖：render harness 路由需能渲染 React 实例（pilot 决定是复用 docs site 路由还是独立 harness）。

4. **双框架展示页**（改 playground）
   - 在 `playground/` 加一个 pilot 页，3 个组件 **Vue / React 双栏对照**渲染。
   - 这是双框架文档站的第一步雏形，也是人工 review 面。

### 4.2 数据流

```
Figma 库（真源）
   │  pnpm sync:figma-library --with-extract（已有，不改）
   ▼
figma-data/ + tokens/variables.css（框架无关，两端共享）
   │
   ├─► [现有] canonical/*.vue ──► 主 lib build ──► Vue npm 包（不动）
   │
   └─► [新增] canonical/*.vue ──► defineCustomElement ──► Web Component bundle
                                          │
                                          └─► React 包装层 ──► React 组件 + .d.ts
                                                   │
                              ┌────────────────────┼────────────────────┐
                              ▼                     ▼                    ▼
                    render-verification     playground 双栏       claude.ai/design
                    （React 同闸验证）        （Vue/React 对照）     （design-sync 消费）
```

要点：**两端的视觉真源都是同一份 Figma 抽取 + 同一份 tokens**，差异只在渲染层；同一套 deterministic 闸同时把关两端，漂移会被机检抓住。

---

## 5. Pilot 通过判据（ROI gate）

3 个组件**全部**满足下列全部条件，才判定路线 A 成立、可考虑全量：

1. **主题**：全局 `--token`（含 light / dark）在 React 渲染里可见生效，与 Vue 端视觉一致。
2. **保真**：React 渲染通过现有 render-verification 闸——getComputedStyle == Figma 期望值（5% 容差，同现有口径），走与 Vue 相同的 manifest 节点。
3. **互操作**：v-model（Input 值受控）、事件（Button `click` / Input `input`）、slot（FormItem 包 Input 组合）都能通过**够地道**的 React API 工作（React 消费者无需手写 custom-element 胶水）。
4. **claude.ai/design 可消费**：React 包装产物能被 design-sync 那条管线 import（React 组件可从 bundle 全局/import 取到并渲染）。

**任一组件卡在任一条** → findings 报告记录是哪条、判定该组件退回路线 B、并重估路线 A 整体是否仍可行（若 3 个里多个在同一互操作面崩，可能整体退 B 或回到方案选型）。

---

## 6. 产出物

1. React 子工作区：3 个组件的 Web Component 构建入口 + React 包装 + 类型。
2. render-verification 扩展：覆盖 3 个节点的 React 渲染，与 Vue 同闸同口径。
3. `playground/` pilot 页：3 个组件 Vue / React 双栏对照。
4. **findings 报告**（落 `docs/internal/` 或 retrospection）：逐组件给"全量推 A / 退 B"结论 + 踩到的 custom-element 怪癖 + 全量阶段的工程清单与估值修正。

---

## 7. 风险与缓解

| 风险 | 缓解 |
|---|---|
| shadow DOM 隔离破坏全局 token 主题 | CSS 自定义属性穿透 shadow DOM 继承（已知特性）；pilot 实测；必要时用 light-DOM / 样式注入模式 |
| v-model / 复杂对象 props 在 React×custom element 不地道 | 包装层吸收映射；Input 专门进 pilot 验证；扛不住退 B |
| slot 组合（FormItem 包 Input）互操作 | FormItem 专门进 pilot 验证；这是最可能触发退 B 的组件 |
| 偏离 Figma 真源（React 端自由发挥） | React 强制走同一 render-verification 闸，机检 getComputedStyle vs Figma；不允许启发式 |
| 污染已发布 Vue 包 / 构建管线 | Web Component 入口完全独立于主 lib build；不改 `package.json` 主 exports |
| SSR / 框架版本兼容 | pilot 仅验证浏览器端渲染；SSR 列为全量阶段再评估（非 pilot 目标） |

---

## 8. 不在本设计内（后续阶段）

- 全量 26 组件的双框架化（pilot 通过后单列计划）。
- 生产级 React npm 包发布机制（entry/exports/版本/changelog/CI）。
- 双框架文档站完整改造（API 表双栏、双框架代码示例切换）。
- Code Connect / 反向生成对 React 的延伸。
- SSR / Next.js 等场景兼容。
