--- name: ui-engineering description: Use when constructing frontend components, selecting layout strategies, orchestrating state, or assembling interactive UI - spans component architecture, responsive adaptation, accessibility compliance, and rendering performance across any frontend framework --- # UI Engineering ## Overview Construct UI components with disciplined architecture, not improvised markup. **Core principle:** Every component decision -- layout strategy, state ownership, accessibility posture, responsive behavior -- follows established patterns. The UX Patterns skill tells you WHAT to build. This skill tells you HOW to build it with structural integrity. **PREREQUISITE:** Invoke godmode:ux-patterns first to identify applicable patterns and token values. This skill assumes tokens and patterns are already established. ## When to Use **Mandatory when:** - Constructing any frontend component - Selecting between Grid and Flexbox - Deciding where state should live - Engineering responsive breakpoint behavior - Building forms, data visualizations, navigation, or overlays - Introducing motion or transitions **Sequenced after:** - Design tokens are established (ux-patterns) - Target UI pattern is identified (ux-patterns) ## The Prime Directive ``` NO COMPONENT WITHOUT STRUCTURE, STATES, AND ACCESSIBILITY DEFINED FIRST ``` Before writing component code, establish: semantic structure (correct HTML elements), all visual states (empty, loading, error, populated, disabled), and accessibility requirements (ARIA attributes, keyboard interaction, contrast ratios). ## Component Architecture ### Composition Over Configuration Assemble components from smaller, composable units rather than monolithic prop-heavy blocks. ``` Dialog DialogHeader DialogTitle DialogDescription DialogBody DialogFooter ``` Each unit is independently useful and independently styleable. ``` Dialog (title, description, body, footer, headerAlign, footerAlign, showCloseButton, variant, size, overlayOpacity, titleSize, ...) ``` Prop explosion, unmaintainable, impossible to extend. ### Pre-Implementation Checklist Before writing any component: 1. **Semantics** -- Which HTML element is correct? (`button` not `div onClick`, `nav` not `div className="nav"`) 2. **Props** -- What is the minimal surface area? Can it be composed instead of configured? 3. **States** -- Default, hover, focus, active, disabled, loading, error, empty 4. **Variants** -- What visual variations are needed? (primary, secondary, ghost, destructive) 5. **Sizes** -- What size tiers exist? (sm, md, lg -- maximum 3-4) 6. **Responsive** -- How does it transform at each breakpoint? 7. **Accessibility** -- ARIA roles, keyboard navigation paths, screen reader announcements ## Layout Strategy Selection ```dot 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 -- Appropriate When - Page-level scaffolding (sidebar + main content + aside) - Uniform card grids - Dashboard arrangements (metric tiles, chart regions) - Any layout requiring two-dimensional control - Cross-row and cross-column alignment **Example patterns:** ```css /* 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 -- Appropriate When - Navigation items along a single row - Form input + button inline grouping - Centering content within a container - Distributing variable-width items along one axis **Example patterns:** ```css /* 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; } ``` ## State Ownership Strategy ```dot 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)"]; } ``` **Governing principles:** 1. Begin with local state. Elevate only when evidence demands it. 2. Server data is NOT client state. Manage it with a dedicated server-state library. 3. Context is for dependency injection (themes, auth context), not for high-frequency updates. 4. Global stores are a last resort, not a starting point. ## Responsive Design Methodology ### Mobile-First Progression Write mobile styles as the baseline, then layer complexity at wider breakpoints. ```css /* 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; } } ``` ### Responsive Adaptation Reference | Element | Mobile | Tablet | Desktop | |---|---|---|---| | **Navigation** | Hamburger or bottom sheet | Tab bar or collapsed sidebar | Expanded sidebar | | **Card grid** | Single column | Two columns | Three to four columns | | **Data table** | Stacked card view or horizontal scroll | Full table, fewer columns | Complete table | | **Sidebar + Main** | Main only; sidebar in drawer | Icon-only sidebar | Fully expanded sidebar | | **Form** | Single column, inputs stretch full width | Single column, max-width 560px | Two columns for paired fields | | **Modal** | Full-screen sheet | Centered, 80% viewport width | Centered, max-width 480px | | **Hero** | Stacked (image below headline) | Stacked, larger type | Side-by-side | ## Accessibility Standards ### Every Interactive Element - [ ] Reachable via Tab key - [ ] Focus ring visible (`:focus-visible`, 2px outline minimum) - [ ] Activatable via Enter/Space (buttons) or Enter (links) - [ ] Possesses an accessible name (visible text, `aria-label`, or `aria-labelledby`) - [ ] Disabled state removes from tab order or applies `aria-disabled` - [ ] Touch target meets 44x44px minimum on mobile ### Forms - [ ] Every input has a visible `