feat(ds-flow): Phase 3 — Web 硬编码色清零 + 原子台账收敛

「各自实现 + 同源闸」决策落地(不建跨端组件包):

- website.css 页脚恒暗区灰阶就近吸附主题无关 sand 原色阶(cfc6b8→sand-300、
  a89e8e→sand-400、8a8070→sand-500),消除 5 处硬编码。
- check-l1-sync 白名单加品牌 logo 色 f4efe8(Brand.astro 内联 SVG fill)。
- usercenter 13 处硬编码全为 #fff(白名单),已干净。
- CONTRACT.md 增「§6 ds-flow Web 原子清单 + 屏级三态台账」:公用原子两端映射表、
  图标三端⊆原型、L2 屏级三态(Flutter 快照/Web 代码先行/原子层同步)、硬编码白名单。

同源闸 4 道全绿(token 值 · 图标⊆原型 · Web 无硬编码色 · 幂等零 diff)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-08 01:09:16 +08:00
parent f69696daae
commit 39324c46a7
4 changed files with 41 additions and 11 deletions
+30
View File
@@ -90,3 +90,33 @@
- 连接键 off 态:原型用实心 11px 环,Flutter 用「虚线轨道环」(design/CLAUDE.md §5 canonical 连接键),以 §5 为准。
- 账户页:生产实现比原型多设备管理/兑换/联系/协议行,属真实功能扩展,沿用同设计语言。
- golden 测试环境未打包 Noto Sans SC → CJK 显示为方块,仅影响 golden 文字、不影响真机;布局可判。节点网格/周柱在未登录测试态无数据(需注入演示数据方显内容)。
---
## 6. ds-flow · Web 原子清单 + 屏级三态台账(2026-07 治理)
> 决策:Web 两端(website / usercenter)框架不同(Astro-island vs Next 静态),**各自实现 + 同源闸**——不建跨端共享组件包,两端对齐同一 `design/prototype/atoms.css` 语义,靠 `tools/check-l1-sync.mjs` 强制 token 值 + 图标同源不漂移。
### 6.1 公用原子清单(canonical = `design/prototype/atoms.css`
| 原子 | 原型 class | website 实现 | usercenter 实现 |
|---|---|---|---|
| 按钮 | `.btn`/`-primary`/`-ghost`/`-subtle`/`-danger` | `website.css .btn*`class | `shared.tsx`CSSProperties |
| 卡片 | `.card` | `.card`/`.plan` | `shared.tsx card` |
| 输入 | `.input`/`.field` | 表单 class | `shared.tsx input` |
| 徽章/药丸 | `.pill`/`.badge`(状态/accent/outline | `.tag`/`.plan` 徽章 | 内联 pill |
| 语言下拉 | `.langsel`/`.menu` | `Header.jsx` 自控下拉 | `shared.tsx LangSeg` |
> 两端语言下拉已统一为「自控菜单」(非原生 select,防 macOS 弹层漂移),行为一致、均对齐 `.langsel`/`.menu` 语义。
### 6.2 图标:三端 ⊆ 原型 sprite
`design/prototype/icons.js`58 Lucide)是图标单源。usercenter `LUCIDE`(键+路径)、Flutter `pangolin_icons._byName`(键)由 `check-l1-sync ③` 强制 ⊆ 原型;website 经 lucide-react 构建期内联。
### 6.3 屏级三态台账(L2
| 面 | 态 | 基准 | 说明 |
|---|---|---|---|
| Flutter mobile/tablet/desktop | 快照 | golden ×light/dark | 原型退役,golden + 本契约文字为准 |
| website(官网各屏) | 代码先行 | 无原型屏 | canonical 在 `web/website/`token/图标同源闸守护 |
| usercenter(各视图) | 代码先行 | 无原型屏 | canonical 在 `web/usercenter/`,同上 |
| 组件原子层 | 同步 | `prototype/index.html` 登记页 | L1 无例外先原型;`check-ds`Phase 5)强制登记 |
### 6.4 硬编码色白名单(`check-l1-sync ④`
`#fff/#ffffff/#000/#000000` + 品牌 logo 固定色 `#B96A3D/#FAF3ED/#F4EFE8/#9E5630/#3D2213`;其余一律 `var(--token)` 或行内 `ds-allow` 豁免。website 页脚恒暗区灰阶已就近吸附 `--sand-300/400/500`
@@ -73,14 +73,14 @@
不建跨端组件包;两端各自实现,但都对齐 `design/prototype/atoms.css`,靠闸保证不漂移。
- [ ] 3.1 抽公共原子清单:langsel(语言下拉,刚修的两套合规范)/ button / card / input / badge / pill。
- [x] 3.1 抽公共原子清单:langsel(语言下拉,刚修的两套合规范)/ button / card / input / badge / pill。
对每个原子在 `atoms.css` 定义 canonical 样式
- [ ] 3.2 website`website.css` + `site-extra.css` 里的按钮/卡片/下拉 class 对齐 atoms.css 语义,
- [x] 3.2 website`website.css` + `site-extra.css` 里的按钮/卡片/下拉 class 对齐 atoms.css 语义,
残留 `#fff/#000`/logo 外的硬编码色清零(当前业务硬编码 ~30 处,多为可保留的白/黑/logo)
- [ ] 3.3 usercenter`shared.tsx``card/input/LangSeg` 内联对象对齐 atoms.css 语义;
- [x] 3.3 usercenter`shared.tsx``card/input/LangSeg` 内联对象对齐 atoms.css 语义;
残留 13 处硬编码(基本 `#fff`)核对,非白/黑/logo 的清零
- [ ] 3.4 两端 langsel 行为/样式一致性核对(此前刚统一为自定义下拉,纳入 atoms 登记)
- [ ] 3.5 更新 `design/CONTRACT.md`:Web 原子清单 + 屏级台账(同步/快照/代码先行三态)
- [x] 3.4 两端 langsel 行为/样式一致性核对(此前刚统一为自定义下拉,纳入 atoms 登记)
- [x] 3.5 更新 `design/CONTRACT.md`:Web 原子清单 + 屏级台账(同步/快照/代码先行三态)
---
+1 -1
View File
@@ -94,7 +94,7 @@ function checkWebTokenSync(label, webPath) {
// ── ④ Web 硬编码色扫描 ──────────────────────────────────────
{
// 白名单:纯白/纯黑 + 品牌 logo 固定色(SVG 内联 fill,非业务散色)
const ALLOW = new Set(['fff', 'ffffff', '000', '000000', 'b96a3d', 'faf3ed', '9e5630', '3d2213']);
const ALLOW = new Set(['fff', 'ffffff', '000', '000000', 'b96a3d', 'faf3ed', 'f4efe8', '9e5630', '3d2213']);
// 排除:生成的 token 定义文件 + 构建产物
const SKIP = new Set([
'web/website/src/styles/tokens.gen.css',
+5 -5
View File
@@ -227,17 +227,17 @@ section{padding:88px 0}
.cta-ghost:hover{border-color:#fff}
/* ---------- footer ---------- */
.ftr{background:var(--sand-950);color:#cfc6b8;padding:64px 0 32px}
.ftr{background:var(--sand-950);color:var(--sand-300);padding:64px 0 32px}
[data-theme="dark"] .ftr{background:#000}
.ftr .top{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:36px}
.ftr .nm{font-family:var(--font-display);font-weight:700;font-size:18px;color:#fff;display:flex;align-items:center;gap:10px}
.ftr .tag{font-size:13.5px;line-height:1.6;margin:14px 0 0;max-width:280px;color:#a89e8e}
.ftr h4{font-family:var(--font-sans);font-size:12px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:#8a8070;margin:0 0 14px}
.ftr .tag{font-size:13.5px;line-height:1.6;margin:14px 0 0;max-width:280px;color:var(--sand-400)}
.ftr h4{font-family:var(--font-sans);font-size:12px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;color:var(--sand-500);margin:0 0 14px}
.ftr ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:10px}
.ftr ul a{font-size:14px;color:#cfc6b8}
.ftr ul a{font-size:14px;color:var(--sand-300)}
.ftr ul a:hover{color:#fff}
.ftr .mono{font-family:var(--font-mono);font-size:12.5px}
.ftr .bot{display:flex;align-items:center;justify-content:space-between;margin-top:46px;padding-top:24px;border-top:1px solid rgba(255,255,255,.1);font-size:12.5px;color:#8a8070;flex-wrap:wrap;gap:12px}
.ftr .bot{display:flex;align-items:center;justify-content:space-between;margin-top:46px;padding-top:24px;border-top:1px solid rgba(255,255,255,.1);font-size:12.5px;color:var(--sand-500);flex-wrap:wrap;gap:12px}
/* ---------- responsive ---------- */
@media (max-width:980px){