Files
Jyotisha/docs/tasks/TASK-site-button-contrast-20260928.md
T

11 KiB
Raw Blame History

TASK · 全站实心按钮配色与禁用态统一(2026-09-28)

基线

  • origin/staging = b3b5f0e1(2026-09-28)。开工时 git fetch origin --prune,以最新 origin/staging 为基线,实际 SHA 写进 PROGRESS。
  • 分支 codex/site-button-contrast-20260928,工作树 .worktrees/site-button-contrast-20260928。
  • 进度记录 docs/tasks/PROGRESS-site-button-contrast-20260928.md。
  • 前置:TASK-chart-types-and-report-buttons-20260928(BUG-1078,报告页局部修复)已部署 staging 6a4e312c。本单取代它的局部覆盖,改成全站一处定义。同日其它任务书与本单无文件交集;frontend/src/app/globals.css 只动本单列出的规则。

事故实证(Claude 2026-09-28 全站扫描)

方法:TypeScript AST 列出 frontend/src 里全部 213 个 <Button> / <button>,再把 staging 线上实际下发的 CSS(/_next/static/chunks/*.css)装进无头 Chrome,深浅两种主题逐一读计算样式、按叠加后的颜色算对比度。脚本已随本单提交:docs/testing/button-contrast-harness.cjs。

样本 浅色 文字 / 底 / 对比 深色 文字 / 底 / 对比
<Button> 默认实心 #1d1d1f / #a9583e / 3.33 #f2f0ea / #d78064 / 2.57
<Button> 默认 · 禁用 45% 透明 / 1.49 45% 透明 / 1.83
.button-primary #fbfaf7 / #cc785c / 3.14 #241f1c / #d78064 / 5.56
.button-primary · 禁用 45% 透明 / 1.61 45% 透明 / 2.20
.danger-primary #fbfaf7 / #9a2f2f / 7.13 #241f1c / #e08573 / 6.04
输入框发送键 #fbfaf7 / #cc785c / 3.14 #241f1c / #d78064 / 5.56
报告页 <Button>(BUG-1078 已修) #fbfaf7 / #a9583e / 4.85 #f2f0ea / #8f4a33 / 5.77
描边 / 次要按钮 16.12 13.30

受影响范围:

  • 实心 <Button>,渲染为真 <button> 的共 63 个(渲染为链接 <a> 的不受影响),其中报告页 2 个已由 BUG-1078 修好,未修 61 个:后台管理 52、点数 / 订阅面板 3、星历 2、生时校正对话 2、输入框发送 / 停止键 2(这两个另有 .composer 层外规则上色,见下)。日期选择器选中日(calendar.tsx 的 bg-primary text-primary-foreground,birth-date-picker.tsx 用 ! 强行救回了一处)同病。
  • .button-primary 共 30 个:登录 7、首次引导 3、人物档案 3(含 2 个 .danger-primary)、首页 2、生时校正各卡 11、兑换码付费墙 2、星盘资料表单 1、首页错误屏 1。
  • 禁用态:全部 213 个按钮都被 button:disabled { opacity: .45 } 整体淡化。
  • 其它自定义按钮类(.button-secondary、会话列表、Tab、chip 等)逐条算过,均 ≥ 4.5。

根因(按符号定位,基线 b3b5f0e1)

  1. frontend/src/app/globals.css 顶部元素规则 button { color: inherit; }、button:disabled { cursor: default; opacity: .45; } 不在任何 @layer 里。Tailwind v4 的工具类(text-primary-foreground、disabled:opacity-50)都在 @layer utilities;层外规则一律压过层内规则,与选择器权重无关。所以 <Button> 的文字色永远是继承来的正文色:浅色是近黑,深色是近白。Tailwind 预检(preflight)在 @layer base 里本来就给 button 设了 color: inherit,这条层外规则是重复的。
  2. .button-primary 与发送键用 --color-action-strong(#cc785c)作底,文字用 --color-on-dark。深色主题把 --color-on-dark 翻成 #241f1c,于是深色下是深字配珊瑚底,浅色下是白字配浅珊瑚(3.14)。frontend/DESIGN.md「Where the action color appears」写明这是产品曾接受的已知缺口("looks like Claude")。
  3. 两套实心按钮各自取色:<Button> 走 --color-primary(= --color-action),.button-primary 走 --color-action-strong。同一个"主操作"在不同页面是两种颜色。
  4. BUG-1078 只在报告页作用域内覆盖了颜色(globals.css 里 :is(.report-center-message, …) [data-slot="button"]… 那一段 + .report-center-generate),根因 1 没动。

决策记录(产品 2026-09-28 拍板)

  • D1 全站一种实心主按钮(产品选 A),与报告页 BUG-1078 已上线的配色相同:
    • 浅色:底 #a9583e(--color-action)+ 字 #fbfaf7,4.85:1;悬停 --color-action-hover(#8f4630)。
    • 深色:底 #8f4a33(--color-action-on-dark)+ 字 #f2f0ea(--color-ink),5.77:1;悬停与报告页同法(向 ink 混 6%)。
    • 适用:<Button> 默认变体、.button-primary、输入框发送 / 停止键、日期选择器选中日。
  • D2 推翻 DESIGN.md 的"已知缺口":.button-primary 不再用 #cc785c 珊瑚底。代价是登录、首次引导按钮深一档,没那么像 Claude,产品接受。#cc785c 在非按钮处照旧:会话选中竖条、焦点环、打字光标、单选圆点、两处头像圆。
  • D3 实心按钮禁用态 = 中性灰:底 --color-canvas-strong、字 --color-ink-tertiary、不透明(opacity 1)。适用于 D1 的全部实心按钮与 .danger-primary。描边、幽灵、次要、图标、chip 等非实心按钮的禁用淡化不在本单范围,保持现状。
  • D4 删掉 BUG-1078 的报告页局部覆盖:一处定义、全站生效。报告页观感不变(数值与 D1 相同)。
  • D5 .danger-primary 启用态保持现在的对比度(浅 7.13、深 6.04):.button-primary 改色后它不得被连带改成浅字配浅红,需要时给它单独写文字色。
  • D6 不重映射 --color-primary:它还被 text-primary 用在报告页的加载圈和图标上,深色下改成 #8f4a33 会让这些图标对比度掉到约 2.3。新增一对只给按钮用的 token(建议名 --color-action-fill / --color-on-action-fill,深浅各一组),<Button> 默认变体、.button-primary、发送键、日历选中日都引用它。

硬红线

  1. 修根因:button { color: inherit; } 删掉(preflight 已有),button:disabled 那条移进 @layer base,或改成不压工具类的写法;不得在各页面再写局部覆盖来绕开。
  2. 不改 --color-primary / --color-primary-foreground 的映射(D6);不改 #cc785c 的非按钮用途(D2)。
  3. 非实心按钮的外观与禁用态不变:用 harness 前后对比描边、次要、幽灵的数值,必须完全相同。
  4. .danger-primary 两种主题 ≥ 4.5:1。
  5. 每个 D1 实心按钮两种主题 ≥ 4.5:1;禁用态 opacity = 1。
  6. components/ui/button.tsx 可以改(上一单的红线只针对报告单),但变体名和 props 不变,调用方零改动。
  7. frontend/src/app/page.tsx 不碰;/ 保持 ○ Static;首屏 gzip ±2%。
  8. 改既有断言写「原值 / 新值 / 原因」(report-buttons-scope-20260928.test.ts 必然要改);测试总数不低于基线。
  9. 不升级依赖;harness 不进 package.json,只用本机全局 playwright-core + Chrome。

任务分解

T1 根因:层外元素规则

  • 处理 globals.css 顶部的 button { color: inherit; }、button:disabled { … }(见红线 1)。
  • 源码合同测试:globals.css 里不存在层外的 button { color 与层外的 button:disabled。
  • 验收:harness 显示 <Button> 默认变体文字色 = text-primary-foreground 解析值;描边 / 次要 / 幽灵数值与基线逐项相同。

T2 按钮专用 token + 两套实心按钮合一

  • 新增 D6 的 token 对(:root、prefers-color-scheme: dark 且非 data-theme=light、[data-theme=dark] 三处,照现有主题写法)。
  • button.tsx 默认变体改用新 token(含 hover),禁用态按 D3。
  • .button-primary、.composer .composer-row-end > button、.composer .composer-stop 改用新 token,禁用态按 D3。
  • .danger-primary 按 D5 保持对比度,禁用态按 D3。
  • 日期选择器选中日走新 token,删掉 birth-date-picker.tsx 里的 ! 强制补丁(根因修了就不需要)。
  • 验收:harness 全表 D1 项两种主题 ≥ 4.5,禁用态 opacity 1、灰底;数值写进 PROGRESS。

T3 删 BUG-1078 局部覆盖

  • 删掉报告页作用域的颜色与禁用规则、.report-center-generate 类(generate-personal-report-button.tsx 里的 className 一并去掉)。报告导出套餐选中态(.report-export-presets [aria-pressed="true"])是另一回事,保留。
  • report-buttons-scope-20260928.test.ts 改为全站合同,三栏说明。
  • 验收:报告页 harness 数值与改前相同(4.85 / 5.77,禁用灰)。

T4 文档

  • frontend/DESIGN.md:「Where the action color appears」删掉"已知缺口"段,改写两级规则(按钮底 = --color-action-fill;#cc785c 只留非按钮用途);「Button」一节写 D1–D3。
  • docs/BUG_HISTORY.md:BUG-1079(全站实心按钮文字被层外 button { color: inherit } 压掉 + .button-primary 低对比 + 禁用半透明)。关联 BUG-1078,写明 1078 只修了报告作用域、根因未动,所以同病在其它 60 多处未拦住。
  • CHANGELOG.md 一条(Skill 不 bump)。
  • docs/testing/site-button-contrast-20260928.md:真机清单,浅 / 深各走一遍:登录、首次引导、点数与订阅面板、兑换码、人物档案(含删除确认)、星盘资料保存、星历、生时校正卡片、输入框发送 / 停止 / 排队、日期选择器、我的报告导出;每处看"文字清楚、禁用是灰不是淡红"。

让步顺序

  1. 后台管理页(components/admin/*)的真机走查可以只抽 3 页,其余靠 harness 与合同测试。
  2. 日期选择器选中日若牵动 calendar.tsx 太多,保留 birth-date-picker.tsx 的补丁,只在 PROGRESS 说明。

T1、T2 的 <Button> / .button-primary / 禁用灰不让。

开工前置命令

cd /workspace/Jyotisha && git status -sb | head -1
git fetch origin --prune
git worktree add -b codex/site-button-contrast-20260928 .worktrees/site-button-contrast-20260928 origin/staging
cd .worktrees/site-button-contrast-20260928
grep -oE "^## BUG-[0-9]+" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1   # 应为 BUG-1078
grep -n "BUG-1078\|button-primary\|opacity: .45" docs/BUG_HISTORY.md | head
cd frontend && export PATH=/exec-daemon:$PATH && npm ci && ./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -6   # 记基线与失败名单
npm run build && mkdir -p /tmp/btn-base && cp .next/static/chunks/*.css /tmp/btn-base/ && node ../docs/testing/button-contrast-harness.cjs /tmp/btn-base   # 基线数值

harness 只读刚 build 出来的 CSS。09-28 扫描时一份过期的 .next 曾让已上线的修复看起来不存在。

BUG 编号

起点 BUG-1079(开工时核对 docs/BUG_HISTORY.md 最大号为 1078)。

交付

  • 代码 + 文档同批一次快进推 git push origin HEAD:staging,核对远端 SHA;门禁绿后核对 /api/health 的 deployment.gitCommit,再用 harness 对 staging 线上 CSS 复测一遍,数值贴进 PROGRESS。
  • 不提升 main、不改 workflow。