Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
16 KiB
16 KiB
PROGRESS · 全站实心按钮配色与禁用态统一(BUG-1079,2026-09-28)
任务书:docs/tasks/TASK-site-button-contrast-20260928.md。执行:Claude 子代理(产品 2026-09-28「请你修复」= 直接执行)。
基线
origin/staging=7db58230;分支codex/site-button-contrast-20260928,工作树.worktrees/site-button-contrast-20260928。Node 22.14(/exec-daemon)。- 基线实测:
tsc0;lint 0 error / 127 warning;npm test4248 / pass 4193 / fail 24 / skip 31 / cancelled 0;next build/○ Static;首屏 gzip(rootMainFiles,level 9)130,915 B。 - harness 基线:
rm -rf .next && npm run build后只拷这次 build 的 CSS;样本类名取自基线源码(button.tsx/calendar.tsx/birth-date-picker.tsx的HEAD版本)。
做了什么
- T1 根因:删层外
button { color: inherit; }(Tailwind preflight 在@layer base已有)。button:disabled { cursor: default; opacity: .45; }改为button:disabled:where(:not([data-slot="button"])):权重仍是 (0,1,1),原生<button>的禁用观感逐字不变;<Button>不再被它压,改由自身工具类决定,基础淡化disabled:opacity-50→disabled:opacity-45(与旧全局值一致,保证红线 3)。没有采用「移进@layer base」:那样层外类规则里的cursor: pointer/opacity会反过来赢过禁用规则,改变原生按钮禁用观感。 - T2 token + 合一:
:root新增--color-action-fill#a9583e /--color-action-fill-hover#8f4630 /--color-on-action-fill#fbfaf7 /--color-action-fill-disabled=var(--color-canvas-strong)/--color-on-action-fill-disabled=var(--color-ink-tertiary);两处深色块 #8f4a33 /color-mix(in srgb, #8f4a33 94%, #f2f0ea)/ #f2f0ea + 两个 disabled(dark-theme-contract要求每个 token 有深色值);打印块补浅色值;@theme inline登记五个 token。button.tsx默认变体 =bg-action-fill text-on-action-fill hover:bg-action-fill-hover disabled:bg-action-fill-disabled disabled:text-on-action-fill-disabled disabled:opacity-100(twMerge 实测保留text-sm、丢掉基础disabled:opacity-45)。.button-primary底 / 边 / 字改新 token,新增.button-primary:disabled(灰底、opacity 1)。发送 / 停止键.composer .composer-row-end > button、.composer-stop改新 token,新增:disabled灰;悬停规则拆开:.composer button:hover其它按钮仍--color-action-hover,.button-primary与发送键改--color-action-fill-hover。.danger-primary显式color: var(--color-on-dark)(D5)。calendar.tsx选中 / 区间起止日改bg-action-fill text-on-action-fill;删birth-date-picker.tsx的[&_button[data-selected-single=true]]:text-primary-foreground!。--color-primary映射未动(D6)。 - T3:删 BUG-1078 报告作用域整段与
.report-center-generate(CSS +generate-personal-report-button.tsx)。.report-export-presets [aria-pressed="true"]保留。 - T4:
frontend/DESIGN.md(token 表加一行、撤回"已知缺口"段、Button 一节写 D1–D3、报告阅读器一节改写)、docs/BUG_HISTORY.mdBUG-1079、CHANGELOG.md、docs/testing/site-button-contrast-20260928.md、BLOCKED.md、本文件、docs/tasks/README.md状态。 - harness(
docs/testing/button-contrast-harness.cjs):样本改为从源码读类名;增描边 / 幽灵 / 次要 / 危险 / 发送 / 停止 / 日历选中日 / 日期占位 / 报告页 / 悬停样本;支持oklab()与color(srgb …)计算值;默认跳过 admin 专用的 antd reset(见偏离 2)。
harness 前后对照(用户页 CSS,无头 Chrome)
| 主题 | 样本 | 改前 文字 / 底 / 不透明度 / 对比 | 改后 文字 / 底 / 不透明度 / 对比 | 变化 |
|---|---|---|---|---|
| light | Button 默认(实心) | #1d1d1f / #a9583e / 1 / 3.33 |
#fbfaf7 / #a9583e / 1 / 4.85 |
变 |
| light | Button 默认 · 禁用 | #979796 / #d6b1a4 / 0.45 / 1.49 |
#6a6963 / #e1ded6 / 1 / 4.10 |
变 |
| light | Button 描边 | #1d1d1f / #fbfaf7 / 1 / 16.12 |
#1d1d1f / #fbfaf7 / 1 / 16.12 |
相同 |
| light | Button 描边 · 禁用 | #979796 / #fbfaf7 / 0.45 / 2.81 |
#979796 / #fbfaf7 / 0.45 / 2.81 |
相同 |
| light | Button 幽灵 | #1d1d1f / #fbfaf7 / 1 / 16.12 |
#1d1d1f / #fbfaf7 / 1 / 16.12 |
相同 |
| light | Button 幽灵 · 禁用 | #979796 / #fbfaf7 / 0.45 / 2.81 |
#979796 / #fbfaf7 / 0.45 / 2.81 |
相同 |
| light | .button-primary | #fbfaf7 / #cc785c / 1 / 3.14 |
#fbfaf7 / #a9583e / 1 / 4.85 |
变 |
| light | .button-primary · 禁用 | #fbfaf7 / #e6c0b1 / 0.45 / 1.61 |
#6a6963 / #e1ded6 / 1 / 4.10 |
变 |
| light | .danger-primary | #fbfaf7 / #9a2f2f / 1 / 7.13 |
#fbfaf7 / #9a2f2f / 1 / 7.13 |
相同 |
| light | .danger-primary · 禁用 | #fbfaf7 / #cf9f9d / 0.45 / 2.21 |
#6a6963 / #e1ded6 / 1 / 4.10 |
变 |
| light | .button-secondary | #1d1d1f / #fbfaf7 / 1 / 16.12 |
#1d1d1f / #fbfaf7 / 1 / 16.12 |
相同 |
| light | .button-secondary · 禁用 | #979796 / #fbfaf7 / 0.45 / 2.81 |
#979796 / #fbfaf7 / 0.45 / 2.81 |
相同 |
| light | 输入框发送键 | #fbfaf7 / #cc785c / 1 / 3.14 |
#fbfaf7 / #a9583e / 1 / 4.85 |
变 |
| light | 输入框发送键 · 禁用 | #fbfaf7 / #e6c0b1 / 0.45 / 1.61 |
#6a6963 / #e1ded6 / 1 / 4.10 |
变 |
| light | 输入框停止键 | #fbfaf7 / #cc785c / 1 / 3.14 |
#fbfaf7 / #a9583e / 1 / 4.85 |
变 |
| light | 日历选中日 | #fbfaf7 / #a9583e / 1 / 4.85 |
#fbfaf7 / #a9583e / 1 / 4.85 |
相同 |
| light | 日期选择器(未选,占位) | #1d1d1f / #fbfaf7 / 1 / 16.12 |
#5f5f59 / #fbfaf7 / 1 / 6.16 |
变 |
| light | 报告页 Button 默认 | #fbfaf7 / #a9583e / 1 / 4.85 |
#fbfaf7 / #a9583e / 1 / 4.85 |
相同 |
| light | 报告页 Button 默认 · 禁用 | #6a6963 / #e1ded6 / 1 / 4.10 |
#6a6963 / #e1ded6 / 1 / 4.10 |
相同 |
| light | 报告页 描边 · 禁用 | #6a6963 / #e1ded6 / 1 / 4.10 |
#979796 / #fbfaf7 / 0.45 / 2.81 |
变 |
| light | 报告页页眉「生成报告」· 禁用 | #6a6963 / #e1ded6 / 1 / 4.10 |
#979796 / #fbfaf7 / 0.45 / 2.81 |
变 |
| light | Button 默认 · 悬停 | #1d1d1f / #b97863 / 1 / 4.78 |
#fbfaf7 / #8f4630 / 1 / 6.52 |
变 |
| light | Button 描边 · 悬停 | #1d1d1f / #ebe9e3 / 1 / 13.86 |
#1d1d1f / #ebe9e3 / 1 / 13.86 |
相同 |
| light | .button-primary · 悬停 | #fbfaf7 / #8f4630 / 1 / 6.52 |
#fbfaf7 / #8f4630 / 1 / 6.52 |
相同 |
| light | .danger-primary · 悬停 | #fbfaf7 / #8b2d2d / 1 / 8.02 |
#fbfaf7 / #8b2d2d / 1 / 8.02 |
相同 |
| light | 输入框发送键 · 悬停 | #fbfaf7 / #8f4630 / 1 / 6.52 |
#fbfaf7 / #8f4630 / 1 / 6.52 |
相同 |
| light | 日历选中日 · 悬停 | #fbfaf7 / #a9583e / 1 / 4.85 |
#fbfaf7 / #a9583e / 1 / 4.85 |
相同 |
| dark | Button 默认(实心) | #f2f0ea / #d78064 / 1 / 2.57 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
| dark | Button 默认 · 禁用 | #82817d / #764f41 / 0.45 / 1.83 |
#9c988e / #3b3a37 / 1 / 3.95 |
变 |
| dark | Button 描边 | #f2f0ea / #262624 / 1 / 13.30 |
#f2f0ea / #262624 / 1 / 13.30 |
相同 |
| dark | Button 描边 · 禁用 | #82817d / #262624 / 0.45 / 3.88 |
#82817d / #262624 / 0.45 / 3.88 |
相同 |
| dark | Button 幽灵 | #f2f0ea / #262624 / 1 / 13.30 |
#f2f0ea / #262624 / 1 / 13.30 |
相同 |
| dark | Button 幽灵 · 禁用 | #82817d / #262624 / 0.45 / 3.88 |
#82817d / #262624 / 0.45 / 3.88 |
相同 |
| dark | .button-primary | #241f1c / #d78064 / 1 / 5.56 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
| dark | .button-primary · 禁用 | #252320 / #764f41 / 0.45 / 2.20 |
#9c988e / #3b3a37 / 1 / 3.95 |
变 |
| dark | .danger-primary | #241f1c / #e08573 / 1 / 6.04 |
#241f1c / #e08573 / 1 / 6.04 |
相同 |
| dark | .danger-primary · 禁用 | #252320 / #7a5148 / 0.45 / 2.30 |
#9c988e / #3b3a37 / 1 / 3.95 |
变 |
| dark | .button-secondary | #f2f0ea / #262624 / 1 / 13.30 |
#f2f0ea / #262624 / 1 / 13.30 |
相同 |
| dark | .button-secondary · 禁用 | #82817d / #262624 / 0.45 / 3.88 |
#82817d / #262624 / 0.45 / 3.88 |
相同 |
| dark | 输入框发送键 | #241f1c / #d78064 / 1 / 5.56 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
| dark | 输入框发送键 · 禁用 | #252320 / #764f41 / 0.45 / 2.20 |
#9c988e / #3b3a37 / 1 / 3.95 |
变 |
| dark | 输入框停止键 | #241f1c / #d78064 / 1 / 5.56 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
| dark | 日历选中日 | #241f1c / #d78064 / 1 / 5.56 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
| dark | 日期选择器(未选,占位) | #f2f0ea / #262624 / 1 / 13.30 |
#b3afa4 / #262624 / 1 / 6.92 |
变 |
| dark | 报告页 Button 默认 | #f2f0ea / #8f4a33 / 1 / 5.77 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
相同 |
| dark | 报告页 Button 默认 · 禁用 | #9c988e / #3b3a37 / 1 / 3.95 |
#9c988e / #3b3a37 / 1 / 3.95 |
相同 |
| dark | 报告页 描边 · 禁用 | #9c988e / #3b3a37 / 1 / 3.95 |
#82817d / #262624 / 0.45 / 3.88 |
变 |
| dark | 报告页页眉「生成报告」· 禁用 | #9c988e / #3b3a37 / 1 / 3.95 |
#82817d / #262624 / 0.45 / 3.88 |
变 |
| dark | Button 默认 · 悬停 | #f2f0ea / #b46e57 / 1 / 3.48 |
#f2f0ea / #95543e / 1 / 5.10 |
变 |
| dark | Button 描边 · 悬停 | #f2f0ea / #30302d / 1 / 11.62 |
#f2f0ea / #30302d / 1 / 11.62 |
相同 |
| dark | .button-primary · 悬停 | #241f1c / #e59273 / 1 / 6.75 |
#f2f0ea / #95543e / 1 / 5.10 |
变 |
| dark | .danger-primary · 悬停 | #241f1c / #e29281 / 1 / 6.72 |
#241f1c / #e29281 / 1 / 6.72 |
相同 |
| dark | 输入框发送键 · 悬停 | #241f1c / #e59273 / 1 / 6.75 |
#f2f0ea / #95543e / 1 / 5.10 |
变 |
| dark | 日历选中日 · 悬停 | #241f1c / #d78064 / 1 / 5.56 |
#f2f0ea / #8f4a33 / 1 / 5.77 |
变 |
结论:实心按钮(默认 <Button>、.button-primary、发送 / 停止键、日历选中日)两主题全部 ≥ 4.5(浅 4.85、深 5.77;悬停浅 6.52、深 5.10);实心禁用 opacity 1 灰底;.danger-primary 7.13 / 6.04 不变;描边、幽灵、次要及其禁用、悬停逐项相同(红线 3)。
外观会变的非实心按钮 / 原生按钮
- 日期选择器未选时的占位文字:
data-[empty=true]:text-muted-foreground以前被层外color: inherit压成正文色,现在生效:浅 #1d1d1f → #5f5f59(6.16),深 #f2f0ea → #b3afa4(6.92)。这是组件本来的设计。 <Button>描边 / 幽灵的hover:text-foreground现在生效:只在按钮所在容器文字色不是--color-ink时才看得出(harness 标准样本前后相同)。- 报告页的描边按钮禁用态(页眉「生成报告」提交中、原始附录下载忙时):BUG-1078 期间是灰底,现在回到与全站描边按钮一致的 45% 淡化(浅 2.81、深 3.88)。按任务书 D3(非实心禁用不在范围)+ D4(删局部覆盖)执行;真机清单第 12 条已写明。
- 原生
<button>带 Tailwind 文字色工具类的:AST 扫描 +grep除上面的日期占位外没有(其余text-primary/text-danger都在图标上,不是按钮)。其它!补丁:TSX 里只有日期选择器那一处,已删;globals.css的!important均与按钮文字色无关(toast、候选徽章等)。
改动的既有断言(原值 / 新值 / 原因)
| 文件 · 断言 | 原值 | 新值 | 原因 |
|---|---|---|---|
session-conversation-layout.test.ts 停止键底色 |
.composer .composer-stop background: var(--color-action-strong) |
var(--color-action-fill) |
BUG-1079 D1:实心按钮一种底色 |
| 同上 停止键悬停 | var(--color-action-hover) |
var(--color-action-fill-hover) |
同上 |
report-buttons-scope-20260928.test.ts 「the shared button keeps its variants, disabled fade and data-slot hook」 |
默认变体 bg-primary text-primary-foreground hover:bg-primary/80、disabled:opacity-50 |
并入「the shared Button, .button-primary, the composer controls and the calendar read the same fill」:默认变体为新 token、disabled:opacity-45 |
上一单红线「button.tsx 不改」只针对报告单;本单 D1 / 红线 6 允许改 |
| 同文件 「the theme tokens the fix reads are unchanged」 | 断言报告修复读的旧 token 原值 | 「one solid-button token pair per theme, readable at 4.5:1 or better」:断言新 token 对三处主题块的值与对比度,并锁 --color-primary 映射与 #cc785c |
报告局部修复已删,锁的对象换成全站 token |
| 同文件 「button overrides exist only inside the report scope」 | 所有 [data-slot="button"] 覆盖必须在报告作用域 |
「the BUG-1078 report-only override is gone; …」:不得再有 --report-button-*、.report-center-generate、[class~="bg-primary"] |
D4 删局部覆盖 |
| 同文件 「solid report buttons read at least 4.5:1 …」 | 断言 \nbutton { color: inherit; } 仍存在 |
「the unlayered element rules no longer override button utilities」:断言层外 button { color 与 button:disabled { 不存在,原生按钮淡化规则存在 |
根因本单修掉 |
| 同文件 「the selected export preset no longer uses …」 | 套餐选中态不用 action-soft | 合并进上面「… gone; the selected export preset stays neutral」,断言不变 | 保留 |
| 同文件 「the header 「生成报告」 button carries the report hook …」 | 必须带 report-center-generate |
必须不带(同上条) | D4 |
测试名:该文件 6 条旧名全部移除,换成 6 条新名(上表列出的 4 条,外加 solid disabled buttons are grey at full opacity, never a faded clay、.danger-primary keeps its own label colour instead of taking the new fill ink),文件内条数与全量总数都不变。
验证
| 项 | 基线 7db58230 |
本分支 | 结论 |
|---|---|---|---|
tsc --noEmit |
0 | 0 | 通过 |
npm run lint |
0 error / 127 warning | 0 error / 127 warning | 通过 |
npm test |
4248 / 4193 / 24 / 31 / 0 | 4248 / 4193 / 24 / 31 / 0 | 通过:24 条失败 diff 为空,均为 DB / 部署 / staging 套件(无 Docker) |
| 读 DESIGN.md 的 7 个测试 + 本单相关(改文档后重跑) | — | 97 / 97 | 通过 |
next build 路由表 |
— | 与基线逐行相同,/ ○ Static |
通过 |
| 首屏 gzip | 130,915 B | 130,915 B | 0.00% |
偏离与发现
- 禁用规则用
:where(:not([data-slot="button"]))而非移进@layer base(理由见 T1),任务书红线 1 的「或改成不压工具类的写法」允许。 - 扫描误计:后台管理路由(
src/app/admin/layout.tsx)只加载antd/dist/reset.css+admin.css+ 字体,不加载globals.css。所以任务书里「后台管理 52 个黑字深红」不成立:它们没有任何 Tailwind 样式,是 antd reset 下的朴素按钮,本单修复对它们无影响。harness 起初把 antd reset 一起加载,才测出"改后仍 3.33"的假象;现默认跳过(HARNESS_INCLUDE_ADMIN=1可带上)。写入BLOCKED.md待产品决定。 - 同理的另一条层外规则
button, input, textarea, select { font: inherit; }压掉<Button>的text-sm(harnessfontSize列:<Button>16px、.button-primary14px)。不在任务书范围,未改,写入BLOCKED.md。
环境缺口
- 无 Docker:24 条 DB / 部署套件同基线。
- 无登录态真机:页面级走查按
docs/testing/site-button-contrast-20260928.md。 - 未推送、未部署。