From 110de794016e710b1747fc776d1aecdeebf17c9b Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 19 Sep 2026 20:58:59 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=A8=E7=AB=AF:=20=E4=B8=89=E7=BA=A7?= =?UTF-8?q?=E6=96=87=E5=AD=97**=E6=B5=85=E8=89=B2=E4=B9=9F=E4=B8=80?= =?UTF-8?q?=E6=A0=B7=E4=B8=8D=E5=A4=9F**=EF=BC=8812=20=E5=A4=84=202.85:1?= =?UTF-8?q?=EF=BC=89+=20=E5=88=A4=E6=8D=AE=E4=B8=8D=E5=86=8D=E5=8F=AA?= =?UTF-8?q?=E9=AA=8C=E4=B8=80=E5=8D=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 一、这是"我自己推的理由"被实测推翻 上一条我把 `textSubtleFor()` 写成**只改深色**,理由是: > 「浅色下这些令牌本来就是为浅底配的,量它们没有信息量」 **那个理由是错的。** 切到浅色主题复扫,立刻抓到 12 处: ``` "用户名 / 显示名 / 角色 / 状态 / 创建时间 / 最后登录" 2.85:1 "已同步" / "37%" / "3px"(外观区) 2.85:1 "这一天没有日程" 2.85:1 ``` `ink rgb(153,153,153)` 压白底 —— 全是 `textSubtle`。 对照 WebUI 浅色(`index.css` `:root`): - `gray-400` = `107 114 128` = **4.83:1** - `gray-500` = `90 98 112` = **6.15:1** ⇒ 系统三级色**两个主题下都不够**。它的语义只是"比二级更淡", **不保证任何可读性下限**;WebUI 那边两套主题都做过适配,这边一档都没有。 修:`textSubtleFor()` 两个主题都返回 `textMuted` (系统二级色,语义就是"次要文字",且**跟随主题** —— 不必再维护两个手写常量、不必登记)。 ## 二、判据从"只验深色"改成"两个主题都验" 原来那条设备判据**在浅色下直接 skip**,skip 的理由正是上面那句错话。 现在两个主题都扫,断言消息里带上主题名(`深色主题下…` / `浅色主题下…`)。 ★ 纪律:**色令牌的可读性是两个主题各自的事,不能只验一半。** "另一半没问题"若没量过,就只是推测。 **变异验证**:把 `textSubtleFor()` 改回"两主题都用 textSubtle" (此时设备正在浅色主题)⇒ **判红**;还原 ⇒ 绿。 ## 三、设备复扫(浅色主题) ``` [通信] 扫 41 段,低对比 0 [日历] 扫 84 段,低对比 0 [联系人] 扫 50 段,低对比 0 [我的] 扫 40 段,低对比 0 ``` **215 段文字全部 ≥3:1。** 两个主题合起来:深色 219 段 + 浅色 215 段,全绿。 ## 四、顺手改掉的 测试名从「深色下短文本必须都读得动」改成「**当前主题**下…」 —— 名字得跟着它实际判的东西走(否则下一个人会以为它只管深色)。 `run-all.mjs` → `checks=515 pass=515 fail=0 skip=0 red=0 broken=0 unreported=0`。 **当前设备主题**:浅色(本轮为验另一半切过去的;服务端 `theme=light`)。 --- .../electron/test/cross-client-theme.test.mjs | 22 ++++++++++++------- .../entry/src/main/ets/common/Theme.ets | 18 ++++++++++++++- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/client/electron/test/cross-client-theme.test.mjs b/client/electron/test/cross-client-theme.test.mjs index c7d7b27..6a92581 100644 --- a/client/electron/test/cross-client-theme.test.mjs +++ b/client/electron/test/cross-client-theme.test.mjs @@ -1318,7 +1318,7 @@ test('C|「会跟随主题翻转的 Resource」不得当前景色(含矛盾 }); -test('★ 设备:深色下短文本必须都读得动(扫一屏,不只查一个元素)', async (t) => { +test('★ 设备:当前主题下短文本必须都读得动(扫一屏,不只查一个元素)', async (t) => { /* * ★★ 这条是上面"品牌色真的画成那个色"的**一般化**。 * @@ -1357,16 +1357,22 @@ test('★ 设备:深色下短文本必须都读得动(扫一屏,不只查 await new Promise((r) => setTimeout(r, 1500)); /* - * ★ 只在**深色**下判。浅色下这些令牌本来就是为浅底配的, - * 量它们没有信息量(也不会红),但会拖慢判据。 + * ★★ 两个主题**都要判**(第一版只判深色,理由写的是"浅色下这些令牌本来就是 + * 为浅底配的,量它们没有信息量" —— **那个理由是错的**)。 + * + * 切到浅色复扫才发现的实例:浅色 `textSubtle` = `rgb(153,153,153)` 压白底 + * = **2.85:1**,而 WebUI 浅色的 gray-400 是 4.83:1。 + * ⇒ "浅色下没问题"是我自己推的,不是量出来的。 + * + * ★ 纪律:**色令牌的可读性是两个主题各自的事**,不能只验一半。 + * 现在把当前主题**记下来**,两个主题都扫;判据只看"有没有低对比", + * 不关心是哪个主题(两种都是错)。 */ - const img0 = D.readPixels(await D.screenshot(hdc, '/tmp/dark-legibility.png')); + const img0 = D.readPixels(await D.screenshot(hdc, '/tmp/theme-legibility.png')); assert.ok(img0, '要能截屏并解码'); const corner = img0.at(4, 4); const isDark = corner && corner.r < 128 && corner.g < 128 && corner.b < 128; - if (!isDark) { - return t.skip(`当前不是深色主题(左上角像素 rgb(${corner?.r},${corner?.g},${corner?.b}))—— 本条只在深色下有意义`); - } + const themeName = isDark ? '深色' : '浅色'; const lum = (c) => { const f = (v) => { const x = v / 255; return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4; }; @@ -1467,7 +1473,7 @@ test('★ 设备:深色下短文本必须都读得动(扫一屏,不只查 '太少说明遍历或尺寸过滤写错了,那样下面的检查恒绿。'); assert.deepStrictEqual(bad, [], - `★ 深色下这些文字读不动(对比度 <3:1):\n ${bad.join('\n ')}\n` + + `★ ${themeName}主题下这些文字读不动(对比度 <3:1):\n ${bad.join('\n ')}\n` + ' 深色下品牌色/面色都要换深色变体:\n' + ' · 文字/图标用 `Theme.accentFor()`(深色自动给 `#80AFF9`,' + '对齐 WebUI 的 `.dark --c-blue-600`)\n' + diff --git a/client/harmony/entry/src/main/ets/common/Theme.ets b/client/harmony/entry/src/main/ets/common/Theme.ets index ad3b2bd..fe8b865 100644 --- a/client/harmony/entry/src/main/ets/common/Theme.ets +++ b/client/harmony/entry/src/main/ets/common/Theme.ets @@ -108,7 +108,23 @@ export class Theme { * 同一条纪律:**别在页面里自己写 `isDark ? a : b`**。 */ static textSubtleFor(dark?: boolean): Resource { - return Theme.isDarkNow(dark) ? Theme.textMuted : Theme.textSubtle; + /* + * ★★ 2026-09-19 **浅色也要换**(第一版只改了深色,漏了另一半)。 + * + * 切到浅色主题复扫,立刻抓到 12 处: + * 浅色 `textSubtle` 实测 `rgb(153,153,153)` 压白底 = **2.85:1** + * 而 WebUI 浅色的 gray-400 是 `rgb(107,114,128)` = **4.83:1**, + * gray-500 是 `rgb(90,98,112)` = **6.15:1**。 + * + * ⇒ 系统三级色**两个主题下都不够**,不是"只有深色有问题"。 + * 同一档位在 WebUI 那边两套主题都做了可读性适配, + * 而系统语义色只管"比二级更淡"这层关系。 + * + * 所以两个主题都返回二级色(`textMuted`)。 + * ★ 为什么不各写一个手写值:系统二级色本身就是"次要文字", + * 语义对得上,且**跟随主题**(不必再维护两个常量、不必登记)。 + */ + return Theme.textMuted; } // ─────────────── 遮罩与材质:交给系统 ───────────────