Frontend UI Engineering
Decide how to structure a UI unit before writing it, so the choices your audits would otherwise catch late (accessibility, i18n, bundle cost, missing states) are made up front. This is the frontend counterpart to codebase-design: treat a component as a deep module — a lot of behaviour behind a small prop surface.
It is a knowledge/decision skill, not a code generator. It resolves the design questions, then hands the actual implementation to tdd/build and verification to the audit skills. Framework-agnostic; examples in React.
Boundaries:
- vs
tdd / build — they implement; this decides the UI shape and hands off.
- vs
codebase-design — that is generic module depth; this applies the lens to components (prop surface = public API).
- vs
prototype — that explores throwaway directions; this is for code that stays.
- vs
a11y-audit / i18n / perf-audit / visual-validate — they verify after; this builds it right up front. It references their standards, never re-copies their checklists.
Workflow
Phase 1 — Classify the work
Name the unit and pick the emphasis:
| Kind |
Emphasis |
| Design-system primitive (Button, Input) |
prop API, variants via props, tokens, a11y contract |
| Composite component |
composition, state placement, the required states |
| Page / route |
data fetching boundary, layout, loading/error at the route |
| Refactor of existing UI |
shrink prop surface, extract states, remove magic values |
Phase 2 — Decision pass (resolve before coding)
Work through each; state the decision explicitly.
- Boundary & prop API. Smallest surface that serves callers. Prefer composition (
children/slots) over configuration. A growing set of boolean flags (isPrimary, isSmall, isLoading…) is a smell — reach for a variant/size union or composition instead.
- Where state lives. The lowest owner that still works: local → lifted to parent → server state → URL. No derived state stored twice; compute from source.
- Data & async. Decide where data is fetched (route/loader boundary vs. inside the component) and who owns caching. Keep components that render data separate from components that fetch it where practical.
- Required UI states. Enumerate and design them now, not later: loading, empty, error, disabled, and partial/optimistic where relevant. A component that only handles the happy path is unfinished.
- Styling. Use design-system tokens (no magic colors/spacing). Express variants through props, not one-off class overrides.
- Responsive / adaptive. Mobile-first; intentional breakpoints; no fixed widths that break small screens.
- Accessibility by construction. Semantic HTML first, ARIA only as a last resort; keyboard + focus order; a label for every control; visible focus. (Defers to
a11y-audit for the full WCAG pass.)
- User-facing strings. Route them through the project's i18n path from the start rather than hardcoding. (Defers to
i18n.)
Phase 3 — Build
Apply the decisions. Keep the component shallow at its interface and hide implementation detail behind it. Delegate any non-trivial logic to tdd (red → green → refactor); this skill does not hand-write the whole component or its tests.
Phase 4 — Definition of done + handoff
Do not consider the unit done until:
Then hand off: tdd for behavior tests (query by role/label), visual-validate for before/after in a real browser, and let review run the audits (a11y/i18n/perf) as the final gate.
Rules
- Resolve the Phase 2 decisions explicitly before writing the component — state each choice.
- Reference the audit skills' standards; never duplicate their checklists here.
- Never hand-write full tests — that is
tdd. This skill designs; it does not replace the builders.
- Framework-agnostic; do not assume a specific framework unless the codebase already uses one.
- Stay within the current unit — do not redesign unrelated components.
Anti-rationalization
Excuses that skip the work, and the rebuttal:
| Excuse |
Rebuttal |
| "I'll add the error/empty state later." |
Later never comes and it ships half-built. States are part of the design pass, not a follow-up. |
| "A boolean prop is faster right now." |
Three booleans is eight states, most untested. A variant union or composition is cheaper within a week. |
| "Hardcode the string, I'll translate later." |
Retro-i18n means re-touching every component. Route it now — it's one import. |
| "ARIA will fix the accessibility." |
Semantic HTML gives it for free; ARIA is the patch for when you couldn't. Reach for the element first. |
| "Fetch inside the component, it's simpler." |
It couples render to network and blocks reuse/testing. Decide the fetch boundary deliberately. |
Red flags
- A prop list longer than the component's own logic.
any/loose props at the component boundary.
- Only the happy path rendered.
- Magic color/spacing values instead of tokens.
div/span with click handlers instead of a button/a.
1---2name: frontend-ui-engineering3description: Front-load UI construction decisions — component boundary and prop API, where state lives, data/async, the required UI states (loading/empty/error), responsive behavior, and accessibility by construction. Use when creating or refactoring a component, page, or design-system primitive. Don't use for pure logic (tdd), throwaway direction exploration (prototype), generic module design (codebase-design), or auditing finished UI (a11y-audit/perf-audit/visual-validate).4---56# Frontend UI Engineering78Decide **how to structure a UI unit before writing it**, so the choices your audits would otherwise catch late (accessibility, i18n, bundle cost, missing states) are made up front. This is the frontend counterpart to [`codebase-design`](../codebase-design/SKILL.md): treat a component as a **deep module** — a lot of behaviour behind a small prop surface.910It is a **knowledge/decision skill**, not a code generator. It resolves the design questions, then hands the actual implementation to `tdd`/`build` and verification to the audit skills. Framework-agnostic; examples in React.1112Boundaries:1314- vs `tdd` / `build` — they implement; this decides the UI shape and hands off.15- vs `codebase-design` — that is generic module depth; this applies the lens to components (prop surface = public API).16- vs `prototype` — that explores throwaway directions; this is for code that stays.17- vs `a11y-audit` / `i18n` / `perf-audit` / `visual-validate` — they verify after; this builds it right up front. It **references** their standards, never re-copies their checklists.1819## Workflow2021### Phase 1 — Classify the work2223Name the unit and pick the emphasis:2425| Kind | Emphasis |26|------|----------|27| Design-system primitive (Button, Input) | prop API, variants via props, tokens, a11y contract |28| Composite component | composition, state placement, the required states |29| Page / route | data fetching boundary, layout, loading/error at the route |30| Refactor of existing UI | shrink prop surface, extract states, remove magic values |3132### Phase 2 — Decision pass (resolve before coding)3334Work through each; state the decision explicitly.35361. **Boundary & prop API.** Smallest surface that serves callers. Prefer **composition** (`children`/slots) over configuration. A growing set of boolean flags (`isPrimary`, `isSmall`, `isLoading`…) is a smell — reach for a `variant`/`size` union or composition instead.372. **Where state lives.** The lowest owner that still works: local → lifted to parent → server state → URL. No derived state stored twice; compute from source.383. **Data & async.** Decide where data is fetched (route/loader boundary vs. inside the component) and who owns caching. Keep components that render data separate from components that fetch it where practical.394. **Required UI states.** Enumerate and design them now, not later: **loading, empty, error, disabled**, and partial/optimistic where relevant. A component that only handles the happy path is unfinished.405. **Styling.** Use design-system **tokens** (no magic colors/spacing). Express variants through props, not one-off class overrides.416. **Responsive / adaptive.** Mobile-first; intentional breakpoints; no fixed widths that break small screens.427. **Accessibility by construction.** Semantic HTML first, ARIA only as a last resort; keyboard + focus order; a label for every control; visible focus. (Defers to [`a11y-audit`](../a11y-audit/SKILL.md) for the full WCAG pass.)438. **User-facing strings.** Route them through the project's i18n path from the start rather than hardcoding. (Defers to [`i18n`](../i18n/SKILL.md).)4445### Phase 3 — Build4647Apply the decisions. Keep the component shallow at its interface and hide implementation detail behind it. Delegate any non-trivial logic to `tdd` (red → green → refactor); this skill does not hand-write the whole component or its tests.4849### Phase 4 — Definition of done + handoff5051Do not consider the unit done until:5253- [ ] Prop surface is minimal; no boolean-flag proliferation54- [ ] Loading, empty, error, and disabled states exist55- [ ] Keyboard reachable and operable; semantic elements used56- [ ] Styling uses tokens; responsive down to the smallest target57- [ ] User-facing strings are translatable5859Then hand off: **`tdd`** for behavior tests (query by role/label), **`visual-validate`** for before/after in a real browser, and let **`review`** run the audits (a11y/i18n/perf) as the final gate.6061## Rules6263- Resolve the Phase 2 decisions explicitly before writing the component — state each choice.64- Reference the audit skills' standards; never duplicate their checklists here.65- Never hand-write full tests — that is `tdd`. This skill designs; it does not replace the builders.66- Framework-agnostic; do not assume a specific framework unless the codebase already uses one.67- Stay within the current unit — do not redesign unrelated components.6869## Anti-rationalization7071Excuses that skip the work, and the rebuttal:7273| Excuse | Rebuttal |74|--------|----------|75| "I'll add the error/empty state later." | Later never comes and it ships half-built. States are part of the design pass, not a follow-up. |76| "A boolean prop is faster right now." | Three booleans is eight states, most untested. A `variant` union or composition is cheaper within a week. |77| "Hardcode the string, I'll translate later." | Retro-i18n means re-touching every component. Route it now — it's one import. |78| "ARIA will fix the accessibility." | Semantic HTML gives it for free; ARIA is the patch for when you couldn't. Reach for the element first. |79| "Fetch inside the component, it's simpler." | It couples render to network and blocks reuse/testing. Decide the fetch boundary deliberately. |8081## Red flags8283- A prop list longer than the component's own logic.84- `any`/loose props at the component boundary.85- Only the happy path rendered.86- Magic color/spacing values instead of tokens.87- `div`/`span` with click handlers instead of a `button`/`a`.