关于 web-ui-best-practices
面向开发者的 Web UI 设计技能,涵盖交互速度、无障碍、导航、状态持久化和现代 CSS,并提供实用审查原则。
主要功能
- 01快速交互反馈
- 02响应式 CSS 模式
- 03可恢复的用户状态
- 04导航与 URL 指引
- 05无障碍与输入指引
适用场景
- 01审查 Web 界面
- 02设计响应式组件
- 03改进表单与导航行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jamditis/ claude-skills-journalism --skill web-ui-best-practices安装后在 Claude Code 中输入/web-ui-best-practices确认已加载。
附带文件agents/openai.yaml
详细介绍
Web UI 最佳实践
构建快速、周到且尊重用户时间的 Web 界面的原则。这里的每条规则都是一项气味测试;有理由时,违反一条没关系,违反多条则说明 UI 需要改进。
速度
每次交互都应在 100ms 内完成。如果做不到,就营造出快速完成的假象。
- 使用乐观 UI 更新,在服务器确认前显示结果
- 对输入进行防抖,但绝不要让感知到的响应延迟
- 在悬停或进入视口时,预取可能的下一条路由
- 使用
will-change和transform实现动画,绝不要使用top/left - 用
performance.now()测量,不要凭感觉判断
// Optimistic delete, remove from UI immediately, reconcile later
async function handleDelete(id) {
setItems(prev => prev.filter(i => i.id !== id));
try {
await api.delete(`/items/${id}`);
} catch {
setItems(prev => [...prev, originalItem]);
toast("Couldn't delete. Restored.");
}
}
骨架屏加载状态
如果你知道即将加载内容的形状,就绝不要显示加载指示器。先渲染与布局匹配的骨架屏,再替换为真实内容。
.skeleton {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: 4px;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
现代 CSS 工具集
2023 至 2026 年间成熟的四项能力,改变了无需 JavaScript 构建组件级响应式布局和类 SPA 转场的方式。在添加框架之前,先考虑使用它们。
容器查询
容器查询让组件根据其容器的尺寸响应,而不是根据视口尺寸响应。同一张卡片可以在 300px 的侧边栏和 900px 的主列中呈现,无需在页面层面协调媒体查询。
.card-list {
container-type: inline-size;
container-name: cards;
}
@container cards (min-width: 480px) {
.card { display: grid; grid-template-columns: 120px 1fr; }
}
自 2023 年起,已在所有主流浏览器中稳定支持。它取代了大多数“同一组件在两个地方需要呈现不同样式”的变通做法。
:has() 父选择器
:has() 让父元素可以根据其后代元素为自身设置样式,这是期待已久的“父选择器”。它适用于将表单字段标记为错误状态、将卡片标记为包含附加图片,或将行标记为包含已聚焦的输入框,而且无需 JS。
/* Highlight a form group when its input has focus */
.form-group:has(input:focus) {
outline: 2px solid var(--color-primary);
}
/* Add bottom margin to articles that contain a figure */
article:has(figure) {
margin-bottom: 2rem;
}
自 2023 年末起,已在 Chrome、Safari 和 Firefox 中稳定支持。它消除了一类由 JS 驱动的类切换操作。
视图转场
View Transitions API 可以在两个 DOM 状态之间(路由变更、模态框打开/关闭、列表项切换)实现动画,无需框架。浏览器会先截取旧状态的快照,替换为新状态,然后在两者之间交叉淡化或滑动切换。
// Same-document transition (Chrome 111+, Safari TP, Firefox behind a flag)
function navigate(newView) {
if (!document.startViewTransition) {
renderView(newView);
return;
}
document.startViewTransition(() => renderView(newView));
}
/* Smooth crossfade by default; override per element */
::view-transition-old(*) { animation-duration: 200ms; }
::view-transition-new(*) { animation-duration: 200ms; }
跨文档视图转场(完整页面导航之间的转场)于 2024 年随 Chrome 126 发布,让 MPA 也能拥有 SPA 般的体验。搭配 prefers-reduced-motion 使用,让对动态效果敏感的用户可以立即切换,而不是播放动画。
滚动驱动动画
animation-timeline: scroll() 和 animation-timeline: view() 根据滚动位置驱动 CSS 动画,而不是根据墙钟时间。一个经典用途是在文章顶部显示进度指示器,并在滚动时逐渐填满。
@keyframes fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.read-progress {
position: fixed; top: 0; left: 0; right: 0; height: 3px;
background: var(--color-primary);
transform-origin: left;
animation: fill linear;
animation-timeline: scroll(root);
}
在基于 Chromium 的浏览器中已稳定支持(Chrome 115+、Edge);截至 2026-05,Safari 和 Firefox 尚未支持。将其用作渐进增强;提供 JS 回退方案,或接受其他浏览器上不那么醒目的基础效果。
不要使用产品导览
如果需要导览来解释 UI,说明 UI 设计有问题。请改用:
- 能通过实际操作进行引导的空状态(“创建你的第一个项目”)
- 渐进式展示,在功能变得相关时再显示
- 首次使用后消失的行内提示
- 无需配置即可使用的默认设置
URL
路径段应简短、易读且便于用户猜测。不要使用 UUID,也不要让查询参数堆成一团。
Good: /projects/weather-app
/settings/billing
/docs/api/auth
Bad: /projects/550e8400-e29b-41d4-a716-446655440000
/app?view=settings&tab=billing&subsection=plan
/dashboard#!/module/documents/list?filter=active
- 使用根据用户提供的名称生成的路径段
- 嵌套层级最多为 3 段
- 让 URL 可以复制和分享,它们是产品的记忆
持久化且可恢复的状态
用户会离开,也会回来。请尊重这一点。
- 将表单草稿状态保存到
localStorage或服务器 - 在后退导航时恢复滚动位置
- 在不同会话之间保留筛选/排序选择
- URL 编码当前视图状态,分享 URL 即可重现该视图
// Persist form state across sessions
function usePersistentForm(key, defaults) {
const [state, setState] = useState(() => {
const saved = localStorage.getItem(key);
return saved ? JSON.parse(saved) : defaults;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
}, [key, state]);
return [state, setState];
}
克制使用颜色
颜色不超过 3 种:一种主色、一种强调色、一种用于危险/破坏性操作的颜色。其他所有颜色都使用灰色系。
:root {
--color-primary: #2563eb;
--color-accent: #f59e0b;
--color-danger: #ef4444;
--gray-50: #fafafa;
--gray-100: #f4f4f5;
--gray-200: #e4e4e7;
--gray-400: #a1a1aa;
--gray-600: #52525b;
--gray-900: #18181b;
}
- 用不透明度和亮度建立层级,而不是增加新色相
- 暗色模式使用相同的 3 种颜色,并反转灰色系
- 如果需要第 4 种颜色,说明你是在弥补布局的不足
不显示滚动条
仅在用户主动滚动时显示。让内容感觉无穷无尽,而不是被困在某处。
/* Hide scrollbar across browsers */
.scroll-container {
overflow-y: auto;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.scroll-container::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
用滚动阴影提示内容溢出,不要添加视觉装饰:
.scroll-shadow {
background:
linear-gradient(white 30%, transparent),
linear-gradient(transparent, white 70%) 0 100%,
radial-gradient(farthest-side at 50% 0, rgba(0,0,0,.15), transparent),
radial-gradient(farthest-side at 50% 100%, rgba(0,0,0,.15), transparent) 0 100%;
background-repeat: no-repeat;
background-size: 100% 40px, 100% 40px, 100% 12px, 100% 12px;
background-attachment: local, local, scroll, scroll;
}
导航深度
从任何位置出发,最多 3 步即可完成所有导航。如果用户需要点击超过 3 次才能到达目标位置,就简化层级。
- 面包屑用于展示层级,而不是导航
- 全局导航始终可见,在桌面端绝不隐藏在汉堡菜单后
- 使用
Cmd+K/Ctrl+K,为高级用户提供备用入口
命令面板(Cmd+K)
任何包含多个页面的应用都需要命令面板。
// Minimal Cmd+K listener
useEffect(() => {
function handleKeyDown(e) {
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
e.preventDefault();
setCommandPaletteOpen(true);
}
}
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
保持面板简单:
- 对页面名称、最近操作和设置进行模糊搜索
- 在行内显示键盘快捷键
- 最近使用的项目排在最前
- 命令达到 20 个以上之前,不要分类
剪贴板
在用户预期的任何地方,都应支持复制和粘贴。
- 代码、URL、API 密钥和 ID 支持一键复制
- 支持从剪贴板粘贴内容到文件上传和图像字段
- 复制后显示简短确认(“已复制!”),并自动消失
async function copyToClipboard(text, label = "Copied") {
await navigator.clipboard.writeText(text);
toast(label, { duration: 1500 });
}
点击目标
增大按钮和输入框的点击目标。WCAG 2.2(成功标准 2.5.8)规定的最低标准是 24×24 CSS 像素;Apple 人机界面指南以及大多数 iOS/Android 原生惯例建议采用舒适的 44×44 点。主要操作以 44px 为实际最低标准;次要控件(例如密集表格行中的图标)则以 24px 为绝对最低标准。
button, .btn, [role="button"] {
min-height: 44px;
min-width: 44px;
padding: 10px 20px;
}
input, select, textarea {
min-height: 44px;
padding: 10px 12px;
font-size: 16px; /* Prevents iOS Safari zoom on focus */
}
- 相邻的可点击元素之间至少留出 8px 间距
- 纯图标按钮的内边距应大于带标签的按钮
- 关键功能不能依赖悬停状态,因为触屏设备不支持悬停
轻松取消
一键取消。不施加压力,不用暗黑模式设计,也不说“确定要错过这个机会吗?”
- 确认按钮旁始终显示取消按钮
- 删除账户只需首次尝试即可完成
- 退订只需点击一次,无需进入偏好设置中心
- 降级流程无需联系客服
工具提示
尽量精简。工具提示意味着界面无法清楚地表达自身含义。
- 仅用于纯图标按钮(提供标签)
- 绝不用于已清晰可读的文本
- 悬停 300ms 后再显示,不要立即出现
- 滚动时消失
- 绝不用工具提示传达关键信息
文案
使用主动语态。每句话最多 7 个词。像普通人一样说话,不要像法律文件。
Good: "Project created"
"Saved 2 minutes ago"
"Delete this file?"
Bad: "Your project has been successfully created!"
"Changes were last saved approximately 2 minutes ago"
"Are you sure you want to permanently delete this file? This action cannot be undone."
- 按钮使用动词:“保存”“删除”“发送”,不要使用“提交”“确定”“确认”
- 错误消息说明发生了什么,以及接下来该怎么做
- 绝不责怪用户(将“输入无效”改为“请输入有效的电子邮件地址”)
- 所有界面文案均使用句子大小写,绝不使用标题式大小写
视觉对齐
优先考虑视觉对齐,而非几何对齐。眼睛看到的不是像素,而是视觉重量。
- 播放图标在圆形内向右偏移 2-3px,看起来才居中
- 首字母大写的文本,其视觉左边缘应与边界框左侧对齐
- 文本旁的图标应根据字形,垂直偏移 1-2px
- 文本周围的内边距应在视觉上平衡,而非数学上相等;底部内边距通常比顶部多 1-2px
/* Geometric center ≠ optical center */
.play-button svg {
transform: translateX(2px);
}
/* Visually balanced card padding */
.card {
padding: 20px 24px 22px 24px;
}
从左到右的阅读流
针对从左到右的阅读习惯和 F 型扫描模式进行优化。
- 将最重要的内容放在左上象限
- 将主要操作放在右侧(视线读完一行后会落到这里)
- 标签放在输入框上方,而不是旁边
- 表格中最常被浏览的列放在最左侧
- 正文不要居中对齐;除单行标题外,其他内容都左对齐
避免用户担心丢失内容
用户担心丢失工作。既要避免这种情况,也要让用户知道内容已保存。
- 自动保存,并显示“已保存”指示和时间戳
- 在破坏性操作后提供撤销功能(软删除,而非永久删除)
- 离开页面时提示“你有未保存的更改”
- 对超过一条推文长度的内容提供版本历史
- 仅对不可逆操作进行确认,不要对常规操作进行确认
// Warn on unsaved changes
useEffect(() => {
function handleBeforeUnload(e) {
if (hasUnsavedChanges) {
e.preventDefault();
e.returnValue = "";
}
}
window.addEventListener("beforeunload", handleBeforeUnload);
return () => window.removeEventListener("beforeunload", handleBeforeUnload);
}, [hasUnsavedChanges]);
可下载的品牌素材
提供一个 /brand 或 /press 页面,供用户下载 SVG Logo 和品牌套件。别让用户截图保存你的 Logo。
- 透明背景的 SVG Logo
- 颜色代码(hex、RGB、HSL)
- 字体名称和字重
- 使用指南(最小尺寸、留白空间、禁用方式)
- 一键下载 ZIP
检查清单
使用以下清单检查任何 Web UI:
- 每次交互都在 100ms 内完成(或通过乐观更新模拟)
- 没有产品导览或引导弹窗
- URL 简短易读,不含 UUID
- 状态在不同会话和页面重新加载后仍能保留
- 颜色不超过 3 种(灰色除外)
- 静止时不显示滚动条
- 到达任何目标最多需要 3 步
- SVG Logo 和品牌套件可供下载
- 使用骨架屏加载器,而非旋转指示器
- 代码、密钥和 URL 均可复制到剪贴板
- 触控目标至少为 44px
- 取消操作明确且只需点击一次
- 提供 Cmd+K 命令面板
- 仅为纯图标按钮提供工具提示
- 文案使用主动语态,最多 7 个词
- 采用视觉对齐,而非几何对齐
- 内容遵循从左到右的 F 型阅读模式
- 自动保存,并显示状态且支持撤销