来源:notes/documents/网页版前端首轮体检与优化实施计划.md

网页版前端首轮体检与优化实施计划

背景与范围

2026-09-22 对线上 https://vite-react-tawny-one-14.vercel.app 做了首轮前端体检:Playwright (Chromium 1134,走代理,本机直连该域名 DNS 被污染)采集五个状态、两种系统主题、三种宽度, 逐张核对截图后定结论。

这一轮范围覆盖未登录访客能看到的那一面。笔记列表与正文在 GitHub OAuth 之后,本机没有 Neon 连接串、代码里也没有口令旁路,当时没有采集到,因此对笔记区只作代码级观察。

同日补了第二轮:用本地夹具重建登录态,把笔记面采集下来(前端是 npm run build 真产物、接口是 backend/src/lib/notes.ts 真身、数据是 notes.json 真产物 155 篇,只伪造 /api/me 身份与鉴权)。 结论写在 notes/documents/网页版笔记面体检报告.md,本文的 P5 据此改写。

体检结论

坏掉级(一处)

亮色系统主题下,登录页的说明文字完全不可见:.muted 是写死的 rgba(255,255,255,.6), 落在白底上对比度 1:1。访客只看到「Folio」加一个按钮,不知道为什么要点它。

亮色主题下说明文字不可见

同一屏在暗色系统主题下是正常的(.muted 约 6.5:1),问题只在亮色。

暗色主题下的同一屏

错误提示也踩同一条线:.error#ff6b6b 在白底上约 2.8:1,暗色下约 5.6:1 才达标。

亮色主题下的授权取消提示

移动端与窄屏同样中招,且 320px 无横向溢出(scrollWidth == clientWidth == 320)。

移动端亮色

320px 窄屏

结构性问题(不改不影响使用,但拖着会越来越贵)

  • web/frontend/src/index.css 还是 Vite 模板原样:body { display: flex; place-items: center }、 写死的 #242424。整站布局实际靠 App.css 里的 text-align: left 反着覆盖模板行为。
  • App.css.logo@keyframes logo-spin.read-the-docs 是死代码,assets/react.svg 也没人引用。
  • 颜色全是散落的 rgba(100,108,255,…) 字面量,没有 token;只有 800px 一个断点。
  • favicon 是 Vite 默认 logo(/vite.svg),浏览器标签页写 Folio,品牌不一致。
  • 只有一个身份源,文案却写「请用下面任一方式登录」——「任一」在只有一项时是空承诺。
  • 登录页 0 个 landmark(没有 main)、0 个 live region;页面唯一动作是个没有任何身份信号的中性按钮。
  • 未登录的 GET /api/me 返回 401(body 是 {"authenticated":false}),每次访问都往 console 打一条红色错误,对开发者工具和前端错误监控都是噪音。

明确没问题的部分

未登录时 /api/notes/api/notes/search/api/notes/detail 一律 401 unauthorized,不泄露 任何标题与路径;OAuth 跳转参数完整正确(redirect_uri 指生产域名、state 在、 allow_signup=false);键盘焦点实测可见(Tab 一次落在按钮上,暗色下是白环)。

键盘焦点可见

实施计划

P0 修亮色主题 —— 已完成 2026-09-22(提交 dac8324

  • 文件:web/frontend/src/index.cssweb/frontend/src/App.css
  • 做法:index.css 收成 token 层,按定版设计的五个角色各给亮暗两套值——素纸 #F1F3F4 / 夜纸 #14171A、靛墨 #22303C / #E6E8EA、淡墨 #5C6B78 / #9AA5AE、 靛青 #2F5D8C / #7FA8D4、朱砂 #B4442F / #E0705C。默认素纸,系统偏好为暗色时整组 换夜纸,不再靠一处半套覆盖;App.css 全部改用 var(--…),散落的 rgba(128,128,128,…)#646cff#ff6b6b 一并收掉。
  • 同根的三处一并修掉:.note-item-meta.note-item-excerpt 原先用 opacity 稀释墨色 (折算 4.27:1),改用淡墨角色;当前项原先铺靛青底色,把行内淡墨压到 4.03:1,改成设计里 写的「当前项用朱砂记号、不铺底色」;.error 由白底 2.78:1 改成墨色文字加朱砂非文字记号。
  • 验收(实测,非估算):本地夹具逐元素算对比度(含 alpha 合成与祖先 opacity), 451 个文本节点 × 亮暗两套 × 笔记面与登录页,四个面 0 失败,最低 4.93:1(素纸上的淡墨)、 暗色 7.17:1,console 0 错误。生产侧公开登录页(走代理)复测:素纸解析为 rgb(241,243,244)、 夜纸 rgb(20,23,26),四段文字 4.93–14.65:1 全过。
  • 一条未达的自定阈值:淡墨落在素纸上是 4.93:1,低于设计写的「淡墨至少 5:1」,差 0.07 (WCAG AA 的 4.5:1 已过)。要么微调 #5C6B78,要么把阈值写成 4.5,等和设计一起拍板。

修好之后的公开登录页,亮暗两套:

修好后的亮色登录页(生产)

修好后的暗色登录页(生产)

以上两张是 2026-09-22 生产环境实拍(push main 后自动部署,20 秒内落地)。

P1 清 Vite 模板残留 —— 已完成 2026-09-22(提交 356f8a0,收尾 4187e10

  • 文件:web/frontend/src/index.cssweb/frontend/src/App.css,并删 assets/react.svg
  • 做法:去掉 body 的 flex 居中与 #rootmax-width / padding / text-align, 布局交回 .card / .app-header / .notes-layout;删 .logologo-spin.read-the-docs
  • 验收:npm run build 通过,登录页与笔记页布局都不塌。
  • 收尾:frontend/src/assets/react.svg 当时没删(4187e10 才删掉),那一次同时把 favicon 从 /vite.svg 换成 Folio 标记 —— 也就是说 P2 的图标那半拖到了 4187e10 才补上。

P2 品牌面 —— 已完成 2026-09-22(字体与配色 356f8a0,标记 4187e10

  • 做法:定一个 Folio 的标记,替换 /vite.svg 作为 favicon,并在登录页复用同一个标记。 审美方向用 frontend-design 定,别直接套默认字体与默认蓝紫。
  • 验收:标签页图标不再是 Vite;登录页出现可辨识的品牌信号。
  • 落地:字体与配色在 P0 的 token 层就位(宋体标题、五个角色、无卡片无阴影), 标记本身 2026-09-22 才补(4187e10)——frontend/public/folio.svg:素纸底上一本对开本, 靛墨页框加一道页边缝、缝上一颗朱砂墨点(外壳的「脊 + 当前项记号」缩小版), 文件内带 prefers-color-scheme: dark 媒体查询,暗色下的整组值换夜纸。登录页标题旁复用同一个标记。 实测:link[rel=icon] 指向 /folio.svg(200 + image/svg+xml),16 / 32 / 64px 两套主题都能认出来。

P3 登录页语义与措辞 —— 已完成 2026-09-22(提交 1cd1d9d7c478e9

  • 文件:web/frontend/src/pages/Login.tsxweb/frontend/src/App.tsx
  • 做法:登录卡片包一层 main;错误与状态提示加 role="alert";只有 GitHub 一个身份源时, 把「请用下面任一方式登录」改成直述,并让按钮带上 GitHub 身份信号。
  • 验收:landmark ≥ 1、live region ≥ 1;文案在只有一个 provider 时不出现「任一」这类空承诺。

P4 对齐 /api/me 的状态码语义 —— 已完成 2026-09-22(提交 190469d

  • 文件:api/me.ts(及前端 web/frontend/src/auth/useSession.ts
  • 做法:未登录返回 200 加 {"authenticated":false},与 body 已经表达的语义一致。
  • 验收:首屏 console 不再出现红色 401;useSession 的未登录分支保持原行为。

P5 笔记区体检(已用本地夹具覆盖)

第二轮用本地夹具重建登录态,覆盖了列表、详情、搜索、亮色切主题、材料类笔记与 390px 移动端。 完整结论见 notes/documents/网页版笔记面体检报告.md,要点:

  • P5.1 正文泄漏 front matter 并重复一级标题(坏掉级,108 篇受影响)。已修(ticket 02,3fb9508 web/scripts/generate-notes.mjstoNote() 把整份文件原文塞进 bodynotes/documents/ 下 108 篇全部带 YAML front matter,于是详情页顶部渲染出 note_id: … content_type: …, 后面再跟一个 51.2px 的重复 H1(Vite 模板的 h1 { font-size: 3.2em })。同一个 bug 让搜索摘录 也被 front matter 占满。做法:剥 front matter 并把正文起点挪到第一个 # 之后(同时用于 bodyexcerpt),改完重新生成 notes.json。验收:随机抽 3 篇文档类笔记,正文首行不再是 note_id:,详情页只有一个同名标题。
  • P5.2 亮色主题下三处文字仍然 1:1,与 P0 同根。已修(P0 dac8324 + 外壳 token 化 356f8a0 .note-detail-head p(路径/大小/更新时间)、 .notes-count155 篇笔记 / 命中 52 篇)、.muted(头部用户名)在亮色下整行消失。 P0 的 token 化必须把这三处一起收进去,别只修登录页。
  • P5.3 正文度量与字体与定版设计冲突。已修(ticket 05,356f8a0 实测正文块宽 956px、字体 Inter、16px/27.2px; 设计笔记要求约 640px(34 个汉字)、正文思源宋体、最小 17px。
  • P5.4 列表排序把材料类条目推满首屏。已修(ticket 03 / 05,356f8a0 books/*/source_materials 一屏占 7/9 条, 材料与解读分不出身份。归到外壳改造(索引层)一起做,别单独调排序。
  • 已确认正常:header.app-header / aside.notes-sidebar / nav.notes-list / main.note-detail 四个 landmark 齐全,侧栏粘性定位生效,搜索防抖后结果与命中数一致,390px 无横向溢出。
  • 当时未覆盖、2026-09-22 补测完毕的:
    • 空搜索结果:索引头显示「搜索结果 0」,索引里一句话「没有匹配的笔记。」,不塌。
    • 超长查询:?q= 1000 字 → 400
    • 笔记不存在:?m=notes&n=documents/不存在.md → 页里写「产物里没有这篇笔记:documents/不存在.md」 (role=alert),不是白屏也不是英文状态码。
    • 代码块与表格横向溢出:在 390px 与 768px 下量过 —— pre 自己横滚(overflow-x: auto)、 表格按 100% 宽排开,页面本身没有横向滚动
    • 笔记内图片:ticket 12 已修,线上不再裂。
    • 平板与窄屏宽度:外壳的 900px 断点、860px 与 420px 的月历/正文都截过图。
  • 仍未覆盖:非 Chromium 内核(Safari / Firefox)、屏幕阅读器、200% 缩放、强制色模式、 已登录态的移动端横屏。

验证方式

  • 每阶段改完走 frontend-testing-debugging 的渲染验证闭环;本机 Browser 插件缺失,按规矩回退 Playwright 并在报告里记明原因。
  • 复现采样的命令形态:Playwright 启动时带 proxy: { server: 'http://127.0.0.1:7890' }, 否则本机解析不到 *.vercel.app
  • 部署走 web/ 下的 npm run deploy;push 到 main 已自动部署。

不做的事

  • 不引入 UI 框架与组件库:这个前端一共 744 行,引依赖的收益远小于运维成本。
  • 不动 vite-react 这个 Vercel 项目名、不动 OAuth 回调注册、不改部署方式。
  • 不把笔记正文或图片搬进数据库或函数包:笔记只在构建期从 notes/ 生成。
  • 不为了好看改掉「界面叫 Folio、文档叫个人助理」这个刻意的不一致。

两条已知限制

  • 这篇文档里的图片在仓库预览与飞书 Docs 里可见,但网页版不提供笔记内图片,线上会裂。 已解决(ticket 12,f505ee7 / 2924f97:构建期把笔记引用的图片拷进静态产物 (5 篇笔记 / 33 张 / 5.1 MiB,落在 frontend/public/note-assets/),正文 URL 改写成 /note-assets/…,渲染侧只放行这条前缀与 https:;被拒绝的引用(缺文件、越界、非图片) 构建日志各报一行、页面上不渲染。
  • 本轮只测了 Chromium 一种内核;屏幕阅读器、200% 缩放、强制色模式、Safari / Firefox 都未覆盖。