Frontend Components
Framework selection (one-line picker)
| Need |
Pick |
| Largest ecosystem, RSC/Next.js |
React |
| Low-boilerplate SFCs, Nuxt |
Vue |
| Smallest bundle, compiler-driven |
Svelte |
| Framework-agnostic / design-system primitive |
Web Components (Lit) |
Deep comparison: react-patterns.md, vue-patterns.md, svelte-patterns.md, web-components.md.
Workflow: build a new component
- Define props API — name, types, required vs optional, default values, controlled-vs-uncontrolled pattern (accept optional
value + onChange).
- Render markup — semantic HTML first, then styling.
- Wire state/effects — local state for ephemeral UI; lift to parent for shared.
- Add a11y — role, aria-*, keyboard handlers, focus management. Verify with axe DevTools.
- Write tests — render, interaction, a11y snapshot.
- Document — Storybook story per variant + states (default, loading, error, disabled).
Checkpoint after step 4: run npx @axe-core/cli http://localhost:6006/iframe?id=<story> — fail build if any violation.
Checkpoint after step 5: branch coverage on the component file must be >= 80% (vitest run --coverage).
Reference template (React + Suspense + custom hook)
import { Suspense, use, useEffect, useId, useState } from "react";
function useDebounced<T>(value: T, ms = 300): T {
const [v, setV] = useState(value);
useEffect(() => { const t = setTimeout(() => setV(value), ms); return () => clearTimeout(t); }, [value, ms]);
return v;
}
export function SearchBox({ value, onChange, resultsPromise }: {
value: string; onChange: (s: string) => void; resultsPromise: Promise<string[]>;
}) {
const id = useId();
const debounced = useDebounced(value);
return (
<div role="search">
<label htmlFor={id}>Search</label>
<input id={id} value={value} => onChange(e.target.value)} aria-describedby={`${id}-hint`} />
<Suspense fallback={<p>Loading…</p>}>
<Results promise={resultsPromise} query={debounced} />
</Suspense>
</div>
);
}
function Results({ promise }: { promise: Promise<string[]>; query: string }) {
const items = use(promise);
return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;
}
Vue SFC, Svelte 5 runes, and Lit equivalents (same controlled-input + debounce pattern) live in vue-patterns.md, svelte-patterns.md, and web-components.md.
Pre-merge gates
npx size-limit # fail if component adds >5kb gzipped
npx @axe-core/cli http://localhost:6006/iframe.html?id=<story> # zero violations
vitest run --coverage # branch coverage ≥80% on the component file
Long lists (>100 rows): use react-window / vue-virtual-scroller / svelte-virtual-list. Code-split modal/admin features (React.lazy, defineAsyncComponent, dynamic import()).
Next Steps
1---2name: frontend-components3description: Use when implementing or scaffolding a reusable UI component in React, Vue, Svelte, or Web Components/Lit; when porting a design across frameworks; or when a component needs hooks, composables, reactive state, slots, or shadow-DOM encapsulation.4---56# Frontend Components78## Framework selection (one-line picker)910| Need | Pick |11|------|------|12| Largest ecosystem, RSC/Next.js | React |13| Low-boilerplate SFCs, Nuxt | Vue |14| Smallest bundle, compiler-driven | Svelte |15| Framework-agnostic / design-system primitive | Web Components (Lit) |1617Deep comparison: [react-patterns.md](references/react-patterns.md), [vue-patterns.md](references/vue-patterns.md), [svelte-patterns.md](references/svelte-patterns.md), [web-components.md](references/web-components.md).1819## Workflow: build a new component20211. **Define props API** — name, types, required vs optional, default values, controlled-vs-uncontrolled pattern (accept optional `value` + `onChange`).222. **Render markup** — semantic HTML first, then styling.233. **Wire state/effects** — local state for ephemeral UI; lift to parent for shared.244. **Add a11y** — role, aria-*, keyboard handlers, focus management. Verify with axe DevTools.255. **Write tests** — render, interaction, a11y snapshot.266. **Document** — Storybook story per variant + states (default, loading, error, disabled).2728**Checkpoint after step 4:** run `npx @axe-core/cli http://localhost:6006/iframe?id=<story>` — fail build if any violation.29**Checkpoint after step 5:** branch coverage on the component file must be >= 80% (`vitest run --coverage`).3031## Reference template (React + Suspense + custom hook)3233```tsx34import { Suspense, use, useEffect, useId, useState } from "react";3536function useDebounced<T>(value: T, ms = 300): T {37 const [v, setV] = useState(value);38 useEffect(() => { const t = setTimeout(() => setV(value), ms); return () => clearTimeout(t); }, [value, ms]);39 return v;40}4142export function SearchBox({ value, onChange, resultsPromise }: {43 value: string; onChange: (s: string) => void; resultsPromise: Promise<string[]>;44}) {45 const id = useId();46 const debounced = useDebounced(value);47 return (48 <div role="search">49 <label htmlFor={id}>Search</label>50 <input id={id} value={value} onChange={(e) => onChange(e.target.value)} aria-describedby={`${id}-hint`} />51 <Suspense fallback={<p>Loading…</p>}>52 <Results promise={resultsPromise} query={debounced} />53 </Suspense>54 </div>55 );56}57function Results({ promise }: { promise: Promise<string[]>; query: string }) {58 const items = use(promise);59 return <ul>{items.map((i) => <li key={i}>{i}</li>)}</ul>;60}61```6263Vue SFC, Svelte 5 runes, and Lit equivalents (same controlled-input + debounce pattern) live in [vue-patterns.md](references/vue-patterns.md), [svelte-patterns.md](references/svelte-patterns.md), and [web-components.md](references/web-components.md).6465## Pre-merge gates6667```bash68npx size-limit # fail if component adds >5kb gzipped69npx @axe-core/cli http://localhost:6006/iframe.html?id=<story> # zero violations70vitest run --coverage # branch coverage ≥80% on the component file71```7273Long lists (>100 rows): use `react-window` / `vue-virtual-scroller` / `svelte-virtual-list`. Code-split modal/admin features (`React.lazy`, `defineAsyncComponent`, dynamic `import()`).7475## Next Steps7677- **[Component Library](../component-library/SKILL.md)**: docs, testing, versioning78- **[CSS Architecture](../css-architecture/SKILL.md)**: styling strategy79- **[Design System Creation](../design-system-creation/SKILL.md)**: tokens + governance80- **[Accessibility Audit](../accessibility-audit/SKILL.md)**: WCAG 2.2 AA81- **[Motion Design](../motion-design/SKILL.md)**: purposeful animation