mcpskills.net
技能MCP智能体提示词
mcpskills.net — A curated directory of AI agent Skills and MCP servers
TermsPrivacy
← 返回技能
Design

前端 UI 工程

构建生产级质量的 UI。在构建或修改面向用户的界面、创建组件、实现布局或管理状态时使用。

作者:Addy Osmani仓库 →来源 →

构建可访问、高性能、视觉精致的生产级用户界面。

使用场景

  • 构建新的 UI 组件或页面
  • 修改现有的面向用户的界面
  • 实现响应式布局
  • 添加交互功能或状态管理
  • 修复视觉或 UX 问题

组件架构

文件结构

将组件相关的所有内容集中放置:

src/components/
  TaskList/
    TaskList.tsx          # 组件实现
    TaskList.test.tsx     # 测试
    TaskList.stories.tsx  # Storybook 故事
    use-task-list.ts      # 自定义 Hook
    types.ts              # 组件专属类型

组件模式

优先使用组合而非配置:

// 好:可组合
<Card>
  <CardHeader>
    <CardTitle>Tasks</CardTitle>
  </CardHeader>
  <CardBody>
    <TaskList tasks={tasks} />
  </CardBody>
</Card>

// 避免:过度配置
<Card
  title="Tasks"
  headerVariant="large"
  bodyPadding="md"
  content={<TaskList tasks={tasks} />}
/>

保持组件职责单一:

export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) {
  return (
    <li className="flex items-center gap-3 p-3">
      <Checkbox checked={task.done} onChange={() => onToggle(task.id)} />
      <span className={task.done ? 'line-through text-muted' : ''}>{task.title}</span>
      <Button variant="ghost" size="sm" onClick={() => onDelete(task.id)}>
        <TrashIcon />
      </Button>
    </li>
  );
}

状态管理

选择最简单且可行的方案:

| 状态类型 | 适用场景 | |---------|---------| | 本地状态 (useState) | 组件专属的 UI 状态 | | 提升状态 | 2–3 个兄弟组件之间共享 | | Context | 主题、认证、语言环境(读多写少) | | URL 状态 (searchParams) | 筛选器、分页、可共享的 UI 状态 | | 服务端状态 (React Query, SWR) | 带缓存的远程数据 | | 全局 store (Zustand, Redux) | 应用范围共享的复杂客户端状态 |

避免超过 3 层的 prop 逐层传递。

避免"AI 审美"

| AI 默认行为 | 问题 | 生产级质量 | |------------|------|-----------| | 到处使用紫色/靛蓝 | 模型默认选择视觉上"安全"的调色板 | 使用项目实际的配色方案 | | 过度使用渐变 | 增加视觉噪点 | 匹配设计系统的扁平或微渐变 | | 所有元素都圆角 | 最大圆角忽略了层级关系 | 使用设计系统中一致的圆角值 | | 千篇一律的首屏区域 | 模板驱动的布局 | 以内容为核心的布局 | | Lorem ipsum 风格的占位文字 | 掩盖了布局问题 | 真实的占位内容 | | 到处使用过大的内边距 | 破坏视觉层级 | 一致的间距体系 |

无障碍访问(WCAG 2.1 AA)

键盘导航

// 每个交互元素都必须可以通过键盘访问
<button onClick={handleClick}>Click me</button>        // ✓ 默认可聚焦
<div onClick={handleClick}>Click me</div>               // ✗ 不可聚焦

ARIA 标签

// 为缺少可见文字的交互元素添加标签
<button aria-label="Close dialog"><XIcon /></button>

// 为表单输入添加标签
<label htmlFor="email">Email</label>
<input id="email" type="email" />

焦点管理

function Dialog({ isOpen, onClose }: DialogProps) {
  const closeRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    if (isOpen) closeRef.current?.focus();
  }, [isOpen]);

  return (
    <dialog open={isOpen}>
      <button ref={closeRef} onClick={onClose}>Close</button>
    </dialog>
  );
}

响应式设计

先设计移动端,再扩展:

<div className="
  grid grid-cols-1      /* 移动端:单列 */
  sm:grid-cols-2        /* 小屏:2 列 */
  lg:grid-cols-3        /* 大屏:3 列 */
  gap-4
">

测试断点:320px、768px、1024px、1440px。

加载状态

// 骨架屏加载(内容区不要用旋转图标)
function TaskListSkeleton() {
  return (
    <div className="space-y-3" aria-busy="true" aria-label="Loading tasks">
      {Array.from({ length: 3 }).map((_, i) => (
        <div key={i} className="h-12 bg-muted animate-pulse rounded" />
      ))}
    </div>
  );
}

验证

  • [ ] 组件无控制台错误渲染
  • [ ] 所有交互元素支持键盘访问
  • [ ] 屏幕阅读器能正确传达页面内容和结构
  • [ ] 响应式:在 320px、768px、1024px、1440px 下正常工作
  • [ ] 加载中、错误和空状态均已处理
  • [ ] 遵循项目的设计系统
  • [ ] 开发工具或 axe-core 中无无障碍访问警告