「各自实现 + 同源闸」决策落地(不建跨端组件包): - 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>
10 KiB
设计契约 · iPad / Tablet(design-distill)
像素基准:
design/ui_kits/tablet/tabapp.jsx(1180×820 横屏侧栏布局)+index.html。 数值取自原型源码,非目测。token 单源:design/colors_and_type.css→client/lib/pangolin_tokens.gen.dart(勿手改)。 目标实现:client/lib/shell/tablet_shell.dart+ 四页isWide分支 +client/lib/widgets/。 验收闸:client/test/golden/tablet_pages_golden_test.dart(1180×820 / iOS 平台 → formFactor=tablet)。
0. 归一化产物
- 类型 A(结构化包):token 单源已存在、codegen 已接(
design/codegen/gen_flutter_tokens.mjs)。本契约只补 tablet 页面像素规格 + 验收清单。 - 断点:触屏宽 ≥900 → tablet 侧栏,否则 mobile 底 Tab(
client/lib/core/responsive/form_factor.dart,对齐 README「≥900 切侧栏」)。
1. token 映射(复用既有语义层)
| 原型 CSS var | Flutter 语义层 |
|---|---|
--bg / --bg-subtle |
c.bg / c.bgSubtle |
--surface |
c.surface |
--fg1/2/3 |
c.fg1/fg2/fg3 |
--accent / --accent-subtle / --accent-border |
c.accent / c.accentSubtle / c.accentBorder |
--success --warning |
c.success c.warning |
--border / --border-strong |
c.border / c.borderStrong |
--radius-{sm,md,lg,xl,full} |
PangolinRadius.{sm,md,lg,xl,full} |
--shadow-{sm,md,lg} |
PangolinShadow.{sm,md,lg} |
--font-{display,sans,mono} |
PangolinText.{h*/body...} / PangolinText.mono |
2. 组件映射
| 原型 | Flutter | 状态 |
|---|---|---|
TabRail 侧栏 |
TabletShell + widgets/nav_sidebar.dart |
已接,宽 232、行高≥48 |
| 连接键三态 | widgets/connect_button.dart |
复用(off 态 canonical 走「虚线轨道环」,见 design/CLAUDE.md §5,优先于原型实心环简化) |
| 免费额度卡 | widgets/quota_card.dart |
复用 |
| 智能选择卡 | widgets/smart_select_card.dart |
复用 |
| 节点行 | widgets/server_tile.dart / nodes_page _NodeGridTile |
双列网格 |
| 指标卡 | stats_page _MetricCard |
复用 |
| 段控/开关/行 | account_page 内组件 |
复用 |
| Toast / 通知条 | widgets/pangolin_toast.dart showPangolinToast |
新增组件(无原型) |
Toast / 通知条规格
跨平台轻提示(如自动连接「正在自动连接到 X」)。无 React 原型 → 此处即真相源;全部走语义 token,明暗自动适配。
外观:底 --surface(浅=白 / 深=#221E19)+ 文字 --fg1(Manrope 14/600,居中)+ 描边 --border 1px + 圆角 md(10)+ 柔和暖阴影(shadow-sm);淡入并轻微上移(240ms ease-out)。绝不用 Material 默认黑底。
布局(铁律):
- 只在主显示区内,不覆盖左侧栏(left = 侧栏宽:desktop 204 / tablet 232 / mobile 0)。
- 宽度自适应内容,上限 = 主显示区宽度的 80%;超过则文本多行。
- 在主显示区内水平居中,贴底(bottom 28)。
- Canonical 实现:
widgets/pangolin_toast.dartshowPangolinToast(context, msg)(Overlay 实现,非 SnackBar,以完全控制上述布局)。pangolin_theme的snackBarTheme只作普通 SnackBar 的兜底底色样式。
3. 页面像素规格(取自 tabapp.jsx)
外壳 TabletApp
- 内屏 1180×820,
data-theme切明暗。侧栏 + 内容列(顶栏 56 + 主体)。 - 内容顶栏:高 56,padding
0 28,标题font-display 700 / 19 / fg1,右侧连接状态(mono 12.5,on=success「● {code}」/ off=fg3「○」)。
TabRail 侧栏
- 宽 232,
bg-subtle,右 1pxborder,padding18 14 16。 - 品牌头:Mark 30 + 「穿山甲」(display 700/17) + 「PANGOLIN」(9/600/字距0.2em/accent),gap 10,padding
0 8 20。 - 导航项:gap 4;每项 padding
13 14、radius md、字号 15、active 700+accent+accent-subtle 底 / inactive 500+fg2、minHeight 48、图标 20(active stroke 2.4)、gap 12。 - 底部套餐徽章:free=surface+border / pro=
linear-gradient(150deg,clay-600,clay-800)+白字;radius lg,padding12 13;头像圈 32 + 名(12.5/700) + 邮箱(10.5/opacity.65)。
连接视图 TabConnect(双栏)
- 左列 flex 1.25,居中,gap 26:按钮 216×216 圆、
box-shadow 0 0 0 11px {ring}, shadow-lg、图标 60、label mono 700/14/字距0.08em;下方 caption(16/600/fg2),on 态计时 mono 28/fg1。 - 右列宽 332,gap 14,垂直居中,paddingRight 28:
- 免费额度卡(card padding
14 16):clock + 「今日剩余 {n} 分钟」+ 「免费版」pill + 进度条(高6,≤3 变 warning) + 看广告按钮(accent-subtle/accent-border/full/minHeight44)。 - 当前节点卡(padding
13 15, minHeight 48):CC 码块 + 「当前节点」(11/600/fg3) + 名(15/600) + 副(12/fg3) + chevron。 - on 态速率行:3 卡(下载/上传/延迟),各 padding
11 13、值 mono 16。
- 免费额度卡(card padding
节点视图 TabNodes
- padding
6 28 24,overflow auto。 - 智能卡:accent-subtle 底、border(active=accent)、radius xl、padding
15 16、minHeight 56、marginBottom 14;zap 在 clay 渐变盒 42×42 + 「智能选择」(15.5/700) + 「推荐」pill + 副(12/fg2) + active check。 - 搜索框:bg-subtle、radius md、padding
11 14、maxWidth 380、marginBottom 14。 - 网格:2 列
1fr 1fr,gap 11;每格 padding13 15、minHeight 56、active=accent-subtle+accent-border:CC + 名(14.5/600) + 副 + ping(mono 12) + Signal + active check。
统计视图 TabStats(上聚合 · 下分设备)
- padding
6 28 24。 - 上聚合:3 指标卡一行,gap 14,marginBottom 20;各 padding
18 20:label(12.5/fg3/600) + 值(mono 26/fg1)。周柱卡 padding20 22:标题(13.5/600) + 7 柱(gap 22, 容器高150, 柱 maxWidth 40, accent opacity .85, radius6 6 0 0) + 值(mono 10.5) + 星期(11.5)。 - 下分设备「设备明细 / By device」(统一组件
DeviceStatRow,四端复用):周柱卡下 marginTop 20,标题(13/600/fg1);设备卡 surface+border+radius-lg+shadow-sm,行内分隔线 border。每行 padding14 16/15 18:左平台图标盒 38×38(accent-subtle, radius md, Lucide smartphone/laptop/monitor-smartphone, clay 2.2px) + 中(名 14.5–15/600 省略号 + 占比迷你条 height 4 radius 999, track=border, fill=accent .85, 宽=本设备流量/最忙设备) + 右(流量 mono 14.5–15 + " GB" caption;时长 caption "X h")。流量降序;空数据显示占位「暂无设备用量 / No device usage yet」。- 数据源:
GET /v1/usage/devices?days=N(账户内每设备窗口聚合,busiest-first)→deviceUsageProvider。
- 数据源:
账户视图 TabAccount
- padding
6 28 24,maxWidth 640(生产页功能更丰富,沿用同设计语言:套餐横幅 + 分区卡 + 行 + 段控/开关)。 - 套餐横幅 radius xl padding 20;行 minHeight 48,图标盒 34 radius sm accent-subtle;TabToggle 48×29(on=accent)。
4. 逐屏验收清单(每屏 × 明/暗 × 中/英)
- 连接 / 节点 / 统计 / 账户 四视图 × light/dark × zh/en:无溢出、侧栏 232 比例正确、触控目标 ≥48。
- 连接页双栏左右比例 ≈ 1.25 : 332;on 态速率行 3 卡出现。
- 节点页 2 列网格 + 置顶智能卡;搜索框 maxWidth 380。
- 颜色全部语义 token、无硬编码 hex(design/CLAUDE.md §1)。
- 文案无红线词(§13)、双语单显不并排。
- golden 回归闸
tablet_pages_golden_test.dart通过;真机/模拟器 mini/11″/12.9″ 横竖屏目测过。
5. 已知差异(记录,非缺陷)
- 连接键 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。