pattern-engineer-frontend-standard
Skill0 starsChanged 5 months agoMITView on GitHub
React frontend bullets: composition-first components, custom hooks, route registration + entry-source reachability (a real inbound path, not just a passing URL-render test) in one slice, render to the surface's `docs/ui-contract/<screen>.yaml` (declared role+accessible-name interface; render to E2E-locked `states`, add only frontend-only ones) + a contract-conformance test asserting that interface by role+name, route-param queries gated by `enabled: !!param`, `onSuccess` cache invalidation, idempotency-key rotation on 4xx, API via `src/lib/api`, Context+Reducer state, RHF+Zod forms, per-route error boundaries, native a11y elements, Tailwind ↔ `docs/design-system/tokens.md`. Activate on frontend `.tsx`/`.ts`.
In this repository74 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- create-bug-issueSkill
- create-enhancement-issueSkill
- create-feature-issuesSkill
- dream-summary-memorySkill
- memory-conventionSkill
- operation-gitSkill
- pattern-architect-api-endpointSkill
- pattern-architect-data-modelSkill
- pattern-architect-deep-moduleSkill
- pattern-e2e-coding-standardSkill
- pattern-engineer-apiSkill
- pattern-engineer-backend-standardSkill
- pattern-engineer-coding-standardSkill
- pattern-engineer-containerSkill
- pattern-engineer-databaseSkill
- pattern-engineer-fastapiSkill
- pattern-engineer-goSkill
- pattern-engineer-javaSkill
- pattern-engineer-kotlinSkill
- pattern-engineer-nodeSkill
- pattern-engineer-non-functionalSkill
- pattern-engineer-observabilitySkill
- pattern-engineer-pythonSkill
- pattern-engineer-rustSkill
- pattern-engineer-securitySkill
- pattern-engineer-ssrSkill
- pattern-engineer-swiftSkill
- pattern-engineer-typescriptSkill
- pattern-engineer-viteSkill
- pattern-exploratory-testingSkill
- pattern-reviewer-apiSkill
- pattern-reviewer-backend-standardSkill
- pattern-reviewer-coding-standardSkill
- pattern-reviewer-containerSkill
- pattern-reviewer-contractSkill
- pattern-reviewer-databaseSkill
- pattern-reviewer-fastapiSkill
- pattern-reviewer-frontend-standardSkill
- pattern-reviewer-goSkill
- pattern-reviewer-javaSkill
- pattern-reviewer-kotlinSkill
- pattern-reviewer-nodeSkill
- pattern-reviewer-non-functionalSkill
- pattern-reviewer-observabilitySkill
- pattern-reviewer-pythonSkill
- pattern-reviewer-rustSkill
- pattern-reviewer-securitySkill
- pattern-reviewer-ssrSkill
- pattern-reviewer-swiftSkill
- pattern-reviewer-test-coverageSkill
- pattern-reviewer-typescriptSkill
- pattern-reviewer-viteSkill
- pattern-test-coverageSkill
- principle-engineer-tddSkill
- scaffold-projectSkill
- workflow-architect-interviewSkill
- workflow-designer-sample-pagesSkill
- workflow-design-interviewSkill
- workflow-e2e-authorSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 0 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-engineer-frontend-standard.md in the consuming project …
- Very specific: line 36
When editing App.test.tsx, never overwrite the file wholesale — accumulate tests.
- Very specific: line 59
Every mutation that changes server state visible through another query calls queryClient.invalidateQueries({ queryKey: <KEY> }) in …
- Very specific: line 65
If transforming args before mutate, wrap with useCallback and a stable dep list.
- Very specific: line 82
ALL backend calls route through src/lib/api/<resource>.ts.
- Very specific: line 137
No hardcoded user-facing strings.
- Very specific: line 147
No hard-coded color values.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
pattern-engineer-frontend-standard
When to activate
Activate when writing or editing React components, hooks, pages, routes, layouts, forms, modals, lists, tables, or navigation in any React-based app (Next.js or Vite). Skip for pure backend code or non-React frontends.
Project memory overlay
After loading this skill, also check $MAIN_ROOT/.claude/memory/patterns/pattern-engineer-frontend-standard.md in the consuming project (resolve MAIN_ROOT="$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")"). If present, load it as an additive overlay to the rules below; if absent, skip silently. See memory-convention for the full contract (additivity, severity floor, conflict surfacing).
Patterns
Component design
- Composition over inheritance: pass behavior as props, structure as children.
- Compound components for related parts (
Tabs/Tabs.Tab) sharing state via internal Context — keep the Context unexported. - Render props /
childrenas a function when consumers own the markup but you own the logic. - Throw a clear error when a compound child is used outside its parent.
Custom hooks
- Extract any reusable stateful logic into a
useXhook. - Return a tuple for 2–3 values; an object once it grows past that.
- Wrap callbacks in
useCallbackso consumers can pass them to memoized children. - Async data fetching: use TanStack Query (or Next route loaders / server components). Hand-rolled
useFetchonly for tiny one-offs, and it MUST handle loading, error, and cancellation.
Routes + reachability
- A new page lands with BOTH the route registration in
App.tsxAND a matchingApp.test.tsxtest in the same slice. App.test.tsxasserts the page is reachable at its declared URL (render <App />inside<MemoryRouter initialEntries={[url]}>, expect heading).- When editing
App.test.tsx, never overwrite the file wholesale — accumulate tests;git diffit before commit. - A registered route is not a reachable page — the invariant is reachability. The task's declared Entry source (carried from
docs/design-system/surfaces.mdonto the task body) must exist as a real inbound path in code, and a test must exercise it:top-levelpage → an entry in the global-nav container (or an explicit redirect target). Test: render the shell, click the nav item, assert the page renders. A top-level page whose only "entry" is the route registration is an orphan — wire it into the nav (the nav lives in the foundation/shell slice, which ships first).detail-child/contextualpage → a link/row/control on its parent surface (e.g. a list row →/entities/:id, a "New" button →/entities/new). The linking control ships in the same slice as the page. Test: render the parent, activate the control, assert navigation.external-entrypage (login, magic-link) → entered via typed URL / email link; exempt from in-app linking. The URL-render test alone suffices.redirect-system(/→ home,*→ 404) → assert the redirect/fallback resolves.
- Cross-page navigation links (e.g. "Forgot password?" on
/login+ "Back to login" on/forgot) ship in the same slice as the page they reference.
UI interaction contract
The surface's docs/ui-contract/<screen>.yaml is the published source for the interface your page must expose — the UI analogue of docs/api-contract/<entity>.yaml. Render to it; it is what the E2E specs query and what pattern-reviewer-contract audits against. (When present — a project without docs/ui-contract/ falls back to the a11y floor below and the E2E specs as the affordance reference.)
- Render every declared
regionandactionwith its exactrole+ accessiblename. The name comes through visible text,aria-label, or a labelled control — exact wording and casing. A<div onClick>labelled "Post" where the contract declares abuttonnamed "Publish" is a conformance failure, not a style nit. - Ship a contract-conformance test for the surface — the executable firewall. Render the surface in a component test (RTL) and assert every declared
region+actionresolves bygetByRole(role, { name }), plus eachstatesentry this slice ships. Without it, contract conformance is only eyeballed at review and drifts; with it green, every E2E locator built from the contract's role+name is guaranteed to resolve — the locator-mismatch churn moves out of the brittle browser layer into a fast component test. The deletable line is therole/aria-label/labelled-control producing each accessible name: delete it and this test must fail. - The skeleton is fixed; render to it, don't diverge.
design-leadownsregions, primaryactions, and the accessibility baseline. If your page genuinely needs an element the skeleton lacks, surface it as a contract gap (an off-skeleton render is a one-way code→contract finding) — don't invent an off-contract control. - Render to the behavioral
statesthe spec locked; extend only what it didn't. For any outcome an E2E spec asserts, the E2E author has already locked the matchingstatesentry indocs/ui-contract/<screen>.yaml(the queryable proof) — render to it exactly; do NOT redefine it (a divergence is a one-way code→contract fix, asserted by the conformance test and gated at pass-E2E, so the behavioral outcome can't drift from the spec). For a frontend-only behavior NO E2E covers (a state only an RTL test proves — e.g. a transient client-side toggle), YOU add the matchingstatesentry in the same commit set, naming the queryable proof: thestatus/alertrole+name, thefield_errorsmessage tied to its field, theredirects_toroute. Either way the reviewer clears a state by its being declared + rendered + asserted (conformance test), never by an undocumented render — build the UI to match what the contract declares. - Honor declared behavioral promises —
disabled_whenpredicates,combobox/radiogroupoptions,requiredfields — exactly as published. data-testidonly where the contract lists it undertest_ids(with its reason); everywhere else native role + accessible name is mandatory — the same a11y floor below, now contract-anchored.
TanStack Query
- Route-param queries guard with
enabled: !!param— without it the query firesGET /api/v1/groups/(or/groups/undefined) on first render. - Pair the guard with an
isLoading: trueinitial-state test so the gate is locked by a regression test. - Every mutation that changes server state visible through another query calls
queryClient.invalidateQueries({ queryKey: <KEY> })inonSuccess. - Common miss:
useLogoutresolves beforeuseMerefetches, leaving stalecurrentUservisible.
Mutation return shape
- Return
mutation.mutatedirectly. Do NOT re-wrap in an arrow function — a fresh ref every render breaks consumeruseEffect/useCallbackdeps. - If transforming args before
mutate, wrap withuseCallbackand a stable dep list. Never a bare arrow per render.
Idempotency-key rotation
- Forms that submit with
Idempotency-Key: rotate the key on 4xx so the user can correct and resubmit. - 2xx = key is spent (next submit gets a fresh
useRefvalue on remount). - 5xx = never rotate (retry was designed for it).
- 4xx = rotate before the next attempt.
Sticky UI state
- "Submitted" / "success" UI that holds forever after first submit must be decided up front:
- Intentional (e.g.
/forgotshows the generic confirmation by design): mark the state asuseState<"idle" | "submitted">with no transition back; comment why; pin with a test. - Unintentional: expose a reset path ("Send another" button,
onSuccess-driven prop, key change → unmount).
- Intentional (e.g.
API access
- ALL backend calls route through
src/lib/api/<resource>.ts. Neverfetch/axiosinside a component or hook. src/lib/apiowns: base URL, auth headers, error normalization, retry/timeout policy, response parsing, cancellation.- Tests mock
src/lib/apifunctions, notfetch.
State management
- Server data → TanStack Query.
- Client state crossing 2–3 levels →
useReducer+ Context. Keep state and dispatch in separate Contexts so dispatch-only consumers don't re-render on state changes. - Discriminated
Actionunions for exhaustive switch checking. - Reach for Zustand / Redux Toolkit only when reducer+context starts duplicating ceremony across many slices.
Performance
- Measure first. Don't sprinkle
useMemo/memopreemptively. useMemofor expensive derivations.useCallbackfor callbacks passed to memoized children.memofor components that re-render often with the same props.- A
memois useless if you pass a fresh object / array / function every render — wrap those too. - Code-split at route boundaries via
lazy(() => import(...))paired with a meaningful<Suspense fallback>. - Virtualize long lists (TanStack Virtual /
react-window) once items exceed ~100.
Forms
- React Hook Form + Zod. Schema is the single source of truth for runtime validation AND types (
z.infer<typeof Schema>). aria-invalid={!!errors.x}androle="alert"on each error message.- Disable the submit while in flight to prevent double submits.
Error boundaries
- One per route + extra boundaries around risky islands (dashboard widgets, third-party embeds).
- Class component (the only place React supports them). In Next App Router use
error.tsxfiles.
Animation
- Framer Motion for non-trivial animation. CSS transitions for simple hover/focus.
AnimatePresenceis mandatory for exit animations.- Respect
prefers-reduced-motionviauseReducedMotion(). - Durations: 120–250ms for UI; longer only for hero / onboarding.
Accessibility
- Native semantic elements (
<button>,<a>,<input>,<select>,<dialog>,<nav>,<h1>–<h6>) overroleon a generic tag. - Biome's
lint/a11y/useSemanticElementsblocks<div role="dialog">,<form role="dialog">,<div role="button">, etc. - Visible focus styles required (
:focus-visible); neveroutline: nonewithout a replacement. - Trap focus inside modals; restore to the opener on close.
- Empty-state copy, loading skeletons, "no results" panels render inside the same semantic landmark (
<main>) the loaded state would — so Playwright'sgetByRole('main').getByRole(...)queries work. - The surface's
docs/ui-contract/<screen>.yamlis your published affordance source (see "UI interaction contract" above). The slice's already-authored E2E specs are the secondary cross-check: read them read-only and confirm your rendered interface matches the names/roles/click-path they query (the affordance reference when no UI contract exists). Interface-matching only — the Gherkin AC stays the source of what the UI must do; a spec asserting behavior the AC never specified is a divergence to surface at the pass-E2E gate, not something to bake into the UI.
Responsive
- Mobile-first. Author the small-screen layout; layer breakpoints upward (
sm:,md:,lg:,xl:). - Fluid units (
rem,clamp(),%,fr) over fixed pixels for layout. - Test at 320 / 768 / 1024 / 1440 minimum.
- Images:
srcset/sizes(or Next<Image>).
i18n
- No hardcoded user-facing strings; route through
next-intl(Next) /react-i18next(Vite). - ICU MessageFormat for plurals / gender / interpolation; never concatenate translated fragments.
- Stable namespaced keys (
auth.signup.submitButton). - Dates / numbers / currency via
Intl.DateTimeFormat/Intl.NumberFormatwith the active locale. <html lang>set;dir="rtl"when needed.
Styling — Tailwind + design tokens
- Source of truth is
docs/design-system/when present (tokens.md,components.md,accessibility.md,overview.md,sample/*.html). - Tailwind class names map 1:1 to design tokens (
color/brand/500→bg-brand-500). - No hard-coded color values; no hard-coded pixel sizes (
[420px],[#3b82f6]); no ad-hoc inlinestyle={{ color, padding }}for visual properties. - Need a value the scale doesn't have? Extend the token in
tailwind.config(mirrored intokens.md) — don't reach for arbitrary[...]classes. - Dynamic values that genuinely can't be tokens (e.g. chart bar's computed height) go through
style={{ height: \${pct}%` }}` — the exception, not a pattern.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

