ui-engineering
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? (
buttonnotdiv onClick,navnotdiv 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
- 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, oraria-labelledby) - Disabled state removes from tab order or applies
aria-disabled - Touch target meets 44x44px minimum on mobile
Forms
- Every input has a visible
<label>(placeholder alone is insufficient) - Required fields indicated (asterisk plus
aria-required="true") - Error messages connected to their input (
aria-describedby) - Error indication uses more than color alone (icon plus text)
- Submission outcomes announced to screen readers (
aria-live)
Images and Icons
- Meaningful images carry descriptive
alttext - Decorative images carry
alt=""oraria-hidden="true" - Icon-only buttons carry
aria-label - SVG icons use
role="img"witharia-labeloraria-hidden="true"
Color and Contrast
- Normal text meets 4.5:1 contrast ratio (3:1 for large text: 18px+ bold or 24px+)
- Information is never conveyed by color alone (supplement with icons, patterns, text)
- Motion respects
prefers-reduced-motion - Color scheme respects
prefers-color-schemeif dark mode is offered
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/heightattributes (prevents CLS),loading="lazy"for below-fold images,srcsetfor 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
More from noobygains/godmode
intent-discovery
Use when starting any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements, and design before implementation.
15agent-messaging
Use when dispatching subagents, composing prompts for teammates, structuring handoff reports, or managing context boundaries between agents. Covers both subagent prompts and team-level messaging.
15fault-diagnosis
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
15merge-protocol
Use when implementation is finished, tests are green, and you need to decide how to land the work - presents structured integration paths for local merge, pull request, deferral, or abandonment
14quality-enforcement
Use when preparing code for commit, PR, or merge - covers linting, type safety, bundle budgets, coverage thresholds, complexity limits, dependency audit, and dead code detection
14pattern-matching
Use when contributing code to an existing project - guarantees that every new line mirrors the established conventions, naming schemes, architectural layering, directory layout, and stylistic choices already present in the codebase rather than drifting toward generic AI defaults
14