# P13 实现计划:暗色模式 - **spec**:`docs/specs/2026-10-03-p13-dark-mode-design.md`(权威依据,冲突时以 spec 为准) - **证据归档**:`crearte/.superpowers/sdd-p13/FINDINGS-survey.md`(7 轮 spike 实测) - **分支**:`feat/p13-dark-mode`(已建,从 crearte `e59a171` 起) - **范围**:仅 `crearte`。**`crearte-server` 与 `crearte-deploy` 零改动**(spec §4 边界)。 - **版本**:crearte 0.26.0 / wrapper 0.3.6(记账由控制者做,实现者**禁动** ROADMAP、CHANGELOG、`docs/`)。 --- ## 任务切分与波次 全部任务都在 `crearte` 单一工作树与 git index 上 → 按 `sdd-parallel-dispatch` §1「一仓一写者」,**交给一个子代理串行完成**,不拆并行(拆了就是四路争用同一个 `.git/index.lock`,P12 已验证这条纪律)。 **TDD 顺序:守卫先行**。T1 写守卫并**看它红**(RED 输出必须复现 spec 引用的实测数字),再 T2 让它绿。P12 的经验:这个顺序使 RED 输出本身成为「守卫有牙」的证明,免去事后 revert 自证那一轮。 | 序 | 任务 | 文件 | 完成判据 | |---|---|---|---| | **T1** | 守卫重构为双主题(**RED 先行**) | `app/lib/contrast.test.ts` | 按块解析(亮/暗各自 Map);两块各断言 12 令牌齐备;双主题各断言 spec §1.3/§7 的 12 条真实配对(阈值:正文 4.5 / wordmark 大字号 3.0 / 焦点环非文本 3.0);`scrim` 分离**按主题钉实际分离侧**(D-F):亮色钉填充侧 `paper vs scrim ≥3`(17.60)、暗色钉边框侧 `ink vs scrim ≥3`(17.60)——**不得写成 `max(两侧) ≥3`**,两侧互补使其理论下界 = √16.50 = 4.0621 > 3 → **数学恒真、永不可能失败**(审查 finding #1,控制者暂力重算证实;实证:亮 scrim 改纯白得填充侧 1.1165 但 max()=18.42 → 仍绿)。也不得只钉 `ink vs scrim` 于两主题(亮色仅 1.07 → 误红);含「旧单 Map 解析器对含暗色块的 CSS 会得出错误结果」的反面钉桩(防后人简化回去)。**此时暗色块尚不存在 → 必须 RED**,红输出须显示「暗色 12 令牌缺失」 | | **T2** | 令牌层落地 | `app/styles/main.css` | `@theme` 新增 `--color-skeleton: #efe9da` / `--color-scrim: #0d0b08`;五个 `--shadow-hard*` 内联 hex → `var(--color-ink)` / `var(--color-accent)`(D-D);追加 `html[data-theme="dark"] { … }` 全量 12 令牌 + `color-scheme: dark`(D-B/D-C,**必须** `html[data-theme=dark]` 特异性 (0,1,1),禁裸属性选择器、禁 `!important`、禁 `@apply` 绕过);`::selection`/`strong`/`.wordmark-label` **零改动**(D-A 核心收益)。**T1 由红转绿** | | **T3** | 三处 usage 迁移 + 硬编码色守卫 | `ResultMeta.vue:52`、`StatePanel.vue` ×7、`FilterDrawer.vue:32`、新守卫文件 | ① `bg-accent text-ink` → `bg-accent-ink text-paper`(唯一把 accent 当背景的地方,迁移后 accent 纯非文本);② `bg-[#EFE9DA]` ×7 → `bg-skeleton`;③ `backdrop:bg-ink/60` → `backdrop:bg-scrim/80`(D-E)。新增源码级守卫扫描 `app/**/*.vue` **禁止** `bg-[#`/`text-[#`/`border-[#` 硬编码 hex,**RED 先行**(迁移前应报 7 处违规),且**必须沿用 P12 `tableOverflow.test.ts` 的屏蔽范式**(`