| name | Create UI Component |
| description | Guidance for building a well-structured, accessible, and testable frontend UI component aligned with the project's component model and design system. |
Skill: Create UI Component
Purpose
Produce a reusable, accessible, and testable UI component that fits the project's design system, state management approach, and component conventions defined in TECH.md.
When to Use
- Adding a new piece of UI that will be rendered in a browser.
- Extracting repeated markup/logic into a shared component.
- Building a form, table, modal, page section, or interactive widget.
Prerequisites
Checklist
Component Design
- Single responsibility — one component does one thing. If it does more than one thing, split it.
- Props/interface first — define the component's TypeScript interface or prop types before writing the implementation.
- Default props — provide sensible defaults for optional props.
- Controlled vs. uncontrolled — decide explicitly; prefer controlled components for forms and interactive elements.
Implementation
- Keep the component pure where possible — derive output from props/state, avoid side effects in render.
- Extract logic to a custom hook if the component has significant state or async behavior.
- Avoid inline styles — use the project's design system tokens, utility classes, or styled components per TECH.md conventions.
- Handle all visual states explicitly: loading, empty, error, success, disabled.
Accessibility (a11y)
- Semantic HTML first — use
<button>, <nav>, <main>, <section> etc. before reaching for <div>.
- Keyboard navigation — interactive elements must be focusable and operable via keyboard.
- ARIA attributes — add
aria-label, aria-describedby, role only when semantic HTML is insufficient.
- Color is not the only signal — never rely on color alone to convey state (use icons, labels, or patterns too).
State Management
- Local state first — use component state (
useState) for state that doesn't need to be shared.
- Lift state only when two or more sibling components need to share it.
- Global state (Redux, Zustand, Pinia, etc.) only for truly application-wide concerns.
Testing
- Test rendered output and user interactions, not internal component state.
- Cover all visual states — render the component in each state (loading, error, success, empty).
- Test accessibility — use testing-library's accessible queries (
getByRole, getByLabelText) rather than getByTestId.
- Mock external dependencies (API calls, context providers) at the component boundary.
Documentation
- Add a JSDoc comment for complex props or non-obvious behavior.
- Export from the appropriate barrel file (
index.ts) per project conventions.
Quality Bar
- Component renders correctly in all defined visual states.
- Component is keyboard-operable and has no missing ARIA roles for interactive elements.
- At least one test per visual state and one test per user interaction.
- No hardcoded colors, sizes, or spacing outside the design system tokens.
Common Pitfalls
- God components — a single component that handles too many concerns; split at natural boundaries.
- Skipping loading/error states — every async operation needs explicit UI feedback.
- Inaccessible interactive elements —
<div onClick> instead of <button>; missing focus management in modals.
- Prop drilling — passing props through many layers instead of lifting to context or global state.
- Testing implementation details — asserting on CSS class names or internal state instead of visible behavior.