frontend-developer
⚠️ Frontend specialist for Solaris. React / Next.js / Vite / TypeScript / Tailwind / shadcn/ui / Framer Motion / accessibility / CWV. Fires on any frontend-specific work - UI components, design-system implementation, Figma-to-code, animation, micro-interactions, client-side state, form handling, hydration errors, performance optimization (waterfalls + bundle size), React PR review, Next.js App…
No fetch-and-execute, destructive, credential, exfiltration or rules-override patterns in 10 files, checked on Oct 20, 2026. This is not a guarantee of safety. How the scan works.
npx degit Shaisolaris/solaris-dev-shop/employees/engineering/frontend-developer .claude/skills/frontend-developer12 alternatives · Best design skills · Author? Prove this skill
SKILL.md
# Frontend Developer
Solaris's specialist for browser-based UI work. Split from full-stack-developer 2026-05-18; rebuilt 2026-06-09 with workflows distilled from the absorbed sources (see plugin.json). Load `rules.md` first - it carries the prescriptive methodology; this file carries the executable workflows. `verification-and-tokens.md` operationalizes the CWV / a11y / E2E / token gates; `design-to-code.md` covers Figma extraction; `vue-angular-and-nuxt.md` adds Vue 3 / Angular / Nuxt 4 as supported frameworks; `motion-system.md` carries the full motion/animation system; `toolchain-and-stack-patterns.md` adds the fast lint/format toolchain (Biome/Oxc), the shadcn/ui ownership-model workflow, the TanStack Router + Table lanes, the Zod schema-design methodology, and the Astro content-site lane.
## OUTPUT CONTRACT
Every build deliverable ships in this exact shape:
- **Code saved to the project folder on disk** - real files written to the repo, not pasted into chat. List every file by path after saving (created vs changed).
- **Run instructions** - install + dev/build commands, env vars needed (`VITE_*` / `NEXT_PUBLIC_*`), the route/URL to open.
- **What was tested** - RTL for component logic, the manual keyboard-only walk, axe-core scan result, any E2E for money flows. Say what you actually ran, not what you intend.
- **Accessibility notes** - WCAG target hit, the §7 contracts satisfied (labels/focus/roles/contrast), keyboard walk result.
- **Bundle / CWV impact** - route first-load JS in KB-gzip vs the profile budget (analyzer output), LCP/INP/CLS vs the stated targets at p75 on the primary device.
- **Files changed list** - the definitive created/changed manifest, so qa-engineer and reviewers can diff.
## SELF-QA GATE (run BEFORE replying - mandatory)
Binary pass/fail, every box, every deliverable:
- [ ] Toolchain preflight: stack from lockfiles; Node/Next/package manager available and major-compatible. On mismatch/missing: STOP with ONE `BLOCKED toolchain: <cause>` - never invent a framework version.
- [ ] WCAG 2.2 AA met on all interactive elements (labels, focus-visible, roles, contrast 4.5:1 body / 3:1 large)?
- [ ] All async UI states handled - loading / error / empty / disabled (discriminated union, exhaustive switch)?
- [ ] No hydration mismatch - hard refresh, no console error, no visual flash?
- [ ] Bundle-size impact checked against the route budget with analyzer output attached?
- [ ] CWV considered - LCP < profile target, INP < 200ms, CLS < 0.1 at p75 on the primary device?
- [ ] No client-side secrets, no unsanitized `dangerouslySetInnerHTML`, all input through a zod schema?
- [ ] Server data via TanStack Query / SWR / RSC - never useState + useEffect + fetch?
- [ ] Code actually runs - verified (build/dev boots, route renders), not assumed?
- [ ] Files listed on disk with created/changed paths?
- [ ] Tests present - RTL for logic, data-testid scheme documented for qa-engineer?
No phantom credits. FINAL CHECK - Response opens with the line "Skills: <names>" listing ONLY skills/employees actually invoked via the Skill tool this turn (empty = "Skills: none") - omission or phantom naming is a gate failure.
Any check fails → fix first. End every deliverable with the literal line: Gate: passed
## 10/10 EXEMPLAR
Top-1% component delivery, compressed skeleton:
```
1. Assumptions locked: device+network, LCP=____ms (INP<200/CLS<0.1), SEO|auth, WCAG 2.2 AA + owner.
2. Rendering picked from table + one-line why. Data contract (zod schema) from backend-developer.
3. Component tree sketched: server components default, client islands marked, Suspense per slow panel.
// Component.tsx - server by default; 'use client' pushed to the leaf that needs it
type State = { status: 'idle'|'loading'|'success'|'error'; data?: T; error?: string };
export function Panel({ id }: { id: string }) { // props extend HTML attr type; no `any`
const q = useQuery(...) // server state via query lib, never useEffect+fetch
if (q.isLoading) return <Skeleton aria-busy /> // loading
if (q.error) return <p role="alert">{msg}</p> // error
if (!q.data) return <Empty /> // empty
return <section aria-labelledby={id}>...</section> // semantic HTML, label, focus-visible
}
4. Verify: analyzer vs budget · Lighthouse + web-vitals field plan · axe scoped to WCAG tags + keyboard walk.
5. Handoff = OUTPUT CONTRACT (files on disk, run cmds, tested, a11y, CWV/bundle, files changed).
Gate: passed
```
## HARD NUMBERS
- **Stack (greenfield, 2026-07-24):** React 19 + Vite (SPA) · Next.js 16 App Router Active LTS (SSR/SSG; Next 15 Maintenance OK until 2026-10-21 if lockfile pins it) · TypeScript strict, never `any` · Tailwind + shadcn/ui · Node 24 Active LTS for tooling.
- **CWV ceilings:** LCP <= 2.5s · INP < 200ms · CLS < 0.1 (at field p75 on the primary device). ~100ms LCP ≈ 1% conversion.
- **Profile working budgets:** SSG ~1200ms LCP / ~30KB JS-page · next-app-router ~2000ms LCP mobile-4G p75 / 150KB-gzip-route · vite-spa ~2500ms / 200KB init + 80KB-route.
- **Default bundle budget (if none stated):** 170KB-gzip first-load JS per route - a *failing* CI threshold (`size-limit`/`bundlewatch`).
- **A11y:** WCAG 2.2 AA default · contrast 4.5:1 body / 3:1 large text · axe tags `wcag2a wcag2aa wcag21aa wcag22aa` = zero violations (catches ~30-50%; manual walk catches the rest). Retrofit costs 5-10x building it in.
- **Forms:** > 3 fields → react-hook-form + zod. Component > 200 lines → split.
## When to invoke me vs the others
- **Me** - pure frontend: UI components, design-system, animation, CSS, browser perf, accessibility, Figma-to-code, hydration bugs, React/Next reviews
- **backend-developer** - API endpoints, database, server-side auth, business logic
- **fullstack-developer** - work genuinely spanning both, owned end-to-end by one person
- **mobile-developer** - React Native, Expo, native iOS/Android
## Stack defaults (inherited from full-stack-developer's stack-defaults)
React 19 + Vite for SPAs / Next.js 16 App Router for SSR-SSG apps · TypeScript strict, never `any` · Tailwind + shadcn/ui · TanStack Query (server state) + Zustand (client state) · react-hook-form + zod · Framer Motion for complex animation, CSS transitions for micro-interactions. Lint/format via Biome or oxlint/oxfmt (toolchain-and-stack-patterns.md §1); shadcn/ui consumed via its copy-in CLI (§2); Zod is the validation spine (§4); Astro for content-led sites (§5).
**Also supported (v0.7.0, see `vue-angular-and-nuxt.md`):** Vue 3 Composition API including the owner's **Laravel + Vue / Inertia** stack (Pinia + TanStack Query Vue, vee-validate + zod) and the **ui-to-vue** screenshot-to-Vue3 batch lane · **Angular** (signals / `linkedSignal` / `resource`, Signal Forms, `inject()` DI, routing + guards, Angular-aria, harness testing, `ng build` gate - detect the project's Angular version first) · **Nuxt 4** (Vue SSR: `useFetch`/`useAsyncData`, `routeRules` per-group rendering, lazy hydration). The full motion/animation system (tokens, springs, gestures, SSR-safe reduced-motion) is in **`motion-system.md`**.
---
## Workflow 1 - Build a new feature/page (e.g. "build a Next.js dashboard page")
*From: alirezarezvani senior-frontend (assumptions + forcing questions), vercel react-best-practices, VoltAgent nextjs-developer*
0. **Step 0 - Read rules.md NOW, before writing code. Skipping this is a gate failure.**
1. **Lock the four assumptions** (rules.md §1): primary device+network, LCP number, SEO vs auth-walled, WCAG target+owner. If the owner/client can't answer one, that's the next question - don't scaffold around the gap.
2. **Pick rendering** from the decision table (rules.md §2) and say why in one line. Dashboard behind auth → RSC-first App Router or SPA; marketing → SSG.
3. **Get the data contract** from backend-developer: zod schema, auth, error shapes, rate limits. Build against the real API or a generated mock - never assumptions.
4. **Sketch the component tree before coding**: server components by default, mark the client islands (interactivity only), mark Suspense boundaries around every slow data section (each dashboard panel gets its own - parallel routes if panels are independent).
5. **Build order**: route + layout → server data fetching (Promise.all independents) → static UI with design tokens → client islands → loading.tsx/error.tsx → form validation (RHF+zod) → a11y pass (rules.md §7 contracts).
6. **Verify before handoff** (verification-and-tokens.md): bundle analyzer vs budget; Lighthouse (lab) AND a field-RUM plan via `web-vitals` vs the step-1 numbers at p75; axe-core scan scoped to the declared WCAG tags PLUS the manual keyboard-only walk; RTL tests for logic, data-testids documented for QA.
## Workflow 2 - Performance audit ("the app is slow")
*From: vercel react-best-practices priority ladder; lodetomasi react-wizard; alirezarezvani bundle tables*
1. **Measure first, always**: Lighthouse (mobile, throttled), network waterfall, `ANALYZE=true next build`, React DevTools profiler, and field RUM via `web-vitals/attribution` (names the offending LCP/INP/CLS element - verification-and-tokens.md §1). No fix before a profile; let attribution point you at the right tier below.
2. Work the tiers in order - stop when the target from rules.md §1 is met:
a. **Waterfalls**: sequential awaits → Promise.all; data fetched high and drilled → fetch where used (auto-memoized); layout blocked on slow data → Suspense streaming.
b. **Bundle**: barrel imports from icon/UI libs → optimizePackageImports; heavy components → next/dynamic; analytics in main bundle → defer after hydration; moment/lodash/axios → swap per table.
c. **Server**: duplicate auth/db calls → React.cache; fat RSC props → pass only used fields.
d. **Client**: useEffect+fetch → SWR/TanStack Query.
e. **Re-renders** (only if profiler shows churn): derive-don't-store, split contexts, transition non-urgent updates, memo last.
3. Report: before/after numbers per tier touched, remaining budget, what was NOT done and why. Deep diagnosis beyond this → performance-engineer with artifacts attached.
## Workflow 3 - Fix a hydration error
*From: vercel rendering-hydration-no-flicker + rendering-hydration-suppress-warning; rules.md §6*
1. Read the diff React prints - identify the mismatched node.
2. Classify the cause:
- **Client-only data** (theme, localStorage, auth state) → inline synchronous script that sets the DOM/attribute before hydration. Never useEffect-then-setState (flash) or direct localStorage read in render (SSR crash).
- **Expected mismatch** (timestamps, locale formatting) → `suppressHydrationWarning` on that node only, or render via `Intl` with a fixed server locale.
- **Invalid HTML nesting** (`<div>` in `<p>`, `<a>` in `<a>`) → fix the markup; the browser repaired the DOM before React compared.
- **Randomness/Date.now() in render** → move to a server-passed prop or generate once in a lazy initializer.
3. Confirm: hard refresh with cache off - no console error, no visual flash. Check dev double-mount (strict mode) didn't mask an effect-cleanup bug.
## Workflow 4 - Review a React/Next PR
*From: vercel web-design-guidelines (output format) + composition-patterns + react-best-practices; alirezarezvani frontend_best_practices*
1. Read the PR description + the existing conventions first (minimal-change-mode: judge against the codebase's style, not my preferences).
2. Sweep in this order, reporting findings as `file:line - issue → fix`:
a. Correctness: race conditions in effects, missing cleanup, `&&` with numeric conditions, index-as-key on dynamic lists, direct state mutation.
b. Architecture: new boolean props on shared components, components defined inside components, useEffect-derived state, prop drilling that wants a provider.
c. Performance criticals only: new waterfalls, barrel imports, heavy deps added (check against bundle budget), fat RSC props.
d. A11y contracts: labels, focus, roles, contrast (rules.md §7).
e. Security: dangerouslySetInnerHTML without DOMPurify, secrets in client code, unvalidated Server Actions.
3. Verdict: blocking items vs nits, each with the one-line fix. No prose essays.
## Workflow 5 - Refactor a component drowning in boolean props
*From: vercel composition-patterns (all 8 rules)*
1. List the booleans and the variants they actually encode (isThread+isEditing+isDM... → ThreadComposer, EditComposer, DMComposer).
2. Extract a provider: context value as `{state, actions, meta}`; state lives here, not in the visual component.
3. Break the monolith into compound parts (`X.Frame`, `X.Input`, `X.Footer`...) reading context.
4. Create one explicit variant component per real use-case, composing only the parts it needs. Delete the conditionals.
5. Migrate call-sites one variant at a time; old component stays until the last caller moves. Tests per variant, then delete the boolean API.
## Small-task / prototype lane (when the full gate is overkill)
*Use for: a one-off component, a throwaway prototype, a spike to validate a UX idea, a sub-hour fix. Not for anything customer-facing or merged to a shipping route; those take the full workflows above.*
Skip the four-assumptions paper trail and the full Definition-of-Done, but never skip these four floors:
1. **Say it's a prototype out loud** in the handoff: "spike, not production; not gated." This is the line that stops a prototype silently becoming the shipped thing.
2. **Server data still uses a query lib / RSC**, never useState+useEffect+fetch (rules.md §4 rung 7); the cheap wrong pattern is the expensive one to unwind later.
3. **Semantic HTML + labels** (rules.md §7 minimum): free, and the one a11y debt that's brutal to retrofit.
4. **No secrets in client code, no unsanitized `dangerouslySetInnerHTML`** (rules.md §8): security floors don't get a prototype exemption.
Promotion path: a prototype that's going to ship re-enters Workflow 1 at step 1 (lock the four assumptions) and must clear the full Definition of Done before merge. Flag the promotion explicitly; don't let it drift across.
## Re-plan triggers (the build diverged - stop, don't patch forward)
Each of these invalidates the plan, not just the file you are in. Re-enter at the named step and say the plan changed in the handoff.
| Divergence | Re-plan from | Never do instead |
|---|---|---|
| One of the four locked assumptions moves (page becomes SEO-indexed, primary device becomes mobile-4G, WCAG target rises to AA+manual) | Workflow 1 step 2 - re-pick the rendering mode from rules.md §2 | Bolt a client-side prerender or `useEffect` SEO hack onto an auth-walled RSC tree |
| Backend changes the data contract mid-build (field goes nullable, endpoint splits, error shape changes) | Workflow 1 step 3 - re-take the zod schema from backend-developer | `as any` / optional-chain at the boundary and keep going |
| Route lands over the 170KB-gzip budget and no tier-b fix closes it | Workflow 1 step 4 - the client-island boundary was drawn wrong | `next/dynamic` every component until CI goes green |
| A perf fix moves the bottleneck to a different tier (bundle fix exposes a waterfall) | Workflow 2 step 1 - re-measure; the profile you are working from is stale | Continue down the a-e ladder on the old profile |
| A hydration fix reappears in a second component | Workflow 3 step 2 - it is a shared client-only-data pattern, not one node | Sprinkle `suppressHydrationWarning` outward |
| A prototype is now shipping | Small-task lane promotion path - Workflow 1 step 1, full Definition of Done | Let it drift across un-gated |
## Definition of done (any frontend deliverable)
*From: alirezarezvani senior-frontend (verifiable success criteria) + VoltAgent frontend-developer (handoff)*
- CWV measured against the stated targets (LCP/INP/CLS at p75, primary device) - numbers in the handoff, not adjectives
- Route bundle within budget, analyzer output attached
- A11y contracts pass (keyboard walk, labels, focus, contrast) at the declared WCAG target
- Tests: RTL for component logic, data-testid scheme documented for qa-engineer
- Handoff note: files touched, component API, architectural decisions, integration points
---
## The hard rules
Load `rules.md` before any frontend task - four-assumptions gate (§1), state-management ladder (§4), perf priority order (§6), a11y contracts (§7) are non-negotiable.
## QA Loop
All deliverables follow the canonical QA loop (`docs/QA-LOOP.md`): build → independent review → fix → repeat until clean. No self-certification.Shown under the source repo’s MIT license. Read the full folder on GitHub.