fix(webui): 导航改"按主题的玻璃"(浅色白玻璃+深字)+ 去掉整屏闪的入场动画
用户两句话:
「那你为什么不把白字换成黑字或者自动反色或者描边呢?」
「部分动画十分不合理,会导致页面大范围的闪动,且不能让人自然的把注意力集中在
将要出现的页面上」
## ① 导航:他说得对,我之前是用错误的方式解决对比度
这个应用是**浅色底 + 深字**,导航却是全页唯一一块黑的 —— 那才是"割裂"。我上一轮
为了"白字对比度"把它做成深玻璃,等于**把一块地方永久压黑**来回避问题。
现在导航底色/文字全部走令牌,按主题切换:
浅色:白玻璃 rgb(255 255 255 / .72) + 深字 #475569 → 对比度 7.48:1
深色:深玻璃 rgb(15 23 42 / .72) + 亮字 #94a3b8 → 自动反色
组件侧改成 `.nav-rail` / `.nav-item[data-active]` 令牌类(Sidebar 与 NarrowNav 同一套)。
同时删掉壁纸模式里写死的深玻璃规则 —— 那条正是"为什么还是黑色"。
## ② 动画:整面板入场删掉
先是从"所有面板一起动"改成"只动主内容区",试下来仍然不对:页面级淡入会把
**已经在那儿的框架**也一起暗一下,观感还是闪。所以这一档整体删掉,只保留局部、
有明确语义的动效(日历翻页、菜单展开)。实测切视图时 `animatedOnSwitch = 0`。
骨架不动,注意力自然落在变化的那块内容上。
## ③ 一条我自己的误判(值得记下)
深色主题下我量到导航文字对比度只有 2.36,一度以为是变量/级联的问题,还写死了一份
深色字面值。**那是误判**:`.nav-item` 有 `transition: color .15s`,我在切主题后
**立刻**读 computed color,拿到的是过渡的**起点**(浅色值)。决定性证据是连内联
`style.color` 都"改不动"它 —— 级联不可能这样,只可能是还在过渡中。等 400ms 再量就正常。
写死的那份已撤掉,并在 CSS 里留下说明;新判据 `test/manual/nav-contrast-verify.mjs`
用 WCAG 比值量对比度(不是"颜色是不是黑的"),每次读之前等 400ms。
背景套件 32 条全绿(含"导航走令牌 + 两套令牌都在")。
This commit is contained in:
@ -447,6 +447,7 @@ function Header({
|
||||
onThread: () => void;
|
||||
}) {
|
||||
const time = new Date(mail.created_at).toLocaleString('zh-CN');
|
||||
const narrow = useIsNarrow();
|
||||
|
||||
// 发件/收件行显示**各方在这条会话里的完整地址**,人与 Agent 带的段数不同:
|
||||
//
|
||||
@ -888,6 +889,11 @@ function ReplyBar({
|
||||
|
||||
if (!replyTo) return null;
|
||||
|
||||
// 判据是「当前登录用户名」而不是字面量 'human':后者是单用户时代的遗留,
|
||||
// 多用户下登录名可能是 jianf,判据恒为假 → 对端取成自己 → 信发给自己。
|
||||
const peer = mailCounterpart(replyTo, me);
|
||||
const target = overrideTarget || mailReplyTarget(replyTo, me);
|
||||
|
||||
// 窄屏收起态:一个悬浮球,点开才出现输入区。
|
||||
//
|
||||
// 窄屏上「顶部邮件信息 + 底部输入框」同时常驻会把可读区压成一条缝(用户实测反馈)。
|
||||
@ -908,11 +914,6 @@ function ReplyBar({
|
||||
);
|
||||
}
|
||||
|
||||
// 判据是「当前登录用户名」而不是字面量 'human':后者是单用户时代的遗留,
|
||||
// 多用户下登录名可能是 jianf,判据恒为假 → 对端取成自己 → 信发给自己。
|
||||
const peer = mailCounterpart(replyTo, me);
|
||||
const target = overrideTarget || mailReplyTarget(replyTo, me);
|
||||
|
||||
const send = async () => {
|
||||
if (!body.trim()) return;
|
||||
setBusy(true);
|
||||
|
||||
@ -79,9 +79,8 @@ export default function NarrowNav() {
|
||||
<button
|
||||
key={short}
|
||||
onClick={() => setViewMode(isComm ? commTab : mode)}
|
||||
className={`relative flex-1 py-2 flex flex-col items-center justify-center gap-0.5 transition-colors ${
|
||||
active ? 'text-white' : 'text-chrome-400 active:bg-chrome-800'
|
||||
}`}
|
||||
data-active={active}
|
||||
className="nav-item relative flex-1 py-2 flex flex-col items-center justify-center gap-0.5"
|
||||
>
|
||||
<Icon />
|
||||
<span className="text-3xs leading-none">{short}</span>
|
||||
@ -106,11 +105,8 @@ export default function NarrowNav() {
|
||||
|
||||
<button
|
||||
onClick={() => setViewMode('account')}
|
||||
className={`flex-1 py-2 flex flex-col items-center justify-center gap-0.5 ${
|
||||
viewMode === 'account' && !composing
|
||||
? 'text-white'
|
||||
: 'text-chrome-400 active:bg-chrome-800'
|
||||
}`}
|
||||
data-active={viewMode === 'account' && !composing}
|
||||
className="nav-item flex-1 py-2 flex flex-col items-center justify-center gap-0.5"
|
||||
>
|
||||
<div className="relative">
|
||||
<PersonIcon />
|
||||
|
||||
@ -61,7 +61,7 @@ export default function Sidebar() {
|
||||
const logout = useAuthStore(s => s.logout);
|
||||
|
||||
return (
|
||||
<div className="w-[60px] h-full shrink-0 flex flex-col items-center py-3 gap-1 bg-chrome-900"
|
||||
<div className="nav-rail w-[60px] h-full shrink-0 flex flex-col items-center py-3 gap-1"
|
||||
style={{ paddingBottom: 'calc(0.75rem + env(safe-area-inset-bottom))' }}
|
||||
>
|
||||
{/*
|
||||
@ -77,7 +77,7 @@ export default function Sidebar() {
|
||||
onClick={() => setViewMode('inbox')}
|
||||
title="AgentMail"
|
||||
aria-label="AgentMail 首页"
|
||||
className="w-10 h-10 shrink-0 mb-1 rounded-xl bg-white/10 text-white flex items-center justify-center hover:bg-white/15 active:bg-white/20 transition-colors"
|
||||
className="nav-item w-10 h-10 shrink-0 mb-1 rounded-xl flex items-center justify-center"
|
||||
>
|
||||
<BrandMarkIcon className="w-5 h-5" />
|
||||
</button>
|
||||
@ -98,11 +98,8 @@ export default function Sidebar() {
|
||||
key={short}
|
||||
onClick={() => setViewMode(target || commTab || modes[0])}
|
||||
title={title}
|
||||
className={`relative w-12 h-12 rounded-lg flex flex-col items-center justify-center gap-0.5 transition-colors ${
|
||||
active
|
||||
? 'bg-chrome-700 text-white'
|
||||
: 'text-chrome-400 hover:bg-chrome-800 hover:text-chrome-100'
|
||||
}`}
|
||||
data-active={active}
|
||||
className="nav-item relative w-12 h-12 rounded-lg flex flex-col items-center justify-center gap-0.5"
|
||||
>
|
||||
<Icon />
|
||||
<span className="text-3xs leading-none">{short}</span>
|
||||
@ -135,11 +132,8 @@ export default function Sidebar() {
|
||||
<button
|
||||
onClick={() => setViewMode('account')}
|
||||
title={`${user?.display_name || user?.username}(点击管理账号)`}
|
||||
className={`relative w-9 h-9 rounded-full flex items-center justify-center text-2xs font-semibold transition-colors ${
|
||||
viewMode === 'account' && !composing
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-chrome-700 text-chrome-200 hover:bg-chrome-600'
|
||||
}`}
|
||||
data-active={viewMode === 'account' && !composing}
|
||||
className="nav-item relative w-9 h-9 rounded-full flex items-center justify-center text-2xs font-semibold"
|
||||
>
|
||||
{(user?.display_name || user?.username || '?').slice(0, 2)}
|
||||
{/* 连接状态点:不遮挡文字,贴在右下角 */}
|
||||
|
||||
Reference in New Issue
Block a user