Claude Agent Skill · by Addyosmani

Frontend Ui Engineering

Install Frontend Ui Engineering skill for Claude Code from addyosmani/agent-skills.

Install
Terminal · npx
$npx skills add https://github.com/vercel-labs/agent-skills --skill vercel-react-best-practices
Works with Paperclip

How Frontend Ui Engineering fits into a Paperclip company.

Frontend Ui Engineering drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

S
SaaS FactoryPaired

Pre-configured AI company — 18 agents, 18 skills, one-time purchase.

$27$59
Explore pack
Source file
SKILL.md328 lines
Expand
---name: frontend-ui-engineeringdescription: Builds production-quality UIs. Use when building or modifying user-facing interfaces. Use when creating components, implementing layouts, managing state, or when the output needs to look and feel production-quality rather than AI-generated.--- # Frontend UI Engineering ## Overview Build production-quality user interfaces that are accessible, performant, and visually polished. The goal is UI that looks like it was built by a design-aware engineer at a top company — not like it was generated by an AI. This means real design system adherence, proper accessibility, thoughtful interaction patterns, and no generic "AI aesthetic." ## When to Use - Building new UI components or pages- Modifying existing user-facing interfaces- Implementing responsive layouts- Adding interactivity or state management- Fixing visual or UX issues ## Component Architecture ### File Structure Colocate everything related to a component: ```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)``` ### Component Patterns **Prefer composition over configuration:** ```tsx// 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} />}/>``` **Keep components focused:** ```tsx// Good: Does one thingexport 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>  );}``` **Separate data fetching from presentation:** ```tsx// Container: handles dataexport 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 renderingexport function TaskList({ tasks }: { tasks: Task[] }) {  return (    <ul role="list" className="divide-y">      {tasks.map(task => <TaskItem key={task.id} task={task} />)}    </ul>  );}``` ## State Management **Choose the simplest approach that works:** ```Local state (useState)           → Component-specific UI stateLifted state                     → Shared between 2-3 sibling componentsContext                          → Theme, auth, locale (read-heavy, write-rare)URL state (searchParams)         → Filters, pagination, shareable UI stateServer state (React Query, SWR)  → Remote data with cachingGlobal store (Zustand, Redux)    → Complex client state shared app-wide``` **Avoid prop drilling deeper than 3 levels.** If you're passing props through components that don't use them, introduce context or restructure the component tree. ## Design System Adherence ### Avoid the AI Aesthetic AI-generated UI has recognizable patterns. Avoid all of them: | AI Default | Why It Is a Problem | Production Quality ||---|---|---|| Purple/indigo everything | Models default to visually "safe" palettes, making every app look identical | Use the project's actual color palette || Excessive gradients | Gradients add visual noise and clash with most design systems | Flat or subtle gradients matching the design system || Rounded everything (rounded-2xl) | Maximum rounding signals "friendly" but ignores the hierarchy of corner radii in real designs | Consistent border-radius from the design system || Generic hero sections | Template-driven layout with no connection to the actual content or user need | Content-first layouts || Lorem ipsum-style copy | Placeholder text hides layout problems that real content reveals (length, wrapping, overflow) | Realistic placeholder content || Oversized padding everywhere | Equal generous padding destroys visual hierarchy and wastes screen space | Consistent spacing scale || Stock card grids | Uniform grids are a layout shortcut that ignores information priority and scanning patterns | Purpose-driven layouts || Shadow-heavy design | Layered shadows add depth that competes with content and slows rendering on low-end devices | Subtle or no shadows unless the design system specifies | ### Spacing and Layout Use a consistent spacing scale. Don't invent values: ```css/* 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 */``` ### Typography Respect the type hierarchy: ```h1 → Page title (one per page)h2 → Section titleh3 → Subsection titlebody → Default textsmall → Secondary/helper text``` Don't skip heading levels. Don't use heading styles for non-heading content. ### Color - Use semantic color tokens: `text-primary`, `bg-surface`, `border-default` — not raw hex values- Ensure sufficient contrast (4.5:1 for normal text, 3:1 for large text)- Don't rely solely on color to convey information (use icons, text, or patterns too) ## Accessibility (WCAG 2.1 AA) Every component must meet these standards: ### Keyboard Navigation ```tsx// 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>``` ### ARIA Labels ```tsx// 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" />``` ### Focus Management ```tsx// Move focus when content changesfunction 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>  );}``` ### Meaningful Empty and Error States ```tsx// Don't show blank screensfunction 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>;}``` ## Responsive Design Design for mobile first, then expand: ```tsx// 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">``` Test at these breakpoints: 320px, 768px, 1024px, 1440px. ## Loading and Transitions ```tsx// 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 speedfunction 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);    },  });}``` ## See Also For detailed accessibility requirements and testing tools, see `references/accessibility-checklist.md`. ## Common Rationalizations | Rationalization | Reality ||---|---|| "Accessibility is a nice-to-have" | It's a legal requirement in many jurisdictions and an engineering quality standard. || "We'll make it responsive later" | Retrofitting responsive design is 3x harder than building it from the start. || "The design isn't final, so I'll skip styling" | Use the design system defaults. Unstyled UI creates a broken first impression for reviewers. || "This is just a prototype" | Prototypes become production code. Build the foundation right. || "The AI aesthetic is fine for now" | It signals low quality. Use the project's actual design system from the start. | ## Red Flags - Components with more than 200 lines (split them)- Inline styles or arbitrary pixel values- Missing error states, loading states, or empty states- No keyboard navigation testing- Color as the sole indicator of state (red/green without text or icons)- Generic "AI look" (purple gradients, oversized cards, stock layouts) ## Verification After building UI: - [ ] Component renders without console errors- [ ] All interactive elements are keyboard accessible (Tab through the page)- [ ] Screen reader can convey the page's content and structure- [ ] Responsive: works at 320px, 768px, 1024px, 1440px- [ ] Loading, error, and empty states all handled- [ ] Follows the project's design system (spacing, colors, typography)- [ ] No accessibility warnings in dev tools or axe-core