--- name: frontend-ui-engineering description: 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 Tasks // Avoid: Over-configured } /> ``` **Keep components focused:** ```tsx // Good: Does one thing export function TaskItem({ task, onToggle, onDelete }: TaskItemProps) { return (
  • onToggle(task.id)} /> {task.title}
  • ); } ``` **Separate data fetching from presentation:** ```tsx // Container: handles data export function TaskListContainer() { const { tasks, isLoading, error } = useTasks(); if (isLoading) return ; if (error) return ; if (tasks.length === 0) return ; return ; } // Presentation: handles rendering export function TaskList({ tasks }: { tasks: Task[] }) { return (
      {tasks.map(task => )}
    ); } ``` ## State Management **Choose the simplest approach that works:** ``` 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 ``` **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 | Production Quality | |---|---| | Purple/indigo everything | Use the project's actual color palette | | Excessive gradients | Flat or subtle gradients matching the design system | | Rounded everything (rounded-2xl) | Consistent border-radius from the design system | | Generic hero sections | Content-first layouts | | Lorem ipsum-style copy | Realistic placeholder content | | Oversized padding everywhere | Consistent spacing scale | | Stock card grids | Purpose-driven layouts | | Shadow-heavy design | 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 title h3 → Subsection title body → Default text small → 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 // ✓ Focusable by default
    Click me
    // ✗ Not focusable
    onKeyDown={e => e.key === 'Enter' && handleClick()}> Click me
    ``` ### ARIA Labels ```tsx // Label interactive elements that lack visible text // Label form inputs // Or use aria-label when no visible label exists ``` ### Focus Management ```tsx // Move focus when content changes function Dialog({ isOpen, onClose }: DialogProps) { const closeRef = useRef(null); useEffect(() => { if (isOpen) closeRef.current?.focus(); }, [isOpen]); // Trap focus inside dialog when open return ( {/* dialog content */} ); } ``` ### Meaningful Empty and Error States ```tsx // Don't show blank screens function TaskList({ tasks }: { tasks: Task[] }) { if (tasks.length === 0) { return (

    No tasks

    Get started by creating a new task.

    ); } return
      ...
    ; } ``` ## Responsive Design Design for mobile first, then expand: ```tsx // Tailwind: mobile-first responsive
    ``` Test at these breakpoints: 320px, 768px, 1024px, 1440px. ## Loading and Transitions ```tsx // Skeleton loading (not spinners for content) function TaskListSkeleton() { return (
    {Array.from({ length: 3 }).map((_, i) => (
    ))}
    ); } // 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); }, }); } ``` ## 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