Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,13 @@ Domain-expert lenses `hyper` auto-engages when their triggers match the request
</details>

<details>
<summary><strong>🎯 Domain (6)</strong> - specialized skills for specific contexts</summary>
<summary><strong>🎯 Domain (7)</strong> - specialized skills for specific contexts</summary>

| Skill | Role |
|---|---|
| `designer` | Intention gate - produces DESIGN.md contract before any visual code |
| `shadcn-expert` | shadcn/ui Base UI architect - ONLY when user picks shadcn in designer Q11b |
| `react-pro-coder` | 56 React/Next.js rules with stable IDs - rendering, state placement, audits |
| `behaviour-analysis` | UI/UX state audits, Nielsen heuristics, interaction matrices |
| `security-review` | OWASP audits, vulnerability checklists |
| `design-patterns-skill` | Clean Code + Pragmatic Programmer patterns |
Expand Down
2 changes: 1 addition & 1 deletion scripts/audit/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const SOURCES: PluginSource[] = [
packages: [{ name: "motion", registry: "npm", targetMajor: 12 }] },
{ plugin: "lenis", editorial: false, skip: false, skills: [],
packages: [{ name: "lenis", registry: "npm", targetMajor: 1 }] },
{ plugin: "react", editorial: false, skip: false, skills: [],
{ plugin: "react", editorial: false, skip: false, skills: ["react-pro-coder"],
packages: [
{ name: "react", registry: "npm", targetMajor: 19 },
{ name: "react-dom", registry: "npm", targetMajor: 19 },
Expand Down
1 change: 1 addition & 0 deletions skills/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Categories:
| `design-patterns-skill` | Apply core programming principles and design patterns from Clean Code, The Pragmatic Programmer, Code Complete, Refactor |
| `designer` | |
| `marketing` | Use to do product marketing for any brand - position it, find the message, write the copy ("marketing words"), set brand |
| `react-pro-coder` | Staff-level React and Next.js engineering discipline - 56 enforced rules across component design, hooks, state placement |
| `readme-writer` | Writes or rewrites project README files using repository evidence instead of generic filler. Use when creating a new REA |
| `reflect` | Review a product screen OR a feature (shipped or planned) AS a real target-customer persona - short, blunt, moody, marke |
| `security-review` | Security code review for vulnerabilities. Use when asked to "security review", "find vulnerabilities", "check for securi |
Expand Down
161 changes: 161 additions & 0 deletions skills/react-pro-coder/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
name: react-pro-coder
category: domain
description: Staff-level React and Next.js engineering discipline - 56 enforced rules across component design, hooks, state placement, performance, data fetching (RSC, React 19 Actions, use()), TypeScript, styling and accessibility, architecture, testing, and production security, with an environment gate, an architecture-first order, tests as output, and a negative-doubt verification pass. Use when writing, reviewing, refactoring, debugging, or auditing React or Next.js code, when deciding where state lives or whether a component renders on the server, or when asked for React best practices or a React audit.
references:
- references/COMPONENT-DESIGN.md
- references/HOOKS.md
- references/STATE.md
- references/PERFORMANCE.md
- references/DATA-FETCHING.md
- references/TYPESCRIPT.md
- references/STYLING-A11Y.md
- references/ARCHITECTURE.md
- references/TESTING.md
- references/SECURITY-PRODUCTION.md
- references/REVIEW-CHECKLIST.md
- references/OUTPUT-CONTRACT.md
- references/TEMPLATES.md
- references/VARIANT-MAPPING.md
---

# React Pro Coder (SDE-3, React 19 + Next.js App Router)

## The Iron Law

```
NO REACT CODE WITHOUT react_get_constraints FIRST
```

The `react_*` MCP tools are the ground truth for this domain. Call `react_get_constraints` before
proposing any React or Next.js change, `react_list_patterns` to find the pattern, and
`react_get_pattern` for the code plus its anti-pattern. Recalling a constraint from memory is a
violation, not a shortcut: the constraints move with the framework and your memory does not.

This skill is the rule set the tools do not carry. Rules live in `references/`, one file per domain,
each rule with a stable ID (`CD-3`, `ST-2`, `DF-5`). Cite the ID whenever a rule is applied or waived
so the reasoning survives review.

## Rationalization table

| Excuse | Reality |
|---|---|
| "It is a one-line change" | One line moved state to the wrong level or added an index key. Read the domain file. |
| "I know the React rules" | The 56 rules are here because knowing them is not the same as applying them under time pressure. |
| "The MCP call is slow" | Slower than a wrong rendering strategy discovered in review? No. |
| "There is no time for tests" | Then there is no time for the bug. TQ rules are not optional (see `test-first`). |
| "Memoize it to be safe" | H-3: memoization without a measurement is cost with no benefit. |
| "I will add the error state later" | SP-3: later is when a user finds it. Four states or it does not ship. |

## Step 0: Environment gate (always first)

```bash
node -v # >= 18.x
npm ls react # React 18+, React 19 for use() and Actions
npm ls next # Next.js 14+ for App Router, 15+ makes params a Promise
npx tsc --noEmit # clean before and after the change
```

Defaults when the project has not decided:

| Concern | Default |
|---|---|
| Framework | Next.js App Router |
| Styling | Tailwind CSS + shadcn/ui (only when the user picked shadcn - see `shadcn-expert`) |
| Icons | lucide-react |
| Shared client state | Zustand (Redux prohibited) |
| Server state | RSC fetch, or TanStack Query / SWR on the client |
| Forms | React Hook Form + Zod |
| Tests | Vitest + Testing Library, Playwright for flows |

## Step 1: Task classification (exactly one)

New Feature, Refactor, Bug Fix, Performance/SEO, Review/Audit, Documentation Only.

| Class | Trigger words |
|---|---|
| New Feature | create, build, add, implement, scaffold, component, page, route |
| Refactor | refactor, restructure, reorganize, migrate, simplify, clean up |
| Bug Fix | fix, bug, broken, failing, regression, crash |
| Performance/SEO | optimize, slow, bundle, LCP, CLS, INP, metadata, SEO |
| Review/Audit | review, audit, critique, accessibility, architecture check |
| Documentation Only | document, explain, comment, write up |

Unclear class means stop and ask, not guess.

## Step 2: Architecture-first order (never skip a layer)

Responsibilities, invariants, dependency direction, module boundaries, public APIs, folder structure,
files, functions, syntax. Syntax is decided last.

## Step 3: The rule set

| Domain | Rules | Reference |
|---|---|---|
| Component design | CD-1..CD-8 | `references/COMPONENT-DESIGN.md` |
| Hooks | H-1..H-7 | `references/HOOKS.md` |
| State management | ST-1..ST-5 | `references/STATE.md` |
| Performance | PF-1..PF-6 | `references/PERFORMANCE.md` |
| Data fetching and React 19 | DF-1..DF-5 | `references/DATA-FETCHING.md` |
| TypeScript | TS-1..TS-5 | `references/TYPESCRIPT.md` |
| Styling and accessibility | UI-1..UI-4 | `references/STYLING-A11Y.md` |
| Architecture | AR-1..AR-6 | `references/ARCHITECTURE.md` |
| Testing and quality | TQ-1..TQ-5 | `references/TESTING.md` |
| Security and production | SP-1..SP-5 | `references/SECURITY-PRODUCTION.md` |

Read the file for the domain being touched. Do not paraphrase a rule from memory when the file is one
read away.

## Step 4: Forbidden patterns (hard stops)

| Pattern | Instead |
|---|---|
| `useEffect` for data fetching | RSC fetch, TanStack Query, SWR |
| `useEffect(fn, [])` as componentDidMount | RSC, or a query with `enabled` |
| Redux | Zustand (Jotai acceptable for atom-shaped state) |
| `any` | `unknown` plus narrowing, or the real type |
| Prop drilling past 2 levels | Composition, slots, or injected context |
| Context for frequently changing state | Zustand selector subscriptions |
| App-wide barrel files | Direct imports (AR-2 holds the one exception) |
| Index as a key on reorderable lists | Stable entity id |
| Side effects during render | Effects, event handlers, or server code |
| `div` with `onClick` | `button`, `a`, or a real semantic element |
| Synchronous `params` in Next.js 15 | `await props.params`, or `use(props.params)` in a client component |

## Step 5: Pattern gate

Use a design pattern only when all three hold: the force it resolves is stated, the invariant it
protects is stated, and the simpler alternative was considered and rejected in writing. No force means
no pattern.

## Step 6: Tests are part of the output

Behavior that exists has a test. Refactors need characterization tests written before the structure
moves. Test through the accessible surface, never internal state. Details in `references/TESTING.md`.
This composes with `test-first`, which owns the ordering, and `ship-gate`, which owns the evidence.

## Step 7: Output contract and negative doubt

Follow `references/OUTPUT-CONTRACT.md`: classification, environment verification, assumptions,
architecture decision, SEO needs, public APIs, code by file path, tests, negative doubt log, risks. The
negative-doubt routine runs before finalizing, and its hard stop applies: if correctness is still
uncertain after the second pass, return the revised design and the missing inputs instead of code.

## Review and audit mode

`references/REVIEW-CHECKLIST.md` is the pass list, keyed to rule IDs, with the severity scale. Report
findings as `path:line - RULE-ID - problem - fix`. Report shape and scaffolds live in
`references/TEMPLATES.md`. For variant-mapping detection, use `references/VARIANT-MAPPING.md`.

## Boundaries with other skills

| Concern | Owner |
|---|---|
| Visual system: type scale, color, spacing, elevation, motion | `designer` (DESIGN.md contract), `ui-ux`, `design-tokens` |
| shadcn/ui component internals and Base UI specifics | `shadcn-expert`, and only when the user picked shadcn |
| Whether the feature should exist at all | `pm-gate` |
| Completion claims and verification evidence | `ship-gate` |

This skill owns the React and Next.js engineering decision: rendering strategy, state placement,
component boundaries, data flow, types, tests. When a DESIGN.md exists, it is the contract and this
skill implements against it rather than relitigating the visuals.
54 changes: 54 additions & 0 deletions skills/react-pro-coder/references/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Architecture (AR-1 .. AR-6)

## AR-1: Feature-based structure, not type-based

```
features/auth/components/
features/auth/hooks/
features/auth/api/
features/auth/index.ts
```

not a top-level `components/`, `hooks/`, `utils/` that every feature dumps into. Type-based folders
scatter one feature across the tree, so a change touches five directories and deletion is never safe.
Genuinely shared primitives (design system components, the `cn` helper) still live in a shared
location; the test is whether a second feature actually uses it today.

## AR-2: Public API per feature, and the barrel exception

A feature exposes what others may use through its own `index.ts` and nothing else. Reaching into
`features/auth/hooks/useSession` from another feature is a boundary violation.

This is the one place a barrel file is allowed, and it comes with conditions: the barrel covers a
single feature, re-exports named symbols only, and the package sets `sideEffects: false` so the bundler
can drop what is unused. App-wide barrels (`components/index.ts` re-exporting everything) remain
forbidden: they pull the whole tree into every importer and defeat tree-shaking. Inside a feature,
import directly.

## AR-3: No circular dependencies

If A imports B and B imports A, the shared piece belongs in C. Cycles break tree-shaking, produce
undefined-at-import bugs that only appear in one bundler, and mean the boundary was drawn wrong. Add a
cycle check to lint (`import/no-cycle`) rather than finding them by hand.

## AR-4: Separate UI from business logic

Components render. Hooks and plain functions decide. A rule that lives in a component can only be
tested by rendering it; the same rule in a function is tested directly and reused on the server.

## AR-5: Error boundaries at feature level

Wrap each major feature, not only the app root. A root-only boundary turns one broken widget into a
blank page. Pair boundaries with the Suspense boundaries from DF-2 so loading and failure are handled
at the same granularity. Report the error from the boundary (SP-5); do not swallow it into a generic
message with no telemetry.

## AR-6: Validate environment config at startup

```tsx
const env = z.object({ API_URL: z.string().url() }).parse(process.env);
```

Fail fast at boot with a message naming the missing variable, rather than failing at 3am inside a
request with `undefined is not a URL`. Keep the server schema and the public schema separate so a
server-only secret cannot leak into the client bundle (SP-2).
87 changes: 87 additions & 0 deletions skills/react-pro-coder/references/COMPONENT-DESIGN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Component Design (CD-1 .. CD-8)

## CD-1: Single responsibility

One component, one job. If describing it needs the word "and", split it.

## CD-2: Keep components under 100 lines

Past 100 lines, extract. Small components are easier to test, memoize, and delete. The line count is a
smell detector, not a lint rule: a 120-line component with one coherent job is fine, an 80-line one
doing three jobs is not.

```tsx
// BAD: one component fetches, filters, renders a table, renders charts, owns modals
function Dashboard() { /* 300 lines */ }

// GOOD
function Dashboard() {
return (
<>
<DashboardFilters />
<DashboardTable />
<DashboardCharts />
</>
);
}
```

## CD-3: Composition over prop spaghetti

Fifteen props is a design failure. Expose slots.

```tsx
// BAD
<Card title="..." subtitle="..." footer="..." action="..." icon="..." />

// GOOD
<Card>
<Card.Header><Card.Title>...</Card.Title></Card.Header>
<Card.Body>...</Card.Body>
<Card.Footer><Button /></Card.Footer>
</Card>
```

## CD-4: Colocate

State, styles, types, and helpers live next to their only consumer. A shared `/components` or `/hooks`
directory is a dumping ground unless the thing in it is genuinely shared. See AR-1 for the
feature-based layout this implies.

## CD-5: Minimal props API

Props are explicit, predictable, and few. One `variant` union beats five booleans, and it makes the
impossible combinations unrepresentable.

```tsx
// BAD
<Button primary large rounded disabled />

// GOOD
type ButtonProps = { variant: 'primary' | 'secondary'; size: 'sm' | 'md' | 'lg' };
<Button variant="primary" size="lg" />
```

## CD-6: No prop drilling past 2 levels

If a prop crosses two intermediate components that do not read it, those components are coupled to data
they do not use. Fix with composition (pass the rendered child down), injected context for stable
values, or a Zustand selector for shared mutable state.

## CD-7: Pure render

Same props, same output. No side effects, no mutation, no `Math.random()` or `Date.now()` in the render
body. Impure render breaks Strict Mode double-invocation, memoization, and server rendering hydration.
Generate ids with `useId`, generate randomness in an effect or on the server.

## CD-8: Guard at the boundary

Handle invalid, empty, and error states at the top of the component and return early. The happy path
below stays flat.

```tsx
if (error) return <ErrorState error={error} />;
if (!user) return <EmptyState />;
```

Every fetch-backed component owes four states: loading, empty, error, success. Enforced by SP-3.
Loading
Loading