审查与构建始终保持正确的网站
一个网站不会因为某人审查过一次就一直正确。只有当每条规则都是一道检查、每道检查都在能阻止错误部署的环节运行、并且每道检查都曾被实际触发失败时,网站才能保持正确。以下所有内容都服务于这一目标。
这是一种方法,而非品牌指南。网站自身的规则(如语言区域、标点符号、名称写法、合作伙伴商标、保密内容、联系方式展示方式)应归入该项目的内部配置文件,通常名为 CLAUDE.md。在审查文案前,请先阅读项目的内部配置文件。如果没有,请主动创建一个(模板见 references/templates.md)。
内部配置文件属于单个网站。不要因为其他项目的规则、司法管辖区、时区或品牌恰好在你的上下文中,就将它们带入当前项目。请询问语言区域、法律管辖(适用哪部隐私法)、调度运行时区和受众,或明确说明你的假设。
两种模式共享同一循环:
- 审查:审计在线网站、分支、页面或他人发送的外部审计报告。
- 构建:启动新网站,或为现有网站添加页面、功能、CMS集合或重复内容类型。
每次变更的循环
-
先测量,在构建输出上,每个地方都测量。 构建网站并读取
dist/(或在线网站),每个页面,在多种宽度下。只检查一个URL的报告会遗漏真正存在问题的页面。将任何外部审计视为一组假设:在本方法来源的项目中,一份报告中的四个“必须处理”项目有三个未能通过测量(它将alt=""视为缺失,将来自两个导航的锚点归咎于卡片标记,并声称存在的结构化数据不存在)。 -
对发现的问题进行分类。 缺陷需要修复。编辑决策(文案、布局权衡、网格最后一行中的孤卡片、使用哪个标题措辞)则应向所有者提供可测量的选项,而非你的偏好。刻意不改的内容需记录原因,以便下一位审查者不会重新处理。在修改任何看起来异常的内容之前,请先阅读附近的代码注释——原因通常在那里。
-
从数据源处修复。 永远不要手动编辑生成的文件。必须能在重新生成后留存下来的修正,应放入生成器应用的规则层中,且该层应在锚定的文本消失时抛出错误。
-
添加一道本应能捕获该问题的检查,放在正确的关口(见下一节),读取构建输出而非测试夹具。然后添加一个控制用例:一个不应触发检查的邻近案例。
-
对检查进行破坏性测试。 破坏真实内容(在源代码中,或通过修改
dist/),运行检查,观察它是否因一条有用的消息而失败,恢复原状,观察它是否通过。一道无法失败的检查与一道实际通过的检查看起来完全一样。如果修改没有触发它,首先确认修改确实影响了检查读取的内容,然后修复检查。scripts/break-test.mjs可以安全地执行修改、运行、恢复循环。 -
重新生成派生数据并检查差异。 图像尺寸表、站点地图日期、搜索索引和类型文件会静默地变得陈旧。不是你造成的差异就是真正的差异,不是噪声。
-
发布,并观察它最终生效。 在依赖推送之前弄清楚它会部署什么(在许多主机上,推送到任何分支都会触发生产部署)。创建分支,合并,推送,然后观察 CI 直到结束,并确认在线网站提供了变更内容。不要仅凭本地构建就报告成功。
-
记录。 一条编号的记录行:变更内容、原因、涉及的文件、由哪道检查保持。也要记录撤回的发现和你自己的错误。格式见
references/templates.md。
关口:每道检查的归属位置
大多数团队假设 CI 会阻止错误部署。但通常并非如此:主机在推送时构建并部署,而 CI 同时运行;CMS 保存会触发部署,且完全没有 CI。为每条规则决定它需要哪个关口,并将该决定写在检查旁边。
| 关口 | 是否阻断部署? | 放置在此处的检查类型 |
|---|---|---|
| 构建/后构建(在宿主构建中运行) | 是 | 任何绝不能到达访问者的内容:泄露的密钥或个人数据、禁止的标点或拼写、商标形式、完整电子邮件地址、内容超出固定插槽长度。必须快速且无需浏览器。 |
对 dist/ 的静态验证(CI) | 否 | 链接、标题、元数据、结构化数据、站点地图、重定向、头文件、标记卫生、计数、可从 HTML 读取的无障碍性。 |
| 无头浏览器扫描(CI) | 否 | 多种宽度下的布局和溢出、截断、目标尺寸、计算样式、行为(轮播、展开面板、嵌入)。 |
| 在线检查(手动,在仪表板变更后) | 否 | 部署响应实际携带的内容,双向检查:声明但未提供,提供但未声明。仪表板规则可以重写仓库从未看到的标头。 |
| 真人设备人工检查 | 否 | 触摸手感、粘性悬浮、iOS :active、安全区域、滚动和交叉行为。 |
两项优化:
- 报告而非失败,当失败会惩罚编辑者时。 如果 CMS 保存必然产生某种结果(例如五篇帖子出现在四张卡片的行中,上传到错误路径),则报告它;因归档问题导致构建失败会让人学会忽略构建失败。语言、泄露和字节仍然会导致失败。
- CI 捕获但绝不能发布的检查,也需要在构建时设一道防护。 如果浏览器扫描发现超长内容破坏了布局,请添加一个廉价的构建时限制(字符数、数量),以便在 CI 仍在运行时,错误版本就无法部署。
如何编写持久有效的检查
- 读取构建输出,而不是测试夹具。 迭代手工维护列表的检查会静默地跳过之后添加的所有内容;在源项目中,所有 CMS 撰写的博客文章都因此未被检查。结构检查应基于
dist/**/index.html。 - 读取每个面向读者的字段:正文文本、
<title>、metacontent、alt、aria-label、title、placeholder。偏差往往存在于没人读取的属性中。 - 计算,而不是字符串匹配。 以像素为单位测量标题宽度(
scripts/title-width.mjs),根据文本顶部边缘的实际渐变 alpha 计算对比度,比较渲染后的几何形状。字符串匹配会因为不同值但相同缺陷而通过。 - 双向检查。 文件存在且页面声明了它;CMS 配置字段与内容模式;声明的标头与服务提供的标头;映射中的每个源路径确实被跟踪。
- 匹配模式,永远不要粘贴秘密本身。 一条包含保密条款本身的阻止列表本身就是泄露。定价模式需要在单词旁边附带数字,以便普通词汇通过。
- 在应用散文规则之前剥离
<script>和<style>,否则 JSON-LD@type和 CSScolor会触发拼写检查。对原始标记检查标点,因为共享的文本规范化工具通常会改写破折号和空格。 - 测量覆盖率。 让断言助手记录哪些断言已执行。从未执行的断言永远无法失败。
- 留出余量。 Linux CI 渲染的文本比 Windows 或 macOS 宽 2% 到 3%;本地仅差 1 像素的布局会在 CI 中失败,而 Android 的渲染效果类似于 CI。留出 20 像素或更多,并测试每个断点两侧的宽度,而不仅仅是 375/768/1280。
最坏情况内容
演示内容通常被无形地精心挑选,以看起来好看。在信任任何渲染可编辑或未来内容的组件(如活动幻灯片、卡片、面包屑、标题、CMS 字段)之前,请通过真实内容所使用的相同数据文件,向它输入合理的最坏情况:最长的真实名称、两行副标题、针对 2 或 4 列网格的数量 1、3 或 5、缺少的可选字段、超出面包屑分割长度的标题。不要提交该测试夹具。然后:
- 明显可见的断裂,在组件中修复(让可选行消失,使用换行而非截断);
- 无法让步的内容,通过构建时限制和清晰的错误消息来保持;
- 属于设计选择的内容(孤卡片、缺失图像),询问所有者。
在源项目中,这发现了文本在每个宽度下都延伸到标题 150 像素之外,以及面包屑丢失空格的错误,而这两点在当日的真实内容中均不可见。
你无法在代理会话中验证的内容
代理和无头浏览器在 visibilityState 为隐藏的状态下渲染:IntersectionObserver 从不触发,懒加载图像从不加载,动画时间线始终为零。它们无法通过合成触摸进行滚动,且 focus() 在未聚焦的文档中不会触发焦点事件。因此,永远不要就滚动触发行为、懒加载或触摸手感从会话中得出结论。暂存变更,明确声明它尚未确认,并给用户一个具体的检查操作(对于懒加载,使用无痕窗口,因为普通重新加载会从缓存中绘制)。更多内容见 references/tooling-gotchas.md。
审查模式:交付内容
以结论开头。然后列出发现,最严重的优先,每条附带其测量依据。将你无法重现的声明保留在发现之外,放在它们自己的简短列表中,并附上你所测量的结果。使用 references/templates.md 中的报告格式。对于每条发现,说明其检查应属于哪个关口。如果用户只要求结论,在几行中给出,并提供完整报告。
当被要求处理第三方审计报告时,在修复任何内容之前测量每条声明的真伪,并报告哪些声明成立、哪些不成立以及审计遗漏了什么。
构建模式:准备工作
对于新网站,在第一页之前搭建好检查框架,因为事后添加意味着要审计所有内容两次。references/build-baseline.md 列出了上线基线:主机和 URL 决策(规范形式、重复来源关闭、404 服务、不可变资源)、四个检查关口、安全标头和 CSP(不使用 'unsafe-inline')、CMS 模式一致性、移动端和动效基线,以及内部配置文件。
对于现有网站上的新页面或功能,运行上述循环,扩展现有检查而非编写并行检查,并对任何渲染可编辑内容的组件运行最坏情况测试。
动效与触摸(简要)
默认减少动效。仅在提供反馈、保持空间连续性、显示状态变化或掩盖突兀变化时进行动画,并将 UI 动效控制在 300 毫秒以内。拒绝滚动揭示淡入、数字计数、跑马灯和每个页面视图上的入场动画。将悬浮效果限制在 (hover: hover) and (pointer: fine) 条件下;为触摸提供 :active 按压状态;每个动效都需要一个减少动效路径和一个能立即显示内容的降级方案。在提出两个事物之间的过渡之前,确认这两个事物在多个页面上都存在。详情见 references/audit-checklist.md(移动端、触摸与动效)。
参考文件
在需要时读取所需的文件:
references/audit-checklist.md:按领域(SEO、无障碍性、性能、安全与隐私、内容风格、布局、CMS、移动端与动效)的完整检查目录,每条包括捕获的问题、所属关口以及促使该检查存在的陷阱。在任何审查之前以及添加检查时阅读。references/build-baseline.md:新网站的上线首日设置。在启动或重新搭建网站时阅读。references/tooling-gotchas.md:工具和环境陷阱,可能耗费数小时。在信任浏览器测量、图像工具或含有正则表达式转义的脚本之前阅读。references/judgement-calls.md:看似错误实则正确的决策,以及如何区分缺陷与编辑决策。在“修复”看似奇怪的内容之前阅读。references/templates.md:记录行、审查报告、所有者决策和内部配置文件模板。
脚本位于此技能自身的 scripts/ 文件夹中,而非项目中。从那里运行它们,或在设置检查时将它们复制到项目中;不要将它们描述为仓库中已经存在。当在报告中引用测量结果时,说明你是如何获得的。需要 Node 18+,无依赖项:
scripts/title-width.mjs:Google 桌面搜索结果中标题的像素宽度,附带针对 Chrome 测量的自测试。node title-width.mjs "Page label · Brand" [--suffix " · Brand"]。scripts/break-test.mjs:修改文件,运行检查命令,确认其因预期消息失败,无论发生什么都恢复文件。使用--help运行。