跨端: 补齐「我的」页深色可读性(4 页 219 段全 ≥3:1)+ 判据自己漏报的那一类
上一条把通信/日历/联系扫干净了,但**「我的」页没扫到**
(扫描脚本按文案点不到它 —— 那是侧栏**底部的头像按钮**,不是导航项)。
补上后立刻又抓出问题,并把判据自身的**漏报形状**一并修了。
## 一、「我的」页两处真 bug
1. **主题分段按钮 `SettingsPage.ets:834`**:
`.fontColor(... ? Theme.surface : Theme.textPrimary)` —— `surface` 是
**会翻转的面色**,压在 `Theme.accent` 蓝底上,深色下变成近黑。
设备读数:`深色 2.93:1 ink rgb(32,34,36) bg rgb(34,96,228)`。
2. **权限档徽标 `MailDetailPage.ets:391`**:同一个错法(第 13 处)。
3. **账号名 `SettingsPage.ets:1179`**:三元里的裸 `Theme.accent`
压在 `accentSoftFor()` 底上 ⇒ `jianf 2.83:1`。
`AdminUsersPage.ets:386` 同形状(角色徽标)。
## 二、判据自己的漏报形状(比 bug 本身更值得记)
静态防线(`C|会翻转的 Resource 不得当前景色`)**在 bug 存在时是绿的**。
原因是它的提取正则:
/\.fontColor\(Theme\.([A-Za-z0-9_]+)\)/ ← 要求令牌是**唯一实参**
于是**三元里的令牌全被漏掉**:
.fontColor(this.appearanceTheme === t ? Theme.surface : Theme.textPrimary)
改成"在 `fontColor(` 之后的整段实参里找所有 `Theme.X`"之后,
它**立刻报出上面第 1、2 两处**(此前一直绿)。
★ **判据漏报的常见形状是它自己的正则太窄,而不是被测代码太隐蔽。**
这条写成注释留在判据里了。
## 三、我自己犯的批量替换错误(已加判据钉住)
把裸 `Theme.accent` 换成 `accentFor()` 时,**误把 6 处 `backgroundColor` 也换了**。
`accentFor()` 深色给浅蓝 `#80AFF9`,而搭档前景是白色 `accentFg`
⇒ 白字压浅蓝 ≈ **1.4:1**,主按钮文字会彻底看不见。
- 6 处已逐处还原(复核:`grep -c "backgroundColor(Theme.accentFor())"` = 0)。
- 新增判据 **`C2`**:`accentFor / dangerFor / approveFor / warnFgFor / textSubtleFor`
**只能用于前景**,`backgroundColor(Theme.XFor(...))` 直接判红。
(`accentSoftFor` 是例外 —— 它本来就是"面"。)
★ 修法不是"下次小心点":批量替换一定会再犯,**一行判据把它变成不可能**。
## 四、设备复扫(修后)
```
[通信] 扫 42 段,低对比 0
[日历] 扫 85 段,低对比 0
[联系人] 扫 51 段,低对比 0
[我的] 扫 41 段,低对比 0 ← 新增
```
**219 段文字全部 ≥3:1**(本轮全部针对深色)。
## 五、底本
`baseline.sha` 第 5 次重算。重算前**专门复核**了"那 6 处误改有没有残留"
(不只看 `git diff` 非空就放行):`backgroundColor(accentFor)` 计数为 0、
`git diff HEAD~1` 里 backgroundColor 只有 calendar 那一处(有意改动)。
`run-all.mjs` → `checks=515 pass=515 fail=0 skip=0 red=0 broken=0 unreported=0`。
**未验**:浅色主题下的对比度没扫(本轮全部针对深色)。
This commit is contained in:
@ -948,6 +948,55 @@ test('★ 品牌浅底必须有深色变体(深色下白底卡片 = 刺眼的
|
||||
'\n改成 Theme.accentSoftFor(this.isDarkNow)(页面要有一个 isDarkNow 状态)');
|
||||
});
|
||||
|
||||
test('C2|`*For()` 入口只能用于**前景**,不得当背景(我批量替换时真犯过)', () => {
|
||||
/*
|
||||
* ★★ 这条是给**我自己**写的 —— 2026-09-19 我批量把裸 `Theme.accent`
|
||||
* 换成 `Theme.accentFor()` 时,把**6 处 `backgroundColor` 也一起换了**。
|
||||
*
|
||||
* 为什么那是 bug:`accentFor()` 在深色下返回**浅蓝** `#80AFF9`,
|
||||
* 而它的搭档前景是 `accentFg`(白)——
|
||||
* 白字压浅蓝底 ≈ 1.4:1,主按钮上的文字会**彻底看不见**。
|
||||
*
|
||||
* ★ 修法不是"下次小心点":批量替换一定会再犯。
|
||||
* 在 `backgroundColor(Theme.XFor(...))` 这个形状上直接判红,
|
||||
* 代价一行,收益是这类错误不可能再出现。
|
||||
*
|
||||
* 适用对象:所有 `*For()` 主题自适应入口 —— 它们的返回值都是
|
||||
* **为前景调过亮的**(`accentFor` / `dangerFor` / `approveFor` / `warnFgFor`)。
|
||||
* `accentSoftFor` 是例外:它本来就是**面**(浅底),当背景才是对的。
|
||||
*/
|
||||
const FG_ONLY = ['accentFor', 'dangerFor', 'approveFor', 'warnFgFor', 'textSubtleFor'];
|
||||
const files = [];
|
||||
const walkDir = (d) => {
|
||||
for (const e of readdirSync(d, { withFileTypes: true })) {
|
||||
const p = join(d, e.name);
|
||||
if (e.isDirectory()) walkDir(p);
|
||||
else if (e.name.endsWith('.ets')) files.push(p);
|
||||
}
|
||||
};
|
||||
walkDir(HARMONY_ETS);
|
||||
|
||||
const bad = [];
|
||||
for (const f of files) {
|
||||
const src = prose(f);
|
||||
const rel = f.replace(HARMONY_ETS, '');
|
||||
src.split('\n').forEach((ln, i) => {
|
||||
for (const name of FG_ONLY) {
|
||||
if (new RegExp(`backgroundColor\\(\\s*Theme\\.${name}\\(`).test(ln)) {
|
||||
bad.push(`${rel}:${i + 1} ${ln.trim().slice(0, 90)}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
assert.deepStrictEqual(bad, [],
|
||||
'★ 这些地方把**为前景调亮的** `*For()` 入口当成背景色用了:\n ' + bad.join('\n ') + '\n' +
|
||||
' 它们返回的是"压在这类底上的字色"(深色下更亮),当**底**会把上面的文字吞掉\n' +
|
||||
' (实测形状:`accentFor()` 深色给 #80AFF9,白字压上去 ≈1.4:1)。\n' +
|
||||
' 当背景用**不带 For 的那个**:`Theme.accent` / `danger` / `approve` / `warnFg`\n' +
|
||||
' (WebUI 的实心按钮底 `--s-*` 两个主题同值,跟着变会让主按钮失去视觉重量)。');
|
||||
});
|
||||
|
||||
test('★ 设备:品牌色**真的画成那个色**(令牌写对了 ≠ 渲染对了)', async (t) => {
|
||||
/*
|
||||
* 补的是 `run-all.mjs` 的 `STATIC_ONLY` 登记里说的那个缺口:
|
||||
@ -1120,6 +1169,20 @@ test('★ 设备:品牌色**真的画成那个色**(令牌写对了 ≠ 渲
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 两边都用**且都合理**的令牌 —— 逐条给理由,别只列名字。
|
||||
*
|
||||
* ★ 目前**空**:这类令牌满足"会翻转的 Resource"且两边都用的组合,
|
||||
* 本身就很可疑(那正是 `Theme.surface` 出问题的形状)。
|
||||
*
|
||||
* ★ 已审过、**不需要**列在这里的(说明为什么它们不该进这张表):
|
||||
* · `accent` / `danger` / `approve` / `warnFg` —— 两边都用是正常设计
|
||||
* (蓝底白字主按钮 + 白底蓝字返回箭头),而且它们是**字符串常量**,
|
||||
* 不跟随主题翻转 ⇒ 不满足条件 A,判据本来就不会碰它们。
|
||||
* · `textPrimary` / `textMuted` / `textSubtle` —— 只当字色,不满足条件 B。
|
||||
*/
|
||||
const ALLOW_BOTH_BG_AND_FG = new Set([]);
|
||||
|
||||
test('C|「会跟随主题翻转的 Resource」不得当前景色(含矛盾信号)', () => {
|
||||
/*
|
||||
* ★★ 这是设备判据(上面那条)抓到的 bug 的**静态防线** ——
|
||||
@ -1198,11 +1261,32 @@ test('C|「会跟随主题翻转的 Resource」不得当前景色(含矛盾
|
||||
for (const m of ln.matchAll(/backgroundColor\(Theme\.([A-Za-z0-9_]+)\)/g)) {
|
||||
if (!asBg.has(m[1])) asBg.set(m[1], where);
|
||||
}
|
||||
for (const m of ln.matchAll(/\.fontColor\(Theme\.([A-Za-z0-9_]+)\)/g)) {
|
||||
if (!asFg.has(m[1])) asFg.set(m[1], where);
|
||||
/*
|
||||
* ★★ 提取时**不能要求令牌是唯一实参** —— 这一版原来用
|
||||
* `\.fontColor\(Theme\.(\w+)\)`,于是**三元里的令牌全被漏掉**:
|
||||
*
|
||||
* .fontColor(this.appearanceTheme === t ? Theme.surface : Theme.textPrimary)
|
||||
*
|
||||
* 实测代价:`SettingsPage.ets:834` 正是这个形状(`surface` 当字色压在
|
||||
* `accent` 蓝底上),而判据**照样绿** —— 直到设备扫描报出
|
||||
* `深色 2.93:1 ink rgb(32,34,36) bg rgb(34,96,228)` 才发现。
|
||||
*
|
||||
* ⇒ 改成"在 `fontColor(` 之后的**整段实参**里找所有 `Theme.X`"。
|
||||
* 判据漏报的形状往往就是它**自己的正则太窄**,而不是被测代码太隐蔽。
|
||||
*/
|
||||
for (const m of ln.matchAll(/\.fontColor\(/g)) {
|
||||
const rest = ln.slice(m.index + m[0].length);
|
||||
/* 只取到本行结束(这些调用都在一行内闭合) */
|
||||
for (const t of rest.matchAll(/Theme\.([A-Za-z0-9_]+)/g)) {
|
||||
if (!asFg.has(t[1])) asFg.set(t[1], where);
|
||||
}
|
||||
}
|
||||
for (const m of ln.matchAll(/iconColor:\s*Theme\.([A-Za-z0-9_]+)/g)) {
|
||||
if (!asFg.has(m[1])) asFg.set(m[1], where);
|
||||
/* `iconColor: X` 同理 —— 冒号后整段都可能有三元 */
|
||||
for (const m of ln.matchAll(/iconColor:/g)) {
|
||||
const rest = ln.slice(m.index + m[0].length);
|
||||
for (const t of rest.matchAll(/Theme\.([A-Za-z0-9_]+)/g)) {
|
||||
if (!asFg.has(t[1])) asFg.set(t[1], where);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user