impeccable harden 实战指南:以错误处理、i18n 与边界用例把界面加固到生产就绪
本文以 impeccable 技能套件中的 harden 参考文档 为骨架展开,讲解该命令的核心命题:只依赖"完美数据"的设计不是生产就绪的设计。你会掌握从需求评估、七大加固维度(文本溢出、国际化、错误处理、边界条件、输入校验、无障碍、性能)到测试与验证的完整闭环,并通过仓库源码与测试夹具了解 impeccable 是如何把这类加固检查固化下来的。
什么是 harden:在 impeccable 中的定位
harden 是 impeccable 技能套件 Refine(精修) 类别下的一个命令,用于把界面打磨到"生产可用"。在 skill/SKILL.src.md 的命令总表中,它的定位如下:
| 命令 | 类别 | 描述 | 参考文档 |
|---|---|---|---|
harden [target] |
Refine | Production-ready: errors, i18n, edge cases | reference/harden.md |
对应的命令元数据(skill/scripts/command-metadata.json)给出它的触发条件与参数形式:
- 描述:"Make interfaces production-ready: error handling, i18n, text overflow, edge case management, and resilience under real-world data."
- 使用场景:当用户要求 harden、make production-ready、处理边界用例、补充错误状态,或修复溢出(overflow)与 i18n 问题时使用;
- 参数提示:
[target],即可以像harden [目标页面/组件]这样限定作用域。
同目录下与它协同的参考文档还有 polish(最终质量过检)、audit(技术质量体检)、onboard(首次使用与空状态)等;harden 完成边界用例覆盖后,会把工作交接给 /impeccable polish 做最后一轮质检(详见 polish 参考)。
该技能同时以多套 harness 目录分发(如 .claude/skills/impeccable/reference/harden.md、.cursor/skills/impeccable/reference/harden.md、.qoder/skills/impeccable/reference/harden.md),以及可供打包分发的 plugin/skills/impeccable/reference/harden.md。不同 harness 使用不同的命令前缀占位符,例如 SKILL 源文件中的
{{command_prefix}}impeccable polish会被替换为各环境实际的前缀,文档正文则统一写作/impeccable polish。
第一步:系统性评估加固需求(Assess Hardening Needs)
加固不是凭感觉修修补补,而是先刻意制造"坏情况"来暴露弱点。harden 文档把评估分为三类。
1. 用极端输入测试
| 输入类别 | 具体用例 |
|---|---|
| 超长文本 | 很长的名字、描述、标题 |
| 超短文本 | 空串、单个字符 |
| 特殊字符 | emoji、RTL 文本、带重音符号的字母 |
| 超大数字 | 百万、十亿级别 |
| 海量条目 | 1000+ 列表项、50+ 下拉选项 |
| 无数据 | 空状态(empty states) |
2. 测试错误场景
- 网络失败:离线(offline)、慢速(slow)、超时(timeout);
- API 错误:400、401、403、404、500;
- 校验错误(validation errors);
- 权限错误(permission errors);
- 限流(rate limiting);
- 并发操作(concurrent operations)。
3. 测试国际化(i18n)
- 长译文:德语通常比英语长 30%;
- RTL 语言:阿拉伯语、希伯来语;
- 字符集:中日韩(CJK)、emoji;
- 日期/时间格式;
- 数字格式(1,000 与 1.000 的差异);
- 货币符号。
CRITICAL 原则:只依赖完美数据才能正常工作的设计不是生产就绪的设计——要对现实做加固(Harden against reality)。
加固的七大维度
评估暴露问题后,就进入系统化的加固。harden 文档给出了七个相互独立的加固维度。
维度一:文本溢出与换行(Text Overflow & Wrapping)
长文本处理的三种 CSS 策略:
/* 单行省略号 */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行截断(clamp 到 3 行) */
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 允许正常换行 */
.wrap {
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
}
Flex/Grid 溢出:Flex 与 Grid 子项默认的最小尺寸取决于内容,一旦内容超长就会把容器撑破。解决方案是把 min-width: 0(Flex 允许子项缩小到内容尺寸以下)与 overflow: hidden 配合,Grid 子项则同时设 min-width: 0 与 min-height: 0:
/* 防止 flex 子项溢出 */
.flex-item {
min-width: 0; /* 允许缩小到内容尺寸之下 */
overflow: hidden;
}
/* 防止 grid 子项溢出 */
.grid-item {
min-width: 0;
min-height: 0;
}
响应式字号:
- 用
clamp()实现流式排版(fluid typography); - 设置最小可读字号:移动端正文 16px 起——这与 impeccable 排版指南给出的底线一致;14px 只允许用于真正次要的文本。注意:iOS Safari 会对小于 16px 的聚焦输入框强制放大(force-zoom),这会破坏表单布局;
- 用 200% 缩放测试文本缩放;
- 确保容器能随文本内容扩展,而不是反向截断。
维度二:国际化(i18n)
文本膨胀(Text expansion):
- 为译文预留 30%~40% 的额外空间预算;
- 用能自适应内容的 flexbox/grid;
- 用最长的语言(通常是德语)做测试;
- 不要在文本容器上使用固定宽度。
// ❌ 错误:假设永远是短英文
<button className="w-24">Submit</button>
// ✅ 正确:自适应内容
<button className="px-4 py-2">Submit</button>
RTL(从右到左)支持:核心是改用 CSS 逻辑属性(logical properties),让布局随 dir 自动镜像,而不是写死物理方向:
/* 使用逻辑属性 */
margin-inline-start: 1rem; /* 而不是 margin-left */
padding-inline: 1rem; /* 而不是 padding-left/right */
border-inline-end: 1px solid; /* 而不是 border-right */
/* 或通过 dir 属性处理镜像元素 */
[dir="rtl"] .arrow { transform: scaleX(-1); }
字符集支持:
- 全站统一 UTF-8 编码;
- 用中日韩(CJK)字符测试;
- 测试 emoji(单个 emoji 可能占 2~4 字节);
- 覆盖不同书写系统:拉丁、西里尔、阿拉伯等。
日期/时间与数字格式化:不要手写拼接,交给标准 Intl API,它会按 locale 输出正确格式:
// ✅ 使用 Intl API 做正确格式化
new Intl.DateTimeFormat('en-US').format(date); // 1/15/2024
new Intl.DateTimeFormat('de-DE').format(date); // 15.1.2024
new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(1234.56); // $1,234.56
复数(Pluralization)规则:英文只有单复数两种形态,但斯拉夫语系、阿拉伯语等有复杂得多的复数分类。字符串模板拼接在全球化场景下必然出错,应交给 i18n 库处理:
// ❌ 错误:假设英文复数规则
`${count} item${count !== 1 ? 's' : ''}`
// ✅ 正确:使用真正的 i18n 库
t('items', { count }) // 内部处理复杂复数规则
维度三:错误处理(Error Handling)
网络错误:清晰说明发生了什么,给出重试入口,能离线就用离线模式,并单独处理超时:
// 带恢复路径的错误状态
{error && (
<ErrorMessage>
<p>Failed to load data. {error.message}</p>
<button onClick={retry}>Try again</button>
</ErrorMessage>
)}
表单校验错误:字段旁就近展示内联错误;信息要具体明确;给出修正建议;不要无谓阻塞提交;出错时保留用户已输入内容(不要整表单清空)。
API 错误码的对应处理:
| 状态码 | 应呈现的状态 |
|---|---|
| 400 | 展示校验错误 |
| 401 | 跳转登录 |
| 403 | 展示权限错误 |
| 404 | 展示"未找到"状态 |
| 429 | 展示限流提示 |
| 500 | 展示通用错误并提供支持入口 |
优雅降级(Graceful degradation):核心功能在无 JavaScript 时也可用;图片带 alt 文本;采用渐进增强;为不支持的新特性提供回退方案。
维度四:边界条件(Edge Cases & Boundary Conditions)
- 空状态(Empty states):列表无条目、无搜索结果、无通知、无数据可展示时,都要给出明确的下一个动作,而不是一片空白。
- 加载状态(Loading states):覆盖初次加载、分页加载、刷新;说明正在加载什么("Loading your projects...");长操作用时间估算。
- 大数据集(Large datasets):用分页或虚拟滚动、提供搜索/筛选、做性能优化——绝不要把 10,000 条数据一次性全部加载。
- 并发操作(Concurrent operations):加载中禁用按钮防止重复提交;处理竞态条件;乐观更新必须带回滚;要有冲突解决策略。
- 权限状态(Permission states):无查看权限、无编辑权限、只读模式,都要有清晰的界面表达并说明原因。
- 浏览器兼容(Browser compatibility):为现代特性准备 polyfill;为不支持的 CSS 提供回退;用特性检测而非浏览器检测;在目标浏览器上逐一测试。
维度五:输入校验与净化(Input Validation & Sanitization)
客户端校验:必填、格式校验(邮箱/电话/URL)、长度限制、正则匹配、自定义规则。
服务端校验(永远要做):绝不要只信任客户端校验;所有输入都必须服务端校验与净化;防护注入攻击;服务端实施限流。
用 HTML 约束属性把规则声明出来——约束本身就是对用户最直接的引导:
<!-- 声明清晰的约束 -->
<input
type="text"
maxlength="100"
pattern="[A-Za-z0-9]+"
required
aria-describedby="username-hint"
/>
<small id="username-hint">
Letters and numbers only, up to 100 characters
</small>
注意 aria-describedby 把提示文本与输入框关联起来,兼顾了约束说明与无障碍需求。
维度六:无障碍韧性(Accessibility Resilience)
- 键盘导航:所有功能可纯键盘操作;Tab 顺序符合逻辑;模态框内要有焦点管理;长内容提供跳转链接(skip links)。
- 屏幕阅读器支持:正确的 ARIA 标签;用 live regions 播报动态变化;有描述性的 alt 文本;使用语义化 HTML。
- 高对比模式:在 Windows 高对比模式下测试;不要只靠颜色传达信息;提供替代视觉线索。
维度七:性能韧性(Performance Resilience)
慢速连接:渐进式图片加载、骨架屏(skeleton screens)、乐观 UI 更新、用 Service Worker 做离线支持。
内存泄漏:组件卸载时清理事件监听器、取消订阅、清除定时器/interval、abort 未完成的请求。
节流与防抖(Throttling & Debouncing):高频事件必须限频:
// 搜索输入防抖
const debouncedSearch = debounce(handleSearch, 300);
// 滚动处理节流
const throttledScroll = throttle(handleScroll, 100);
测试策略:把加固做进回归防线
手工测试清单:
- 用极端数据测(超长、超短、空);
- 用不同语言测;
- 离线测试;
- 限速到 3G 模拟慢连接;
- 用屏幕阅读器测;
- 纯键盘导航测;
- 在旧浏览器上测。
自动化测试:
- 针对边界用例的单元测试;
- 针对错误场景的集成测试;
- 关键路径的 E2E 测试;
- 视觉回归测试;
- 无障碍测试(axe、WAVE 等工具)。
值得强调的是,impeccable 自身在设计流程上就要求"有界通过、而非死循环"式的验证(见 skill/SKILL.src.md 核心原则):完整构建 → 一次性批量检查(桌面 + 移动端)→ 一次性批量修复 → 最多再确认一轮即停止。这种收敛式验证同样适用于 harden——避免用无休止的自我 QA 消耗成本。
仓库中也有与这些加固主题对应的自动化反模式夹具可供参考:例如 tests/fixtures/antipatterns/text-overflow.html、tests/fixtures/antipatterns/body-text-viewport-edge.html(正文触碰视口边缘规则)、tests/fixtures/antipatterns/clipped-overflow-container.html(裁剪溢出容器规则),而 tests/live-browser-ignores.test.mjs 等测试则验证了 clipped-overflow-container 这类规则与 ignore 机制的配合。可见"溢出/裁切/可读性"这些 harden 关注点,在 impeccable 的检测引擎里是有 fixture 化回归保障的。
绝对不要做的清单(NEVER)
harden 文档在最后给出了一组硬性禁令,可以作为走查时的红线:
- 假设输入完美无缺——一切都要校验;
- 忽视国际化——为全球用户而设计;
- 错误提示写得笼统敷衍(例如干巴巴的 "Error occurred");
- 忘记离线场景;
- 只信任客户端校验;
- 文本容器用固定宽度;
- 假设文本长度都是英文级别;
- 一个组件报错就把整个界面阻塞掉(错误隔离,而不是全屏白屏)。
IMPORTANT:加固的本质是预期意外(expecting the unexpected)。真实用户一定会做出你从未想象过的事。
验证加固效果(Verify Hardening)
改完之后不要口头宣称"已加固",而是逐项实测:
- 长文本:试试 100+ 字符的名字;
- Emoji:在所有文本字段里塞入 emoji;
- RTL:用阿拉伯语或希伯来语测试;
- CJK:用中文/日文/韩文测试;
- 网络问题:断开网络、限速连接;
- 大数据集:用 1000+ 条数据测试;
- 并发操作:连续快速点击提交 10 次;
- 错误:强制触发 API 错误,把每种错误状态都过一遍;
- 空数据:删光数据,测试所有空状态。
当边界用例覆盖完毕后,harden 文档给出的收尾动作是:交接给 /impeccable polish 做最终质检(polish 的完整流程见 skill/reference/polish.md)——harden 负责"面对真实世界也能撑住",polish 负责"上线前的最后一公里细节"。
延伸阅读:把 harden 放进 impeccable 的完整工作流
harden 不是孤立的一步,它处在 impeccable 的技能编排中。想了解它如何被触发与路由,可以继续阅读:
- 命令总表与"Refine 精修"类别定位:skill/SKILL.src.md(
harden条目位于命令表中,与polish、bolder、quieter、distill、onboard并列); - 命令触发条件与参数元数据:skill/scripts/command-metadata.json;
- 精修类命令的共同语义——"精修是保留,重做是替换":Refine 保留现状的形态、行为、文案与范围外的一切,改动前先询问用户,这与 skill/SKILL.src.md 中的设计原则一致;
- 无参数调用时的上下文路由(如何判断当前该不该推荐 harden):skill/reference/routing.md;
- 质量底线与绝对禁令(编辑 UI 前必读):skill/reference/craft-floor.md。
一句话总结本篇:如果你的设计只有在"数据完美、网络畅通、用户讲英语"时才成立,那它还没有生产就绪。照着 harden 参考文档 的评估清单、七大加固维度与验证清单走一遍,配合自动化反模式夹具做回归,再交给 polish 收尾——这才算完成了对真实世界的加固。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0631
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
video-shotcraftAI宣传片skill,使用 Remotion 制作电影级产品视频:提供106 张镜头配方卡和可复用的视频魔板。适用于 Claude Code 与 Codex以及所有其他智能体Markdown00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python09
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00