shared/guidelines/frontend-design
Source:
shared/guidelines/frontend-design.md
guideline: frontend design
Loaded when the task touches
*.{tsx,jsx,vue,svelte,astro,html,css,scss}or the user mentions UI / component / page / layout / design.
Hard rules
- Component boundary = data + behavior + appearance for one purpose. Split when any of the three doesn’t fit.
- Server-rendered first (SSR / RSC / static) unless interactivity is essential. Client JS is a cost paid by every visit.
- No new global state without naming the boundary. Pages own routes; features own user-flow state; shared state needs justification.
- No prop drilling >2 levels. Compose, context, or co-locate.
- Accessibility is non-optional. Every interactive element has a name, role, and keyboard path. Hold to WCAG 2.2 AA; see
accessibility.md. - Performance budget is per-route. JS ≤ 200KB gzipped initial route; LCP ≤ 2.5s on 4G; CLS < 0.1. Track regressions.
- Design tokens > magic numbers. Pull from the design system or
tailwind.config; never hex-code-in-jsx.
Decision frames
- Class component vs hooks: hooks always (React 16.8+). Class only to extend legacy.
- CSS-in-JS vs utility classes vs CSS modules: match repo convention. If new repo: utility classes (Tailwind) for speed, CSS modules for componentized teams.
- Form library: native
<form>+ minimal validation lib for simple;react-hook-formfor complex multi-step. - State manager: TanStack Query for server state; Zustand / Jotai for client state; Redux only if the team already uses it.
- Date / time: dayjs / date-fns over Moment. Never roll your own.
Anti-patterns
useEffectto fetch data on mount. Use a query lib (TanStack Query / SWR).useEffectthat watches multiple deps with conditional setState inside — usually means the derivation belongs above, not in effects.- Wrapping every component in
React.memo. Memo only the ones with measurable re-render cost. dangerouslySetInnerHTMLon user content. Use a sanitizer if you must.- Reaching into a child component’s DOM via
refto mutate. Lift state instead. - Building a custom modal / popover when the headless lib (Radix, Headless UI) exists.
Cite when reviewing
- Repo’s design system (Storybook URL if present in
package.jsonorrepos.md frontmatterrepos[*].design_system_url“). - Repo’s existing component patterns (grep before suggesting “add a new component”).
accessibility.mdfor keyboard / contrast / aria.performance.mdfor budget violations.