关于 ui-engineering
ui-engineering 是指导前端组件实现的工程技能,面向构建 UI 的开发者。它强调先规划语义结构、状态、无障碍与响应式行为,并提供布局、状态管理、表单和性能方面的实践规则。
主要功能
- 01组件组合与架构检查
- 02Grid 与 Flexbox 选择指引
- 03状态归属原则
- 04响应式设计方法
- 05无障碍与表单规范
- 06动画及渲染性能建议
适用场景
- 01构建前端组件前规划结构与状态
- 02为页面选择布局方式
- 03实现响应式表单与导航
- 04检查交互组件的无障碍要求
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ noobygains/ godmode --skill ui-engineering安装后在 Claude Code 中输入/ui-engineering确认已加载。
附带文件component-patterns.md
详细介绍
UI 工程
概述
以严谨的架构构建 UI 组件,而不是临时拼凑标记。
核心原则: 每项组件决策——布局策略、状态归属、无障碍设计、响应式行为——都遵循既定模式。UX Patterns 技能告诉你要构建什么。本技能告诉你如何以结构完整的方式构建。
前置条件: 先调用 godmode:ux-patterns,确定适用模式和令牌值。本技能假定令牌和模式已确立。
使用时机
以下情况必须使用:
- 构建任何前端组件
- 在 Grid 和 Flexbox 之间做选择
- 决定状态应放在哪里
- 设计响应式断点行为
- 构建表单、数据可视化、导航或覆盖层
- 引入动效或过渡效果
在以下步骤之后使用:
- 设计令牌已确立(ux-patterns)
- 目标 UI 模式已确定(ux-patterns)
首要指令
NO COMPONENT WITHOUT STRUCTURE, STATES, AND ACCESSIBILITY DEFINED FIRST
编写组件代码之前,先明确:语义结构(正确的 HTML 元素)、所有视觉状态(空、加载中、错误、已填充、禁用),以及无障碍要求(ARIA 属性、键盘交互、对比度)。
组件架构
组合优于配置
将组件组装成更小、可组合的单元,而不是构建臃肿且属性繁多的整体。
``` Dialog DialogHeader DialogTitle DialogDescription DialogBody DialogFooter ``` 每个单元都可独立使用,也可独立设置样式。 ``` Dialog (title, description, body, footer, headerAlign, footerAlign, showCloseButton, variant, size, overlayOpacity, titleSize, ...) ``` 属性泛滥、难以维护、无法扩展。实现前检查清单
编写任何组件之前:
- 语义——哪个 HTML 元素才合适?(用
button而不是div onClick,用nav而不是div className="nav") - 属性——最精简的接口是什么?能否通过组合实现,而不是通过配置实现?
- 状态——默认、悬停、焦点、激活、禁用、加载中、错误、空
- 变体——需要哪些视觉变体?(主要、次要、幽灵、危险)
- 尺寸——有哪些尺寸等级?(sm、md、lg——最多 3-4 个)
- 响应式——在每个断点处如何变化?
- 无障碍——ARIA 角色、键盘导航路径、屏幕阅读器播报
布局策略选择
digraph layout_choice {
rankdir=TB;
q1 [label="What is being\narranged?", shape=diamond];
q2 [label="Single axis or\ntwo axes?", shape=diamond];
q3 [label="Are items\nuniform in size?", shape=diamond];
grid [label="Use CSS Grid\ngrid-template-columns\ngrid-template-rows", shape=box];
flex [label="Use Flexbox\nflex-direction\njustify/align", shape=box];
grid_auto [label="Use CSS Grid\nauto-fill/auto-fit\nminmax()", shape=box];
q1 -> q2;
q2 -> grid [label="two axes\n(rows AND columns)"];
q2 -> q3 [label="single axis\n(row OR column)"];
q3 -> grid_auto [label="yes\n(uniform cards)"];
q3 -> flex [label="no\n(nav items,\nform row)"];
}
适合使用 CSS Grid 的情况
- 页面级框架(侧边栏 + 主内容 + 辅助栏)
- 统一的卡片网格
- 仪表板布局(指标卡片、图表区域)
- 任何需要二维控制的布局
- 跨行和跨列对齐
示例模式:
/* Self-adjusting card grid */
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-6);
}
/* Dashboard scaffold */
.dashboard {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto 1fr;
gap: var(--space-6);
}
.dashboard .wide-chart { grid-column: span 2; }
适合使用 Flexbox 的情况
- 单行导航项
- 表单输入框与按钮的行内分组
- 在容器内居中内容
- 沿单轴分布宽度不一的元素
示例模式:
/* Navigation row */
.nav-row {
display: flex;
align-items: center;
gap: var(--space-4);
}
/* Inline form group */
.inline-group {
display: flex;
align-items: flex-end;
gap: var(--space-3);
}
.inline-group .input-field { flex: 1; }
状态归属策略
digraph state_ownership {
rankdir=TB;
q1 [label="Where does this\nstate belong?", shape=diamond];
q2 [label="Consumed by\nmultiple components?", shape=diamond];
q3 [label="Server-originated\nor client-only?", shape=diamond];
q4 [label="Prop-drilling\nexceeds 3 levels?", shape=diamond];
local [label="Component-local state\nuseState / ref", shape=box];
server [label="Server state manager\nTanStack Query / SWR", shape=box];
context [label="Context / Provider\nReact Context / provide-inject", shape=box];
global [label="Global store\nZustand / Pinia / Signals", shape=box];
q1 -> q2;
q2 -> local [label="no\n(isolated component)"];
q2 -> q3 [label="yes"];
q3 -> server [label="server-originated\n(API responses,\ncached data)"];
q3 -> q4 [label="client-only\n(UI flags,\npreferences)"];
q4 -> context [label="no\n(2-3 levels)"];
q4 -> global [label="yes\n(application-wide)"];
}
基本原则:
- 从局部状态开始。只有在有证据表明确有需要时才提升状态。
- 服务端数据不是客户端状态。应使用专用的服务端状态库管理。
- Context 用于依赖注入(主题、身份验证上下文),而不是用于高频更新。
- 全局存储是最后手段,而非起点。
响应式设计方法
移动优先的递进方式
先将移动端样式作为基线,再在更宽的断点逐步增加复杂性。
/* Mobile baseline */
.wrapper {
padding: var(--space-4);
}
/* Tablet tier */
@media (min-width: 768px) {
.wrapper {
padding: var(--space-6);
max-width: 768px;
margin: 0 auto;
}
}
/* Desktop tier */
@media (min-width: 1024px) {
.wrapper {
padding: var(--space-8);
max-width: 1280px;
}
}
响应式适配参考
| 元素 | 移动端 | 平板 | 桌面端 |
|---|---|---|---|
| 导航 | 汉堡菜单或底部抽屉 | 标签栏或折叠侧边栏 | 展开的侧边栏 |
| 卡片网格 | 单列 | 两列 | 三至四列 |
| 数据表格 | 卡片堆叠视图或水平滚动 | 完整表格,减少列数 | 完整表格 |
| 侧边栏 + 主内容区 | 仅主内容区;侧边栏放入抽屉中 | 仅显示图标的侧边栏 | 完全展开的侧边栏 |
| 表单 | 单列,输入框拉伸至全宽 | 单列,最大宽度 560px | 成对字段使用两列 |
| 模态框 | 全屏抽屉 | 居中,占视口宽度的 80% | 居中,最大宽度 480px |
| 主视觉区 | 纵向排列(图片在标题下方) | 纵向排列,字体更大 | 并排排列 |
无障碍标准
每个交互元素
- 可通过 Tab 键访问
- 焦点环清晰可见(
:focus-visible,轮廓至少为 2px) - 可通过 Enter/Space(按钮)或 Enter(链接)激活
- 具有无障碍名称(可见文本、
aria-label或aria-labelledby) - 禁用状态会从 Tab 顺序中移除,或应用
aria-disabled - 移动端触控目标至少为 44x44px
表单
- 每个输入框都有可见的
<label>(仅使用占位符不够) - 标明必填字段(星号加上
aria-required="true") - 错误消息与对应输入框关联(
aria-describedby) - 错误提示不能仅依赖颜色(同时使用图标和文本)
- 向屏幕阅读器播报提交结果(
aria-live)
图片和图标
- 有意义的图片带有描述性
alt文本 - 装饰性图片使用
alt=""或aria-hidden="true" - 仅含图标的按钮带有
aria-label - SVG 图标使用
role="img"和aria-label,或使用aria-hidden="true"
颜色和对比度
- 普通文本的对比度至少为 4.5:1(大号文本为 3:1:粗体 18px 及以上,或 24px 及以上)
- 绝不只通过颜色传达信息(辅以图标、图案、文本)
- 动画遵循
prefers-reduced-motion - 如果提供深色模式,配色方案需遵循
prefers-color-scheme
动画和动效
何时使用动画
- 状态转换: 悬停、聚焦、展开/折叠、显示/隐藏
- 反馈信号: 成功、错误、加载进度
- 空间提示: 说明内容从何处出现或消失
何时不使用动画
- 没有功能用途的装饰效果
- UI 转换持续时间超过 300ms
- 阻止用户交互的动效(强制等待)
- 用户无法控制的持续运动
时长参考
| 交互 | 时长 | 缓动 |
|---|---|---|
| 悬停响应 | 150ms | ease |
| 按钮按下 | 100ms | ease-out |
| 模态框进入 | 200ms | ease-out |
| 模态框退出 | 150ms | ease-in |
| 抽屉滑入 | 250ms | ease-out |
| 淡入 | 200ms | ease |
| 页面转换 | 200-300ms | ease-in-out |
尊重用户的动效偏好
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
表单架构
验证体验
- 不要在每次按键时都进行验证。 在失焦(离开字段)或提交表单时验证。
- 在字段下方内联显示错误,不要放在汇总横幅中。
- 错误文本替代辅助文本——不要同时显示两者。
- 错误提示使用颜色和图标——绝不能只依赖颜色(无障碍要求)。
- 保留有效字段指示器(复选标记或绿色边框),随着用户填写逐步显示。
- 提交期间禁用提交按钮,并显示加载指示器。
字段布局指南
- 标签放在输入框上方(不要放在旁边,也不要仅使用占位符)
- 使用
<fieldset>和<legend>对相关字段分组 - 必填字段用星号标记,并为屏幕阅读器添加
(required) - 可选字段可改为显示 “(optional)”
- 表单最大宽度:单列 480-560px
- 提交按钮左对齐,不要居中(模态框内除外)
渲染性能
关键路径优化
- 首屏内容优先加载。 延迟加载首屏下方的资源和脚本。
- 正文优先使用系统字体,除非品牌要求使用网页字体。网页字体会导致布局偏移。
- 规范使用图像: 设置
width/height属性(防止 CLS),为首屏下方的图像设置loading="lazy",并使用srcset实现响应式加载。 - 关注打包体积: 每个导入的库都会产生开销。添加依赖前,请先衡量其对打包体积的影响。
懒加载策略
- 首屏下方的图像:
loading="lazy" - 路由模块:动态导入/代码拆分
- 大型组件(图表、富文本编辑器):在交互时或组件可见时加载
- 切勿对首屏内容进行懒加载
常见错误
| 错误 | 修正方法 |
|---|---|
所有内容都使用 div | 使用语义化 HTML(button、nav、main、section、article) |
| 仅用占位符作为标签 | 始终提供可见的 <label> 元素 |
| 到处使用像素字面值 | 使用设计系统中的令牌变量 |
| 容器宽度固定 | 使用最大宽度搭配百分比宽度或自动边距 |
| 移除焦点轮廓 | 设置 :focus-visible 的样式,而不是移除轮廓 |
| 在 div 上添加点击处理程序 | 交互元素使用 <button> 或 <a> |
| 使用外边距控制布局间距 | 使用 Grid 或 Flexbox 的 gap |
| 导入整个图标库 | 单独导入图标;启用 tree-shaking |
| 只实现顺利流程 | 在实现顺利流程前,先设计好所有状态 |
| 只在桌面设备上测试 | 先测试移动设备,再验证桌面设备 |
集成
前置条件:
- godmode:ux-patterns —— 必须先建立令牌和模式
互补技能:
- godmode:design-integration —— 在现有设计系统中工作时使用
- godmode:test-first —— 组件测试遵循测试优先方法
支持文件:
component-patterns.md—— 内置无障碍支持的可复用组件蓝图
评论
支持 Markdown