前端质量
任何面向用户的Web UI的质量标准。硬性要求(无障碍访问、性能、移动优先),而非风格偏好。审查能够捕捉到对真实用户至关重要的错误——缓慢的页面、损坏的键盘导航、加载时布局偏移。
模型路由
当模型需要评判或重写UI、UX、视觉层次、无障碍文案或交互细节时,使用操作员的前端偏好:
source "$WALTER_OS_HOME/scripts/walter/lib/model-router.sh"
frontend_model=""
walter_model_resolve frontend frontend_model
默认偏好为Claude。操作员可以在~/.config/walter-os/overlay/personal.env中将别名指向其他LiteLLM路由。
该技能在触及前端的差异处自动触发。如需从零开始构建着陆页,请使用landing-page-fast(包含这些规则但提供脚手架)。
为何这对本操作员尤为重要
- [Project A] 用户是阿根廷的SMB供应商——许多人使用手机,经常处于3G/4G网络,连接不稳定。网站慢=用户流失。WCAG很重要,因为部分用户技术素养低且有视力障碍。
- [Project B] 用户包括残障患者。WCAG不是“锦上添花”——而是可用性要求。
- [Company] 文档 由资深开发者阅读,他们会在遇到加载缓慢或键盘操作损坏的网站时立即离开。
无障碍(WCAG 2.2 AA — 必需)
键盘
- 每个交互元素均可通过Tab键到达。按钮、链接、表单字段、自定义控件。手动进行Tab遍历;如果任何控件被跳过,则为阻塞项。
- 焦点可见。
:focus-visible轮廓必须明显。不要在没有替代方案的情况下设置outline: none。 - 逻辑Tab顺序。阅读顺序与DOM顺序匹配;避免使用
tabindex值>0(它们会破坏自然流程)。 - 跳转链接。每个页面上的第一个可聚焦元素是“跳转到主内容”。
- Escape键关闭弹窗。在弹窗关闭前,焦点锁定在弹窗内部。
语义化HTML
<button>用于操作,<a>用于导航。永远不要使用<div onClick>替代。- 标题层级分明:每个页面一个
<h1>;<h2>...<h6>不跳级。 - 地标:
<header>、<nav>、<main>、<footer>。仅在没有语义元素适用时使用ARIA角色。 - 列表使用
<ul>/<ol>。不要使用<div>模拟列表。 - 表单控件有
<label>。for属性与id匹配。
颜色和对比度
- WCAG AA对比度最小值:正文文本4.5:1,大文本(18pt+或14pt+加粗)3:1,UI组件和图形3:1。
- 颜色不是唯一指示符:错误状态显示图标+文字+颜色,而不仅仅是红色边框。
- 使用模拟色盲测试。使用Stark、axe DevTools或浏览器开发者工具中的色觉模拟器。
表单
- 每个输入都有可见标签。占位符不是标签。
- 错误消息通过
aria-describedby程序化关联。 - 必填字段已标记。视觉上(
*或“必填”)和程序化地(aria-required="true"或required)都标记。 - 验证错误在获得焦点时不消失。持续存在直到更正或关闭。
- 成功状态已确认。提交后,屏幕阅读器用户知道操作已成功。
图片和媒体
- 每个
<img>都有alt属性。装饰性图片留空(alt="");内容性图片提供描述。生成的AI图片:描述图片中实际内容,而非提示词。 - 视频/音频有字幕/文字记录。
- 没有自动播放声音。
- 动画尊重
prefers-reduced-motion:@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
工具
- 开发模式下使用
@axe-core/react— 将无障碍问题记录到控制台。 eslint-plugin-jsx-a11y— 在构建时捕获问题。- 发布前进行手动屏幕阅读器测试:VoiceOver (Mac)、NVDA (Windows)、TalkBack (Android)。
- CI中Lighthouse无障碍评分≥95。
性能 — 核心网页指标
谷歌排名的3个指标。通过全部三个才能被评为“良好”:
LCP(最大内容绘制)— 加载速度
目标:3G网络下< 2.5s。对于[Project A]的实际应用场景更严格,因为阿根廷移动网络慢于中位数。
方法:
- 预加载LCP图片:
<link rel="preload" as="image" href="...">。 - 无阻塞渲染的资源:延迟非关键JS,内联关键CSS。
- 自托管字体,使用
font-display: swap或使用系统字体堆栈。 - CDN用于静态资源:Vercel、Cloudflare R2、Bunny.net。
- HTTP/2或HTTP/3:Vercel/Cloudflare默认。
- 压缩图片:WebP/AVIF,根据视口调整大小。
- 特别优化首屏图片:它几乎总是LCP元素。
INP(与下一次绘制的交互)— 响应性
目标:p75 < 200ms。2024年取代FID。
方法:
- 交互期间主线程无超过50ms的长任务。
- 延迟水合(Astro islands、Next.js streaming、qwik)。
- Web Workers用于繁重计算。
- 防抖/节流输入处理程序。
- 避免使用
dangerouslySetInnerHTML处理大字符串 — 解析会阻塞。 - 仅在测量后使用
useMemo/memo,而非预先使用。
CLS(累计布局偏移)— 视觉稳定性
目标:< 0.1。
方法:
- 图片和视频标签包含
width+height(浏览器会预留空间)。 - 为广告/嵌入内容预留空间,使用min-height。
- 不在现有内容上方插入内容:通知横幅放在折叠下方或通过transform向下推,而不是在DOM上方插入。
- 网页字体:使用
font-display: swap+size-adjust匹配度量,或者如果可以接受使用font-display: optional。 - 骨架屏尺寸与真实内容匹配。
打包体积
- 初始JS预算:着陆页gzip后< 150KB,应用外壳< 250KB。
- 按路由代码分割(Next.js、Astro、SvelteKit默认支持)。
- 对繁重的非关键依赖使用动态导入(图表、编辑器)。
- 审计依赖:使用
bundle-analyzer或next-bundle-analyzer。删除你只从中导入了一个组件的10MB UI库。 - Tree-shaking正常工作:只导入所需内容(
import { thing } from 'lib'而不是import lib from 'lib')。
加载模式
- 预连接到所需源:
<link rel="preconnect" href="...">。 - 悬停/聚焦时预取可能的下一个页面(Next.js支持)。
- 折叠下方图片懒加载:
loading="lazy"。 - iframe懒加载:
loading="lazy"。 - Service Worker实现离线优先(在适用场景 — [Project A]受益,营销页面则不需要)。
状态 — 每个组件处理全部五种
对于任何数据驱动的UI,如果任何状态未处理,则为阻塞项:
- 加载 — 骨架屏或微调器。不是空白屏幕。不是布局偏移。
- 空 — 首次使用、无数据状态。带有填充数据的行动号召。
- 错误 — 清晰的消息、重试按钮、不向用户显示堆栈跟踪。记录到Sentry。
- 部分/过时 — 数据来自缓存或部分获取时。
- 成功 — 正常路径。
列表示例:
if (isLoading) return <ListSkeleton />
if (error) return <ErrorState onRetry={refetch} />
if (data.length === 0) return <EmptyState cta={...} />
return <List items={data} />
如果你的组件在差异中只显示了成功状态,请问:其他4个状态在哪里?
移动优先
- 首先设计360px宽,然后向上扩展。
- 触摸目标≥ 44×44px(Apple HIG)或48×48px(Material)。
- 没有仅悬停交互:悬停时可用的任何功能也有焦点/触摸路径。
- 在真实设备或chrome开发者工具节流模式下测试:3G网络、4倍CPU降速。
- 表单无需缩放即可使用:输入框
font-size: 16px+(否则iOS在聚焦时会缩放)。
工具参考
CI / 合并前:
- Lighthouse CI,每个指标有预算。
- **
@axe-core/cli**或Lighthouse无障碍子集。 bundlewatch,使打包体积超出预算的PR失败。- 视觉回归:Chromatic(Storybook)、Percy或Playwright截图检查。
本地:
- Chrome DevTools Performance + Lighthouse面板。
- **
unlighthouse**用于全站审计。 - **
@unjs/check-vitals**用于一次性核心网页指标检查。
输出格式
**[BLOCKING|WARN|NIT] [FE-N] <一行说明>**
Where: `path/to/Component.tsx:42-58`
Category: <a11y | perf | states | mobile | semantic>
Impact: <哪个用户 / 场景 / 指标>
Fix: <具体变更,如果合适可包含代码>
Verify: <如何确认 — Lighthouse运行 / 手动测试 / 其他>
严重程度:
- BLOCKING:违反WCAG AA、核心流程损坏、核心网页指标处于“差”范围。必须修复。
- WARN:最佳实践差距、尚未对用户可见的性能回退。
- NIT:改进机会(更小的打包体积、更好的骨架屏)。
硬性规则
- 永远不要使用
<div onClick>作为按钮。 始终遵守。 - 永远不要使用没有
alt属性的图片。 始终遵守。 - 永远不要因真实内容导致布局偏移。 始终预留空间。
- 永远不要自动播放带声音的内容。 始终遵守。
- 尊重
prefers-reduced-motion。 始终遵守。
该技能不包括的内容
- 后端(请使用
web-security-baseline)。 - 数据库 schema / 查询(请使用
data-migration-safety)。 - 品牌标识 / 视觉设计(请使用
brand-creation)。 - 新建着陆页构建(请使用
landing-page-fast,它会提供脚手架)。
参考资料
- WCAG 2.2 快速参考
- web.dev Core Web Vitals 文档
- web.dev 性能学习路径
- Heydon Pickering 著《Inclusive Components》
- The A11y Project 检查清单