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.
Pre-Implementation Checklist
Before writing any component:
- Semantics -- Which HTML element is correct? (
button not div onClick, nav not div className="nav")
- Props -- What is the minimal surface area? Can it be composed instead of configured?
- States -- Default, hover, focus, active, disabled, loading, error, empty
- Variants -- What visual variations are needed? (primary, secondary, ghost, destructive)
- Sizes -- What size tiers exist? (sm, md, lg -- maximum 3-4)
- Responsive -- How does it transform at each breakpoint?
- Accessibility -- ARIA roles, keyboard navigation paths, screen reader announcements
Layout Strategy Selection
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:
/* 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:
/* 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
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:
- Begin with local state. Elevate only when evidence demands it.
- Server data is NOT client state. Manage it with a dedicated server-state library.
- Context is for dependency injection (themes, auth context), not for high-frequency updates.
- 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.
/* 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
Forms
Images and Icons
Color and Contrast
Animation and Motion
When to Animate
- State transitions: Hover, focus, expand/collapse, reveal/hide
- Feedback signals: Success, error, loading progress
- Spatial cues: Communicating where content originated or departed
When NOT to Animate
- Decoration with no functional purpose
- Durations exceeding 300ms for UI transitions
- Motion that blocks user interaction (forced wait)
- Continuous movement without user control
Timing Reference
| Interaction |
Duration |
Easing |
| Hover response |
150ms |
ease |
| Button press |
100ms |
ease-out |
| Modal entrance |
200ms |
ease-out |
| Modal exit |
150ms |
ease-in |
| Drawer slide |
250ms |
ease-out |
| Fade entrance |
200ms |
ease |
| Page transition |
200-300ms |
ease-in-out |
Respect User Motion Preferences
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Form Architecture
Validation Experience
- Do not validate on every keystroke. Validate on blur (field exit) or on form submission.
- Display errors inline beneath the offending field, not in a summary banner.
- Error text displaces helper text -- never display both simultaneously.
- Error indication uses color plus icon -- never color alone (accessibility).
- Retain valid-field indicators (checkmark or green border) as the user progresses.
- Disable the submit button during submission and show a loading indicator.
Field Layout Guidelines
- Labels above inputs (never beside, never placeholder-only)
- Related fields grouped with
<fieldset> and <legend>
- Required fields marked with asterisk AND
(required) for screen readers
- Optional fields may display "(optional)" instead
- Maximum form width: 480-560px single-column
- Submit button left-aligned, not centered (exception: inside modals)
Rendering Performance
Critical Path Optimization
- Above-fold content loads first. Defer below-fold assets and scripts.
- Prefer system fonts for body text unless branding mandates a web font. Web fonts introduce layout shift.
- Image discipline: Apply
width/height attributes (prevents CLS), loading="lazy" for below-fold images, srcset for responsive delivery.
- Bundle awareness: Every imported library is a cost. Measure bundle impact before adding dependencies.
Lazy Loading Strategy
- Below-fold images:
loading="lazy"
- Route modules: dynamic import / code splitting
- Heavy components (charts, rich editors): load on interaction or visibility
- Never lazy-load above-fold content
Common Mistakes
| Mistake |
Correction |
div for everything |
Use semantic HTML (button, nav, main, section, article) |
| Placeholder as sole label |
Always provide visible <label> elements |
| Pixel literals everywhere |
Use token variables from the design system |
| Fixed widths on containers |
Use max-width + percentage or auto margins |
| Removing focus outlines |
Style :focus-visible instead of eliminating outline |
| Click handlers on divs |
Use <button> or <a> for interactive elements |
| Margins for layout spacing |
Use gap with Grid or Flexbox |
| Importing entire icon libraries |
Import individual icons; enable tree-shaking |
| Only implementing the happy path |
Design all states before building the happy path |
| Testing only on desktop |
Test mobile-first, verify desktop afterward |
Integration
Prerequisite:
- godmode:ux-patterns -- Tokens and patterns must be established first
Complementary skills:
- godmode:design-integration -- When operating within an established design system
- godmode:test-first -- Component tests follow test-first methodology
Supporting files:
component-patterns.md -- Reusable component blueprints with accessibility built in
1---2name: ui-engineering3description: 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 framework4---56# UI Engineering78## Overview910Construct UI components with disciplined architecture, not improvised markup.1112**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.1314**PREREQUISITE:** Invoke godmode:ux-patterns first to identify applicable patterns and token values. This skill assumes tokens and patterns are already established.1516## When to Use1718**Mandatory when:**19- Constructing any frontend component20- Selecting between Grid and Flexbox21- Deciding where state should live22- Engineering responsive breakpoint behavior23- Building forms, data visualizations, navigation, or overlays24- Introducing motion or transitions2526**Sequenced after:**27- Design tokens are established (ux-patterns)28- Target UI pattern is identified (ux-patterns)2930## The Prime Directive3132```33NO COMPONENT WITHOUT STRUCTURE, STATES, AND ACCESSIBILITY DEFINED FIRST34```3536Before 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).3738## Component Architecture3940### Composition Over Configuration4142Assemble components from smaller, composable units rather than monolithic prop-heavy blocks.4344<Good>45```46Dialog47 DialogHeader48 DialogTitle49 DialogDescription50 DialogBody51 DialogFooter52```53Each unit is independently useful and independently styleable.54</Good>5556<Bad>57```58Dialog (title, description, body, footer, headerAlign, footerAlign,59 showCloseButton, variant, size, overlayOpacity, titleSize, ...)60```61Prop explosion, unmaintainable, impossible to extend.62</Bad>6364### Pre-Implementation Checklist6566Before writing any component:67681. **Semantics** -- Which HTML element is correct? (`button` not `div onClick`, `nav` not `div className="nav"`)692. **Props** -- What is the minimal surface area? Can it be composed instead of configured?703. **States** -- Default, hover, focus, active, disabled, loading, error, empty714. **Variants** -- What visual variations are needed? (primary, secondary, ghost, destructive)725. **Sizes** -- What size tiers exist? (sm, md, lg -- maximum 3-4)736. **Responsive** -- How does it transform at each breakpoint?747. **Accessibility** -- ARIA roles, keyboard navigation paths, screen reader announcements7576## Layout Strategy Selection7778```dot79digraph layout_choice {80 rankdir=TB;81 q1 [label="What is being\narranged?", shape=diamond];82 q2 [label="Single axis or\ntwo axes?", shape=diamond];83 q3 [label="Are items\nuniform in size?", shape=diamond];8485 grid [label="Use CSS Grid\ngrid-template-columns\ngrid-template-rows", shape=box];86 flex [label="Use Flexbox\nflex-direction\njustify/align", shape=box];87 grid_auto [label="Use CSS Grid\nauto-fill/auto-fit\nminmax()", shape=box];8889 q1 -> q2;90 q2 -> grid [label="two axes\n(rows AND columns)"];91 q2 -> q3 [label="single axis\n(row OR column)"];92 q3 -> grid_auto [label="yes\n(uniform cards)"];93 q3 -> flex [label="no\n(nav items,\nform row)"];94}95```9697### CSS Grid -- Appropriate When9899- Page-level scaffolding (sidebar + main content + aside)100- Uniform card grids101- Dashboard arrangements (metric tiles, chart regions)102- Any layout requiring two-dimensional control103- Cross-row and cross-column alignment104105**Example patterns:**106```css107/* Self-adjusting card grid */108.card-grid {109 display: grid;110 grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));111 gap: var(--space-6);112}113114/* Dashboard scaffold */115.dashboard {116 display: grid;117 grid-template-columns: 1fr 1fr 1fr;118 grid-template-rows: auto 1fr;119 gap: var(--space-6);120}121.dashboard .wide-chart { grid-column: span 2; }122```123124### Flexbox -- Appropriate When125126- Navigation items along a single row127- Form input + button inline grouping128- Centering content within a container129- Distributing variable-width items along one axis130131**Example patterns:**132```css133/* Navigation row */134.nav-row {135 display: flex;136 align-items: center;137 gap: var(--space-4);138}139140/* Inline form group */141.inline-group {142 display: flex;143 align-items: flex-end;144 gap: var(--space-3);145}146.inline-group .input-field { flex: 1; }147```148149## State Ownership Strategy150151```dot152digraph state_ownership {153 rankdir=TB;154 q1 [label="Where does this\nstate belong?", shape=diamond];155 q2 [label="Consumed by\nmultiple components?", shape=diamond];156 q3 [label="Server-originated\nor client-only?", shape=diamond];157 q4 [label="Prop-drilling\nexceeds 3 levels?", shape=diamond];158159 local [label="Component-local state\nuseState / ref", shape=box];160 server [label="Server state manager\nTanStack Query / SWR", shape=box];161 context [label="Context / Provider\nReact Context / provide-inject", shape=box];162 global [label="Global store\nZustand / Pinia / Signals", shape=box];163164 q1 -> q2;165 q2 -> local [label="no\n(isolated component)"];166 q2 -> q3 [label="yes"];167 q3 -> server [label="server-originated\n(API responses,\ncached data)"];168 q3 -> q4 [label="client-only\n(UI flags,\npreferences)"];169 q4 -> context [label="no\n(2-3 levels)"];170 q4 -> global [label="yes\n(application-wide)"];171}172```173174**Governing principles:**1751. Begin with local state. Elevate only when evidence demands it.1762. Server data is NOT client state. Manage it with a dedicated server-state library.1773. Context is for dependency injection (themes, auth context), not for high-frequency updates.1784. Global stores are a last resort, not a starting point.179180## Responsive Design Methodology181182### Mobile-First Progression183184Write mobile styles as the baseline, then layer complexity at wider breakpoints.185186```css187/* Mobile baseline */188.wrapper {189 padding: var(--space-4);190}191192/* Tablet tier */193@media (min-width: 768px) {194 .wrapper {195 padding: var(--space-6);196 max-width: 768px;197 margin: 0 auto;198 }199}200201/* Desktop tier */202@media (min-width: 1024px) {203 .wrapper {204 padding: var(--space-8);205 max-width: 1280px;206 }207}208```209210### Responsive Adaptation Reference211212| Element | Mobile | Tablet | Desktop |213|---|---|---|---|214| **Navigation** | Hamburger or bottom sheet | Tab bar or collapsed sidebar | Expanded sidebar |215| **Card grid** | Single column | Two columns | Three to four columns |216| **Data table** | Stacked card view or horizontal scroll | Full table, fewer columns | Complete table |217| **Sidebar + Main** | Main only; sidebar in drawer | Icon-only sidebar | Fully expanded sidebar |218| **Form** | Single column, inputs stretch full width | Single column, max-width 560px | Two columns for paired fields |219| **Modal** | Full-screen sheet | Centered, 80% viewport width | Centered, max-width 480px |220| **Hero** | Stacked (image below headline) | Stacked, larger type | Side-by-side |221222## Accessibility Standards223224### Every Interactive Element225226- [ ] Reachable via Tab key227- [ ] Focus ring visible (`:focus-visible`, 2px outline minimum)228- [ ] Activatable via Enter/Space (buttons) or Enter (links)229- [ ] Possesses an accessible name (visible text, `aria-label`, or `aria-labelledby`)230- [ ] Disabled state removes from tab order or applies `aria-disabled`231- [ ] Touch target meets 44x44px minimum on mobile232233### Forms234235- [ ] Every input has a visible `<label>` (placeholder alone is insufficient)236- [ ] Required fields indicated (asterisk plus `aria-required="true"`)237- [ ] Error messages connected to their input (`aria-describedby`)238- [ ] Error indication uses more than color alone (icon plus text)239- [ ] Submission outcomes announced to screen readers (`aria-live`)240241### Images and Icons242243- [ ] Meaningful images carry descriptive `alt` text244- [ ] Decorative images carry `alt=""` or `aria-hidden="true"`245- [ ] Icon-only buttons carry `aria-label`246- [ ] SVG icons use `role="img"` with `aria-label` or `aria-hidden="true"`247248### Color and Contrast249250- [ ] Normal text meets 4.5:1 contrast ratio (3:1 for large text: 18px+ bold or 24px+)251- [ ] Information is never conveyed by color alone (supplement with icons, patterns, text)252- [ ] Motion respects `prefers-reduced-motion`253- [ ] Color scheme respects `prefers-color-scheme` if dark mode is offered254255## Animation and Motion256257### When to Animate258259- **State transitions:** Hover, focus, expand/collapse, reveal/hide260- **Feedback signals:** Success, error, loading progress261- **Spatial cues:** Communicating where content originated or departed262263### When NOT to Animate264265- Decoration with no functional purpose266- Durations exceeding 300ms for UI transitions267- Motion that blocks user interaction (forced wait)268- Continuous movement without user control269270### Timing Reference271272| Interaction | Duration | Easing |273|---|---|---|274| Hover response | 150ms | ease |275| Button press | 100ms | ease-out |276| Modal entrance | 200ms | ease-out |277| Modal exit | 150ms | ease-in |278| Drawer slide | 250ms | ease-out |279| Fade entrance | 200ms | ease |280| Page transition | 200-300ms | ease-in-out |281282### Respect User Motion Preferences283284```css285@media (prefers-reduced-motion: reduce) {286 *, *::before, *::after {287 animation-duration: 0.01ms !important;288 transition-duration: 0.01ms !important;289 }290}291```292293## Form Architecture294295### Validation Experience2962971. **Do not validate on every keystroke.** Validate on blur (field exit) or on form submission.2982. **Display errors inline** beneath the offending field, not in a summary banner.2993. **Error text displaces helper text** -- never display both simultaneously.3004. **Error indication uses color plus icon** -- never color alone (accessibility).3015. **Retain valid-field indicators** (checkmark or green border) as the user progresses.3026. **Disable the submit button during submission** and show a loading indicator.303304### Field Layout Guidelines305306- Labels above inputs (never beside, never placeholder-only)307- Related fields grouped with `<fieldset>` and `<legend>`308- Required fields marked with asterisk AND `(required)` for screen readers309- Optional fields may display "(optional)" instead310- Maximum form width: 480-560px single-column311- Submit button left-aligned, not centered (exception: inside modals)312313## Rendering Performance314315### Critical Path Optimization3163171. **Above-fold content loads first.** Defer below-fold assets and scripts.3182. **Prefer system fonts for body text** unless branding mandates a web font. Web fonts introduce layout shift.3193. **Image discipline:** Apply `width`/`height` attributes (prevents CLS), `loading="lazy"` for below-fold images, `srcset` for responsive delivery.3204. **Bundle awareness:** Every imported library is a cost. Measure bundle impact before adding dependencies.321322### Lazy Loading Strategy323324- Below-fold images: `loading="lazy"`325- Route modules: dynamic import / code splitting326- Heavy components (charts, rich editors): load on interaction or visibility327- Never lazy-load above-fold content328329## Common Mistakes330331| Mistake | Correction |332|---|---|333| `div` for everything | Use semantic HTML (`button`, `nav`, `main`, `section`, `article`) |334| Placeholder as sole label | Always provide visible `<label>` elements |335| Pixel literals everywhere | Use token variables from the design system |336| Fixed widths on containers | Use max-width + percentage or auto margins |337| Removing focus outlines | Style `:focus-visible` instead of eliminating outline |338| Click handlers on divs | Use `<button>` or `<a>` for interactive elements |339| Margins for layout spacing | Use `gap` with Grid or Flexbox |340| Importing entire icon libraries | Import individual icons; enable tree-shaking |341| Only implementing the happy path | Design all states before building the happy path |342| Testing only on desktop | Test mobile-first, verify desktop afterward |343344## Integration345346**Prerequisite:**347- **godmode:ux-patterns** -- Tokens and patterns must be established first348349**Complementary skills:**350- **godmode:design-integration** -- When operating within an established design system351- **godmode:test-first** -- Component tests follow test-first methodology352353**Supporting files:**354- `component-patterns.md` -- Reusable component blueprints with accessibility built in