跨端: 语义色/三级文字在深色下没提亮(设备扫出 12 处)+ 判据取样法修到第三版
## 一、判据基建先修对,否则是在听噪声
深色可读性扫描的**取样法迭代了三版**,每版都因为**假红**才改的
(记在这里,因为"判据自己错"比"界面错"更费时间):
1. **取中心一个像素** ⇒ 落在笔画之间、读到的是底色 ⇒ 一片 ratio=1.00 假红。
2. **取全局最暗/最亮** ⇒ 会采到**两个不同的东西**:细字的抗锯齿中间色。
实例:segmented「日」报 ink `rgb(32,34,35)` / bg `rgb(98,100,101)` 2.69:1 ——
而截图里它要么蓝底白字、要么深底浅字,两种都远高于 3:1。
**反复核对才确认是判据的错,不是界面的错。**
3. **步长采样 + 众数** ⇒ 仍然假红:周表头「一」报 2.03:1,
而逐像素的真实墨是 `rgb(166,167,167)`(**6.6:1**)——
稀疏网格**整个跳过了笔画**。这不是调参能修好的(字越小越糟)。
4. **逐像素 + 众数=底 + 离底最远者=墨**(最终版)。
理由:一个文字框里**面积最大的一定是底**,而"离底色最远"就是笔画最实那部分。
⚠️ 逐像素是**负担得起的**(整屏已解码在内存,一个框最多 9 万像素)——
**不要再为了省开销退回抽样**,前两版都因此假红。
## 二、真 bug 三批(都是"深色下没提亮")
### ① 语义色前景(12 处 → 修完设备复扫 0)
WebUI 的语义色也是**双通道**,`.dark` 段把 `red/green/amber-700` 换成浅色
(`248/118/249 → 164/219/195` 那组)。鸿蒙只有一个深色值:
- 「归档」按钮 `Theme.danger` #B91C1C 压深面 ⇒ **2.47:1**(联系人页 7 处)
- 「今天」/「日」等 ⇒ **2.38–2.77:1**
加 `dangerDark/approveDark/warnFgDark`(逐字对齐 WebUI `.dark`)
+ `dangerFor()/approveFor()/warnFgFor()` 入口,22 处接入。
### ② 日历工具条整块配色抄错(不是"深色档选错")
对照 WebUI `CalendarView.tsx:313-331`:
| 元素 | WebUI | 鸿蒙(改前) |
|---|---|---|
| 「今天」 | `border-gray-300` + `text-gray-700`(**中性**) | `accentSoft` 底 + `accentStrong` 字 |
| 月/周/日 未选中 | `bg-white` + `text-gray-700` | `accentSoft` 底 |
鸿蒙把**次要的中性按钮**画成了**品牌色按钮** —— 视觉重量跟主操作一样,
三个档看起来"都像选中"。已按 WebUI 改成中性。
### ③ 三级文字在深色下 2.82:1(**71 处**,全局最大的一个)
WebUI 的 `.dark` 段注释逐字写着这个坑:
> 「`gray-400/500`(次要文字)→ **提亮**。深底上的浅色 gray-400
> 只有约 2:1 对比度,远低于 WCAG AA 的 4.5:1 —— **看得见但读不动**。」
鸿蒙用的系统三级色 `ohos_id_color_text_tertiary` 深色下是 `rgb(102,103,105)`
压深面 **2.82–3.05:1**,而它被用在 10–11px 的**小字**上
(时间戳、农历、空态、辅助说明)。
**不换掉系统令牌**("系统拥有的维度"纪律照旧),加 `textSubtleFor()`:
深色下改用二级色(实测 `rgb(166,167,167)` ≈ 7.15:1,
且与 WebUI 的 `gray-500` 同档位)。71 处接入。
**设备复扫(修后)**:通信/日历/联系三页 **176 段文字全部 ≥3:1**。
## 三、欠账与底本
- `baseline.sha` 第 4 次重算。仍**先逐个取证**:
`git diff --quiet HEAD -- <f> && echo STALE || echo INTENTIONAL`
⇒ 三个全是 `INTENTIONAL`(我改的),不是变异残留。
把这条取证命令也写进文件,下次照抄。
- `dangerDark/approveDark/warnFgDark` 已登记进 `SELF_OWNED_COLORS`
+ `Theme.ets` 登记表(各带理由与 WebUI 出处)。
`run-all.mjs` → `checks=514 pass=514 fail=0 skip=0 red=0 broken=0 unreported=0`。
**未验**:①「我的」页(底部头像按钮,扫描脚本没找到入口)没扫;
② 浅色下的对比度没扫(本轮全部针对深色)。
This commit is contained in:
@ -409,7 +409,7 @@ struct InboxTab {
|
||||
Row() {
|
||||
Text(this.accountFilter === 'all' ? '全部邮箱' : this.accountName)
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtle })
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtleFor() })
|
||||
.rotate({ angle: 90 })
|
||||
.margin({ left: 3 })
|
||||
}
|
||||
@ -456,7 +456,7 @@ struct InboxTab {
|
||||
.fontColor(this.accountFilter === acct.id ? Theme.accent : Theme.textPrimary)
|
||||
.fontWeight(this.accountFilter === acct.id ? FontWeight.Bold : FontWeight.Normal)
|
||||
Text(acct.username)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.layoutWeight(1)
|
||||
@ -483,13 +483,13 @@ struct InboxTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.mails.length === 0) {
|
||||
Column() {
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -568,7 +568,7 @@ struct InboxTab {
|
||||
* `rotate` 留在图标上(转的是箭头本身,不是那 20vp 的槽位)。
|
||||
*/
|
||||
Stack({ alignContent: Alignment.Center }) {
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtle })
|
||||
AmIcon({ iconName: 'chevronRight', iconSize: 12, iconColor: Theme.textSubtleFor() })
|
||||
.rotate({ angle: this.isExpanded(g.key) ? 90 : 0 })
|
||||
}
|
||||
.width(20)
|
||||
@ -579,7 +579,7 @@ struct InboxTab {
|
||||
.fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).layoutWeight(1)
|
||||
Text(compactMailTime(g.latest?.created_at ?? ''))
|
||||
.fontSize(10).fontColor(Theme.textSubtle).margin({ left: 8 })
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor()).margin({ left: 8 })
|
||||
}
|
||||
.width('100%')
|
||||
Row() {
|
||||
@ -600,7 +600,7 @@ struct InboxTab {
|
||||
Text(g.alias.length > 0 ? '.' + g.alias : '(未命名会话)')
|
||||
.fontSize(10).fontColor(Theme.accentFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).layoutWeight(1)
|
||||
Text(g.mails.length + ' 封').fontSize(10).fontColor(Theme.textSubtle).margin({ left: 8 })
|
||||
Text(g.mails.length + ' 封').fontSize(10).fontColor(Theme.textSubtleFor()).margin({ left: 8 })
|
||||
}
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -639,14 +639,14 @@ struct InboxTab {
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
Blank()
|
||||
Text(compactMailTime(mail.created_at)).fontSize(10).fontColor(Theme.textSubtle)
|
||||
Text(compactMailTime(mail.created_at)).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
/*
|
||||
@ -668,7 +668,7 @@ struct InboxTab {
|
||||
*/
|
||||
if (mail.cc_count > 0) {
|
||||
Text('抄送 ' + mail.cc_count)
|
||||
.fontSize(10).fontColor(Theme.textSubtle)
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
.margin({ top: 3 })
|
||||
}
|
||||
}
|
||||
@ -811,7 +811,7 @@ struct SentTab {
|
||||
.fontSize(13).fontWeight(FontWeight.Medium).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
Text(mail.created_at).fontSize(10).fontColor(Theme.textSubtle)
|
||||
Text(mail.created_at).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
@ -846,7 +846,7 @@ struct SentTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
Blank()
|
||||
Text(g.mails.length + ' 封').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(g.mails.length + ' 封').fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -868,7 +868,7 @@ struct SentTab {
|
||||
Text('发件箱').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
if (this.loaded > 0) {
|
||||
Text(this.loaded + ' 封').fontSize(12).fontColor(Theme.textSubtle)
|
||||
Text(this.loaded + ' 封').fontSize(12).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 16, right: 16 })
|
||||
@ -899,7 +899,7 @@ struct SentTab {
|
||||
Column() { LoadingProgress().width(32).height(32) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.danger) }
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.dangerFor()) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.groups.length === 0) {
|
||||
/*
|
||||
@ -908,9 +908,9 @@ struct SentTab {
|
||||
* 副句说清这一栏是干什么的。
|
||||
*/
|
||||
Column() {
|
||||
AmIcon({ iconName: 'sent', iconSize: 36, iconColor: Theme.textSubtle }).margin({ bottom: 8 })
|
||||
AmIcon({ iconName: 'sent', iconSize: 36, iconColor: Theme.textSubtleFor() }).margin({ bottom: 8 })
|
||||
Text(emptyTitle('sent')).fontSize(15).fontColor(Theme.textMuted)
|
||||
Text(emptyHint('sent')).fontSize(12).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(emptyHint('sent')).fontSize(12).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1092,12 +1092,12 @@ struct PermissionTab {
|
||||
|
||||
if (req.context.length > 0) {
|
||||
Text(req.context)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(3).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 4 })
|
||||
}
|
||||
|
||||
Text(req.created_at).fontSize(10).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(req.created_at).fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
|
||||
if (this.noteFor === req.mail_id) {
|
||||
TextInput({ placeholder: '备注(会随决策一起送给 Agent)', text: this.noteText })
|
||||
@ -1113,7 +1113,7 @@ struct PermissionTab {
|
||||
Text('拒绝')
|
||||
.fontSize(13).height(36).layoutWeight(1)
|
||||
.textAlign(TextAlign.Center)
|
||||
.backgroundColor(Theme.dangerBg).fontColor(Theme.danger)
|
||||
.backgroundColor(Theme.dangerBg).fontColor(Theme.dangerFor())
|
||||
.borderRadius(Theme.radiusControl)
|
||||
// 拒绝先展开备注框,而不是直接拒:拒绝往往要说明理由,而理由是给模型看的
|
||||
.onClick(() => {
|
||||
@ -1130,7 +1130,7 @@ struct PermissionTab {
|
||||
|
||||
if (this.noteFor === req.mail_id) {
|
||||
Text(this.noteText.length > 0 ? '再点一次「拒绝」即送出(带备注)' : '可填备注,再点一次「拒绝」送出')
|
||||
.fontSize(10).fontColor(Theme.textSubtle).margin({ top: 4 })
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 4 })
|
||||
}
|
||||
}
|
||||
.width('100%').alignItems(HorizontalAlign.Start)
|
||||
@ -1182,13 +1182,13 @@ struct PermissionTab {
|
||||
Column() { LoadingProgress().width(32).height(32) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.danger) }
|
||||
Column() { Text(this.error).fontSize(13).fontColor(Theme.dangerFor()) }
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.requests.length === 0) {
|
||||
Column() {
|
||||
AmIcon({ iconName: 'shield', iconSize: 36, iconColor: Theme.textSubtle }).margin({ bottom: 8 })
|
||||
AmIcon({ iconName: 'shield', iconSize: 36, iconColor: Theme.textSubtleFor() }).margin({ bottom: 8 })
|
||||
Text(emptyTitle('permissions')).fontSize(15).fontColor(Theme.textMuted)
|
||||
Text(emptyHint('permissions')).fontSize(12).fontColor(Theme.textSubtle).margin({ top: 6 })
|
||||
Text(emptyHint('permissions')).fontSize(12).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1756,13 +1756,13 @@ struct ContactsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Text(this.error).fontSize(14).fontColor(Theme.dangerFor())
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.contacts.length === 0) {
|
||||
Column() {
|
||||
Text('暂无联系人').fontSize(16).fontColor(Theme.textSubtle)
|
||||
Text('暂无联系人').fontSize(16).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.contactView === 'card') {
|
||||
@ -1873,7 +1873,7 @@ struct ContactsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.sessionMails.length === 0) {
|
||||
Column() {
|
||||
Text('这条会话没有邮件').fontSize(14).fontColor(Theme.textSubtle)
|
||||
Text('这条会话没有邮件').fontSize(14).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -1887,7 +1887,7 @@ struct ContactsTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Blank()
|
||||
Text(compactMailTime(m.created_at))
|
||||
.fontSize(10).fontColor(Theme.textSubtle)
|
||||
.fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
Text(m.subject)
|
||||
@ -1897,7 +1897,7 @@ struct ContactsTab {
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%').margin({ top: 4 })
|
||||
Text(m.body_preview)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%').margin({ top: 2 })
|
||||
}
|
||||
@ -1935,7 +1935,7 @@ struct ContactsTab {
|
||||
.fontSize(13).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Text(c.path)
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ left: 6 }).layoutWeight(1)
|
||||
if (c.unread_count > 0) {
|
||||
@ -1981,7 +1981,7 @@ struct ContactsTab {
|
||||
|
||||
Row() {
|
||||
Text(c.mail_count + ' 封 · ' + shortTimeOf(c.last_activity))
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
Blank()
|
||||
/*
|
||||
* 权限档位徽标:**档位 + 强制力标记**,点它弹出"平台实际做到了什么"。
|
||||
@ -2048,11 +2048,11 @@ struct ContactsTab {
|
||||
AmIcon({
|
||||
iconName: iconName,
|
||||
iconSize: 12,
|
||||
iconColor: tone === 'danger' ? Theme.danger : Theme.textMuted
|
||||
iconColor: tone === 'danger' ? Theme.dangerFor() : Theme.textMuted
|
||||
})
|
||||
Text(label)
|
||||
.fontSize(11)
|
||||
.fontColor(tone === 'danger' ? Theme.danger : Theme.textMuted)
|
||||
.fontColor(tone === 'danger' ? Theme.dangerFor() : Theme.textMuted)
|
||||
.margin({ left: 4 })
|
||||
}
|
||||
.height(26)
|
||||
@ -2078,7 +2078,7 @@ struct ContactsTab {
|
||||
.maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.width('100%')
|
||||
Text('对应 Agent 的 session 将被归档,此列表与邮箱界面同时移除')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.margin({ top: 3 })
|
||||
.width('100%')
|
||||
Row() {
|
||||
@ -2147,7 +2147,7 @@ struct ContactsTab {
|
||||
* 于是同一个联系人在两种视图里给出的关键信息不一致。
|
||||
*/
|
||||
Text(c.mail_count + ' 封 · ' + shortTimeOf(c.last_activity))
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.fontSize(11).fontColor(Theme.textSubtleFor())
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
}
|
||||
.width('100%').margin({ top: 3 })
|
||||
|
||||
Reference in New Issue
Block a user