关于 harden
harden 是一项设计技能,帮助界面应对边缘情况、错误、本地化需求和真实使用环境。它面向准备发布产品流程的团队,涵盖错误恢复、无障碍、权限、媒体及高压使用场景。
主要功能
- 01测试极端输入与错误场景
- 02提升本地化和 RTL 适应性
- 03设计清晰的错误恢复路径
- 04完善权限与高影响操作
- 05处理加载、空状态和媒体问题
- 06检查无障碍与高压使用情境
适用场景
- 01让表单适应多样的用户输入
- 02改善网络与 API 错误恢复
- 03加固账户、权限或管理流程
- 04适配翻译文本和 RTL 布局
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ aladicf/ better-react-web-ui --skill harden安装后在 Claude Code 中输入/harden确认已加载。
详细介绍
增强界面,使其能够应对边界情况、错误、国际化问题,以及会破坏理想化设计的真实使用场景。
将边界情况视为设计输入,而不是在顺畅流程已经上线后才进行清理。
设计错误、警告、成功和信息状态时,请查阅语义颜色参考文档,确保语义颜色清晰明确,而非纯粹装饰。 强化通知流程、动态消息、摘要或提醒设置,以避免用户疲劳和过度打扰时,请查阅状态沟通参考文档。 强化用户上传的媒体、截图、图标缩放或图像溢出行为时,请查阅图像处理参考文档。 任务涉及验证行为、摘要、严格验证器、可恢复的字段错误,或因恢复设计不佳导致的放弃操作时,请查阅错误恢复参考文档。 强化登录、会话过期、密码设置、多因素身份验证、账号锁定或账号恢复流程时,请查阅身份验证与账号恢复参考文档。 强化角色编辑器、访问权限申请流程、403 错误恢复、管理员与成员界面、功能边界或高风险权限变更时,请查阅权限与角色 UX参考文档。 强化语言选择器、区域覆盖设置、货币或配送偏好,或可能在旅行、使用 VPN 或多语言环境下出错的区域设置切换流程时,请查阅语言与区域设置选择参考文档。 强化键盘支持、焦点指示器、跳过链接、隐藏内容行为、模态框焦点管理,或自定义组件与第三方组件的无障碍声明时,请查阅组件无障碍参考文档。 当错误需要专门的路由级恢复页面来处理 401、403、404、429、500 或 503 等状态时,请查阅空状态模式参考文档。 强化必须在压力、紧急情况、运营压力或突发状况下仍保持可用的工作流时,请查阅交互设计参考文档。 强化加载状态、过期数据提示、骨架屏使用,或可能掩盖脆弱实际行为的、看似性能良好的状态时,请查阅加载反馈与感知性能参考文档。 界面虽然在技术上可用,却因细微且反复出现的卡顿、状态丢失、悬停陷阱、反馈不足或类似小问题而显得不稳定、脆弱或损害信任时,请查阅微小故障与感知质量参考文档。
强制准备工作
阅读 frontend-design,并遵循其中的上下文收集协议。复用现有上下文,只针对影响重大的信息缺口提问。此外,还要收集最可能破坏当前流程的高风险场景、边界情况和用户限制。
评估加固需求
找出弱点和边缘情况:
- 使用极端输入进行测试:
- 非常长的文本(名称、描述、标题)
- 非常短的文本(空内容、单个字符)
- 特殊字符(表情符号、从右向左书写的文本、变音符号)
- 大数值(百万、十亿)
- 大量条目(1000+ 个列表项、50+ 个选项)
- 无数据(空状态)
- 不便处理的媒体(微小图标、巨型屏幕截图、裁剪或背景颜色杂乱的用户上传图片)
- 测试错误场景:
- 网络故障(离线、网速慢、超时)
- API 错误(400、401、403、404、500)
- 验证错误
- 验证器过于严格,或格式要求不匹配
- 表单、多步流程和叠加层中的浏览器前进/后退行为
- 权限错误
- 速率限制
- 并发操作
-
测试国际化:
- 较长的译文(德语通常比英语长 30%)
- 从右向左书写的语言(阿拉伯语、希伯来语)
- 字符集(中文、日语、韩语、表情符号)
- 日期/时间格式
- 数字格式(1,000 与 1.000)
- 货币符号
-
测试高压使用场景:
- 小型或分屏工作区
- 嘈杂、频繁受打断的环境
- 需要快速决策、耐心有限
- 需要一个明确的下一步,而不是密集的控件墙的用户
重要:只适用于完美数据的设计还不能用于生产环境。请针对现实情况进行加固。
加固维度
系统性地提升韧性:
文本溢出与换行
长文本处理:
/* Single line with ellipsis */
.truncate {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Multi-line with clamp */
.line-clamp {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* Allow wrapping */
.wrap {
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
}
Flex/Grid 溢出:
/* Prevent flex items from overflowing */
.flex-item {
min-width: 0; /* Allow shrinking below content size */
overflow: hidden;
}
/* Prevent grid items from overflowing */
.grid-item {
min-width: 0;
min-height: 0;
}
响应式文本大小:
- 使用
clamp()实现流畅的排版 - 设置最小可读字号(紧凑布局中为 14px)
- 测试文本缩放(缩放至 200%)
- 确保容器随文本扩展
国际化(i18n)
文本扩展:
- 为译文预留 30-40% 的空间
- 使用能适应内容的 flexbox/grid
- 使用最长的语言进行测试(通常是德语)
- 避免为文本容器设置固定宽度
// ❌ Bad: Assumes short English text
<button className="w-24">Submit</button>
// ✅ Good: Adapts to content
<button className="px-4 py-2">Submit</button>
RTL(从右向左)支持:
/* Use logical properties */
margin-inline-start: 1rem; /* Not margin-left */
padding-inline: 1rem; /* Not padding-left/right */
border-inline-end: 1px double currentColor; /* Not border-right */
/* Or use dir attribute */
[dir="rtl"] .arrow { transform: scaleX(-1); }
字符集支持:
- 在所有地方使用 UTF-8 编码
- 使用中文/日语/韩语(CJK)字符进行测试
- 使用表情符号进行测试(它们可能占 2-4 个字节)
- 处理不同的文字系统(拉丁文、西里尔文、阿拉伯文等)
日期/时间格式:
// ✅ Use Intl API for proper formatting
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
复数形式:
// ❌ Bad: Assumes English pluralization
`${count} item${count !== 1 ? 's' : ''}`
// ✅ Good: Use proper i18n library
t('items', { count }) // Handles complex plural rules
错误处理
网络错误:
- 显示清晰的错误消息
- 提供重试按钮
- 说明发生了什么
- 提供离线模式(如适用)
- 处理超时情况
// Error states with recovery
{error && (
<ErrorMessage>
<p>Failed to load data. {error.message}</p>
<button onClick={retry}>Try again</button>
</ErrorMessage>
)}
表单验证错误:
- 在字段附近显示行内错误
- 提供清晰、具体的消息
- 建议更正方式
- 不要不必要地阻止提交
- 出错时保留用户输入
- 对于地址或电话号码等现实生活中的格式,如果阻止提交会导致用户放弃,且风险模型允许,则提供可覆盖验证的途径
API 错误:
- 根据状态码进行适当处理
- 400:显示验证错误
- 401:提示登录或刷新会话,并在可能时保留用户原本要访问的目标页面
- 403:显示权限状态,并提供明确的访问途径
- 404:显示未找到状态,并提供明确的恢复导航
- 429:显示临时速率限制状态,并在已知时告知重试时间
- 500:显示服务器错误状态,并提供重试,以及安全的备用方案或支持途径
- 503:显示维护或临时服务中断状态,并在可用时告知状态/预计恢复时间
如果故障影响整个路由,应将其设计为专用错误页面,而不是微小的行内消息或提示气泡。
优雅降级:
- 核心功能无需 JavaScript 也能正常工作
- 图片应有替代文本
- 渐进增强
- 为不受支持的功能提供回退方案
边缘情况与边界条件
能力越强,责任越大
功能越强大,界面就越需要清楚地说明可能造成的后果,并防止意外损害。
高影响功能包括:
- 批量操作
- 管理面板
- 权限编辑器
- 自动化与集成
- 能够更改或生成大量内容的 AI 工具
- 具有破坏性的编辑、发布、计费和账户操作
应添加的防护措施:
- 基于角色的权限和明确的能力边界
- 在执行高影响操作前提供预览、摘要或数量统计
- 可撤销的操作提供撤销功能;不可逆或成本高昂的操作要求确认
- 对组织有影响的操作提供审计跟踪或可见的历史记录
- 安全的默认设置、明确的警告,以及通俗易懂的后果说明
应让用户感到能力可控,而非充满风险。如果某项功能可能造成大范围错误,就应设计界面,让用户在操作发生前了解将会发生什么。
空状态:
- 列表中没有项目
- 没有搜索结果
- 没有通知
- 没有可显示的数据
- 提供明确的下一步操作
通知边缘情况:
- 通知量激增(短时间内事件过多)
- 重复或近似重复的通知
- 状态变更后未读徽标仍显示过时信息
- 未遵守免打扰时段、静音或延后通知设置
- 需要摘要而非实时打扰的用户
加载状态:
- 初始加载
- 分页加载
- 刷新
- 显示正在加载的内容(“正在加载你的项目……”)
- 为长时间操作提供预计耗时
大型数据集:
- 分页或虚拟滚动
- 搜索/筛选功能
- 性能优化
- 不要一次性加载全部 10,000 个项目
并发操作:
- 防止重复提交(加载时禁用按钮)
- 处理竞态条件
- 使用乐观更新,并在失败时回滚
- 解决冲突
权限状态:
- 无权查看
- 无权编辑
- 只读模式
- 清楚说明原因
- 在适当情况下,说明如何申请访问权限或了解由谁管理该权限
浏览器兼容性:
- 为现代功能提供 polyfill
- 为不受支持的 CSS 提供回退方案
- 检测功能,而非检测浏览器
- 在目标浏览器中进行测试
图片、屏幕截图与用户媒体
用户上传的图片:
- 将图片限制在受控容器中,并设置合理的宽高比
- 使用 cover/裁剪,而不是保留会破坏网格布局的混乱固有布局
- 当图片与页面背景融为一体时,使用细微的内阴影或内边框,避免背景渗透
屏幕截图:
- 注意避免将屏幕截图缩得太小而无法阅读
- 细节很重要时,优先使用更紧凑的裁剪图、局部屏幕截图,或在源布局尺寸较小时截取的屏幕截图
图标与视觉素材:
- 不要将微小图标放大到远超其设计尺寸
- 不要将细节丰富的图稿缩得过小,以致模糊不清
输入验证与清理
表单和输入中的 Postel 法则
界面输出应当保守;只要系统能够安全地进行规范化,就应宽容对待无害的输入差异。
良好的弹性处理方式:
- 只要含义仍然明确,就接受带或不带空格、短横线或国家代码标点的电话号码
- 自动去除首尾空白,而不是因为粘贴的值不符合必填项要求就直接报错
- 当大小写无关紧要时,对标识符、标签和搜索查询采用一致的大小写处理方式
- 优雅地规范化粘贴的内容,而不是因为用户从其他工具带来数据就惩罚他们
- 失败时保留用户的原始输入,让修正更容易
只有在歧义、安全或不可逆错误确实重要时才严格要求。目标不是为了宽松而宽松,而是减少不必要的精细操作。
客户端验证:
- 必填字段
- 格式验证(邮箱、电话、URL)
- 长度限制
- 模式匹配
- 自定义验证规则
服务端验证(始终执行):
- 绝不只信任客户端验证
- 验证并清理所有输入
- 防范注入攻击
- 速率限制
约束处理:
<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>
在安全的情况下,优先规范化,而不是拒绝:
- 在判定输入无效前,先规范化空白、标点和大小写
- 如果格式调整会影响含义或置信度,应向用户显示规范化后的结果
- 拒绝不安全、有歧义或存在实质性错误的输入,但要解释原因和修正方法
无障碍弹性
键盘导航:
- 所有功能都可通过键盘访问
- 合理的 Tab 键顺序
- 在模态框中管理焦点
- 为长内容提供跳转链接
屏幕阅读器支持:
- 使用恰当的 ARIA 标签
- 播报动态变化(实时区域)
- 提供描述性替代文本
- 使用语义化 HTML
运动敏感性:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
高对比度模式:
- 在 Windows 高对比度模式下进行测试
- 不要只依赖颜色
- 提供其他视觉提示
性能弹性
慢速连接:
- 渐进式图像加载
- 骨架屏
- 乐观 UI 更新
- 离线支持(service workers)
内存泄漏:
- 清理事件监听器
- 取消订阅
- 清除计时器/间隔计时器
- 卸载时中止待处理的请求
节流与防抖:
// Debounce search input
const debouncedSearch = debounce(handleSearch, 300);
// Throttle scroll handler
const throttledScroll = throttle(handleScroll, 100);
测试策略
手动测试:
- 使用极端数据进行测试(非常长、非常短、为空)
- 使用不同语言进行测试
- 测试离线情况
- 测试慢速连接(将网络限速至 3G)
- 如果产品在操作压力下使用,应在嘈杂、繁忙或分屏环境中进行测试
- 使用屏幕阅读器进行测试
- 测试仅使用键盘导航
- 在旧版浏览器中进行测试
- 如果工作流涉及事故、截止期限、审批或其他高风险时刻,至少进行一次压力演练或应急演练
自动化测试:
- 针对边缘情况的单元测试
- 针对错误场景的集成测试
- 针对关键路径的端到端(E2E)测试
- 视觉回归测试
- 无障碍测试(axe、WAVE)
重要:强化意味着预料意外情况。真实用户会做出你从未想象过的操作。
绝不:
- 假设输入完美无误(验证所有内容)
- 忽略国际化(面向全球进行设计)
- 使用笼统的错误消息(“发生错误”)
- 忘记离线场景
- 只信任客户端验证
- 为文本设置固定宽度
- 假设文本长度与英语相同
- 一个组件出错就阻塞整个界面
- 让用户上传的图像决定布局形状和间距
- 将截图缩得过小,导致内容难以辨认
- 将微小图标放大到超出其预期视觉尺寸,还称之为“响应式”
验证加固效果
使用边缘情况进行全面测试:
- 长文本:尝试使用长度超过 100 个字符的名称
- 表情符号:在所有文本字段中使用表情符号
- RTL:使用阿拉伯语或希伯来语进行测试
- CJK:使用中文、日语或韩语进行测试
- 网络问题:断开互联网连接、限制连接速度
- 大型数据集:使用 1000 多个项目进行测试
- 并发操作:快速点击提交按钮 10 次
- 错误:强制触发 API 错误,测试所有错误状态
- 空状态:删除所有数据,测试空状态
请记住:你要针对真实的生产环境进行加固,而不是追求演示效果的完美。要预料到用户会输入奇怪的数据、在流程中途断开连接,并以意想不到的方式使用你的产品。让每个组件都具备韧性。