---
name: frontend-ui-engineering
description: Build production-quality UI: components, layouts, state, accessibility, and polish that does not look AI-generated. Use when building or modifying user-facing interfaces, creating components, implementing responsive layouts, or fixing visual and UX issues. Not for commit and branch discipline around the change: use git-workflow-and-versioning. Not for pure design review without code.
license: MIT
metadata:
  author: TechTide AI (Alex Cinovoj)
  provenance: rewritten from patterns in addyosmani/agent-skills (MIT)
  category: Build & Implementation
---

# Frontend UI Engineering

Ship UI that looks like a design-aware engineer built it: real design system adherence, WCAG 2.1 AA accessibility, all four states handled (loading, error, empty, success). The recognizable "AI aesthetic" is a quality signal, and the signal says low.

## Workflow

1. **Learn the project's system first.** Find the spacing scale, color tokens, radius scale, and type hierarchy before writing a component. No design system: use a 0.25rem spacing scale, semantic tokens, and restraint.
2. **Structure the component.** Colocate component, test, hook, and types in one directory. Composition over configuration: children and slots beat a dozen props. Split anything past ~200 lines. Separate data fetching (container) from rendering (presentation). Read references/patterns.md when you need the code patterns for composition, container/presentation, focus management, or optimistic updates.
3. **Pick the simplest state tool that works.** useState for local UI, lifted state for 2-3 siblings, context for read-heavy globals (theme, auth), URL params for shareable state (filters, pagination), React Query/SWR for server data, a global store only for complex app-wide client state. Prop drilling past 3 levels means restructure.
4. **Handle all four states.** Every data-driven view renders loading (skeleton, not spinner), error (message plus retry), empty (guidance plus action), and success. A blank screen in any state is a bug.
5. **Build accessibility in.** Native elements first (`<button>`, not `<div onClick>`). Label every icon-only control and every input. Move focus on dialogs and route changes, trap it in modals. Contrast 4.5:1 normal text, 3:1 large. Never color as the only signal.
6. **Make it responsive mobile-first.** Base styles for 320px, expand with breakpoints. Test at 320, 768, 1024, 1440.
7. **Kill the AI aesthetic.** Checklist of defaults to reject: purple/indigo palette, gradients everywhere, rounded-2xl on everything, generic hero sections, lorem ipsum copy, uniform oversized padding, stock card grids, layered shadows. Replace each with the project's actual system and content-first layout.

## Good vs bad

**Bad:** A dashboard card grid: every card `rounded-2xl shadow-xl p-8`, indigo gradient header, spinner while loading, blank div when the list is empty, `<div onClick>` for the row actions.

**Good:** Cards use the project's radius and spacing tokens, the grid prioritizes the two metrics users scan first, loading renders a skeleton matching final layout, empty state says "No tasks yet" with a create button, rows are real buttons reachable by Tab.

## Verification

Do this pass on the finished UI. Tab through the entire page: every interactive element receives visible focus and activates with Enter/Space. Run axe-core (or the browser a11y devtools) and resize to 320px. Expect: zero console errors, zero axe violations, no horizontal scroll at 320px, and all four states reachable. Any failure: fix it before handing off, "works with a mouse at 1440px" is not done.

## Completion checklist

- [ ] Project design tokens used, no invented spacing or raw hex values
- [ ] Component colocated, under ~200 lines, composition-based
- [ ] State managed with the simplest sufficient tool
- [ ] Loading, error, empty, success all implemented
- [ ] Keyboard pass clean, labels on all unlabeled controls
- [ ] Contrast and non-color signals verified
- [ ] Works at 320, 768, 1024, 1440
- [ ] No AI-aesthetic defaults present

Any box unchecked: not done. Fix or say so.

## Red flags

Verbatim rationalizations that precede low-quality UI:

- "Accessibility is a nice-to-have for now"
- "We'll make it responsive later"
- "This is just a prototype"
- "The design isn't final, so styling can wait"
- "The AI look is fine for a first pass"

Closure rule: prototypes become production. If the code will be seen by a user or a reviewer, the standards above apply to it now, not later.

## Footguns

- **Spinner-for-everything loading.** Spinners cause layout shift and hide structure. Fix: skeletons that match the final layout, `aria-busy` on the container.
- **Div soup interactivity.** `<div onClick>` is invisible to keyboards and screen readers. Fix: native `<button>`/`<a>`; if forced, add role, tabIndex, and both Enter and Space handling.
- **Arbitrary values.** `padding: 13px` and `#7c3aed` scattered around defeat the system. Fix: tokens and scale values only; an arbitrary value is a signal you skipped step 1.
- **Optimistic updates without rollback.** UI updates, request fails, state lies. Fix: snapshot previous state in onMutate, restore it in onError (pattern in references/patterns.md).
