원클릭으로
frontend-ui-engineering
构建生产级 UI。当构建或修改面向用户的界面时使用。创建组件、实现布局、管理状态,或输出需要看起来和感觉上都是生产级而不是 AI 生成时使用。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
构建生产级 UI。当构建或修改面向用户的界面时使用。创建组件、实现布局、管理状态,或输出需要看起来和感觉上都是生产级而不是 AI 生成时使用。
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
指导稳定的 API 和接口设计。设计 API、模块边界或任何公共接口时使用。创建 REST 或 GraphQL endpoint、定义模块之间的类型契约,或建立前后端边界时使用。
在真实浏览器中测试。构建或调试任何在浏览器中运行的内容时使用。当你需要通过 Chrome DevTools MCP 检查 DOM、捕获 console 错误、分析网络请求、分析性能,或用真实运行时数据验证视觉输出时使用。
自动化 CI/CD pipeline 设置。用于设置或修改构建和部署 pipeline 时;用于需要自动化质量门禁、在 CI 中配置 test runners,或建立部署策略时。
执行多维度代码审查。用于合并任何变更之前;用于审查自己、其他 agent 或人类编写的代码;用于在代码进入主分支前从多个维度评估代码质量。
为清晰度简化代码。用于在不改变行为的前提下重构代码以提升清晰度;用于代码能运行但比应有状态更难阅读、维护或扩展时;用于审查已累积不必要复杂度的代码时。
优化 agent 上下文设置。当开始新会话、agent 输出质量下降、在任务之间切换,或需要为项目配置规则文件和上下文时使用。
| name | frontend-ui-engineering |
| description | 构建生产级 UI。当构建或修改面向用户的界面时使用。创建组件、实现布局、管理状态,或输出需要看起来和感觉上都是生产级而不是 AI 生成时使用。 |
构建可访问、高性能、视觉打磨到位的生产级用户界面。目标是让 UI 看起来像由顶级公司的、有设计意识的工程师构建,而不是 AI 生成。这意味着真正遵循设计系统、正确的可访问性、周到的交互模式,以及没有通用的“AI aesthetic”。
把与组件相关的一切放在一起:
src/components/
TaskList/
TaskList.tsx # Component implementation
TaskList.test.tsx # Tests
TaskList.stories.tsx # Storybook stories (if using)
use-task-list.ts # Custom hook (if complex state)
types.ts # Component-specific types (if needed)
优先组合,而不是配置:
// Good: Composable
<Card>
<CardHeader>
<CardTitle>Tasks</CardTitle>
</CardHeader>
<CardBody>
<TaskList tasks={tasks} />
</CardBody>
</Card>
// Avoid: Over-configured
<Card
title="Tasks"
headerVariant="large"
bodyPadding="md"
content={<TaskList tasks={tasks} />}
/>
保持组件聚焦:
// Good: Does one thing
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>
);
}
把数据获取与展示分离:
// Container: handles data
export function TaskListContainer() {
const { tasks, isLoading, error } = useTasks();
if (isLoading) return <TaskListSkeleton />;
if (error) return <ErrorState message="Failed to load tasks" retry={refetch} />;
if (tasks.length === 0) return <EmptyState message="No tasks yet" />;
return <TaskList tasks={tasks} />;
}
// Presentation: handles rendering
export function TaskList({ tasks }: { tasks: Task[] }) {
return (
<ul role="list" className="divide-y">
{tasks.map(task => <TaskItem key={task.id} task={task} />)}
</ul>
);
}
选择能工作的最简单方案:
Local state (useState) → Component-specific UI state
Lifted state → Shared between 2-3 sibling components
Context → Theme, auth, locale (read-heavy, write-rare)
URL state (searchParams) → Filters, pagination, shareable UI state
Server state (React Query, SWR) → Remote data with caching
Global store (Zustand, Redux) → Complex client state shared app-wide
避免超过 3 层的 prop drilling。 如果你把 props 传过不使用它们的组件,引入 context 或重构组件树。
AI 生成的 UI 有可识别的模式。全部避免:
| AI 默认值 | 为什么是问题 | 生产质量 |
|---|---|---|
| 到处都是紫色/靛蓝 | 模型默认选择视觉上“安全”的调色板,让每个应用看起来都一样 | 使用项目实际的色彩方案 |
| 过度渐变 | 渐变增加视觉噪声,并与多数设计系统冲突 | 使用符合设计系统的平面色或细微渐变 |
| 所有东西都圆角(rounded-2xl) | 最大圆角传递“友好”感,但忽略真实设计中的圆角层级 | 使用设计系统中一致的 border-radius |
| 通用 hero sections | 模板化布局,与实际内容或用户需求无关 | 内容优先的布局 |
| Lorem ipsum 风格文案 | 占位文本会隐藏真实内容暴露的布局问题(长度、换行、溢出) | 真实的占位内容 |
| 到处都是超大 padding | 一味宽松的等量 padding 会破坏视觉层级并浪费屏幕空间 | 一致的间距尺度 |
| Stock card grids | 统一网格是忽略信息优先级和扫描模式的布局捷径 | 目标驱动的布局 |
| 阴影很重的设计 | 层叠阴影增加与内容竞争的深度,并拖慢低端设备渲染 | 除非设计系统指定,否则使用细微阴影或不使用阴影 |
使用一致的间距尺度。不要发明数值:
/* Use the scale: 0.25rem increments (or whatever the project uses) */
/* Good */ padding: 1rem; /* 16px */
/* Good */ gap: 0.75rem; /* 12px */
/* Bad */ padding: 13px; /* Not on any scale */
/* Bad */ margin-top: 2.3rem; /* Not on any scale */
尊重文字层级:
h1 → Page title (one per page)
h2 → Section title
h3 → Subsection title
body → Default text
small → Secondary/helper text
不要跳过标题级别。不要把标题样式用于非标题内容。
text-primary、bg-surface、border-default,不要用原始 hex 值每个组件都必须满足这些标准:
// Every interactive element must be keyboard accessible
<button onClick={handleClick}>Click me</button> // ✓ Focusable by default
<div onClick={handleClick}>Click me</div> // ✗ Not focusable
<div role="button" tabIndex={0} onClick={handleClick} // ✓ But prefer <button>
onKeyDown={e => {
if (e.key === 'Enter') handleClick();
if (e.key === ' ') e.preventDefault();
}}
onKeyUp={e => {
if (e.key === ' ') handleClick();
}}>
Click me
</div>
// Label interactive elements that lack visible text
<button aria-label="Close dialog"><XIcon /></button>
// Label form inputs
<label htmlFor="email">Email</label>
<input id="email" type="email" />
// Or use aria-label when no visible label exists
<input aria-label="Search tasks" type="search" />
// Move focus when content changes
function Dialog({ isOpen, onClose }: DialogProps) {
const closeRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (isOpen) closeRef.current?.focus();
}, [isOpen]);
// Trap focus inside dialog when open
return (
<dialog open={isOpen}>
<button ref={closeRef} onClick={onClose}>Close</button>
{/* dialog content */}
</dialog>
);
}
// Don't show blank screens
function TaskList({ tasks }: { tasks: Task[] }) {
if (tasks.length === 0) {
return (
<div role="status" className="text-center py-12">
<TasksEmptyIcon className="mx-auto h-12 w-12 text-muted" />
<h3 className="mt-2 text-sm font-medium">No tasks</h3>
<p className="mt-1 text-sm text-muted">Get started by creating a new task.</p>
<Button className="mt-4" onClick={onCreateTask}>Create Task</Button>
</div>
);
}
return <ul role="list">...</ul>;
}
先为移动端设计,再扩展:
// Tailwind: mobile-first responsive
<div className="
grid grid-cols-1 /* Mobile: single column */
sm:grid-cols-2 /* Small: 2 columns */
lg:grid-cols-3 /* Large: 3 columns */
gap-4
">
在这些断点测试:320px、768px、1024px、1440px。
// Skeleton loading (not spinners for content)
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>
);
}
// Optimistic updates for perceived speed
function useToggleTask() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: toggleTask,
onMutate: async (taskId) => {
await queryClient.cancelQueries({ queryKey: ['tasks'] });
const previous = queryClient.getQueryData(['tasks']);
queryClient.setQueryData(['tasks'], (old: Task[]) =>
old.map(t => t.id === taskId ? { ...t, done: !t.done } : t)
);
return { previous };
},
onError: (_err, _taskId, context) => {
queryClient.setQueryData(['tasks'], context?.previous);
},
});
}
关于详细可访问性要求和测试工具,见 references/accessibility-checklist.md。
| 自我合理化 | 现实 |
|---|---|
| “Accessibility 只是锦上添花” | 在许多司法辖区它是法律要求,也是工程质量标准。 |
| “我们之后再做 responsive” | 事后补响应式设计,比一开始就构建难 3 倍。 |
| “设计还没最终定稿,所以我先跳过 styling” | 使用设计系统默认值。无样式 UI 会给 reviewer 留下破损的第一印象。 |
| “这只是 prototype” | 原型会变成生产代码。把基础打对。 |
| “AI aesthetic 现在也可以” | 它会传递低质量信号。从一开始就使用项目真实设计系统。 |
构建 UI 后: