From reins
Invoke before writing any frontend React/TypeScript code. Enforces component-driven TDD with project-specific constraints.
How this skill is triggered — by the user, by Claude, or both
Slash command
/reins:frontend-tddThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Invoke before writing any frontend React/TypeScript code. Enforces component-driven TDD with project-specific constraints.
Invoke before writing any frontend React/TypeScript code. Enforces component-driven TDD with project-specific constraints.
cd frontend && npx vitest run tests/<component>.test.tsx --reporter verbose
"<verb>s <what> when <condition>" (e.g., "renders welcome screen when no messages")Write the smallest component code that makes the test green.
cd frontend && npx vitest run tests/<component>.test.tsx --reporter verbose
Apply the constraints below:
cd frontend && npx tsc --noEmit && npx next build
GodComponent (DON'T) → Container + Presenter (DO)
├─ auth state AuthProvider (context)
├─ landing layout LandingPage (component)
├─ modal state AuthModal (component)
├─ form logic LoginForm (component)
└─ animations useScrollReveal (hook)
globals.css: bg-[var(--color-bg)], not bg-whitebg-[var(--color-muted)] for disabled/skeleton states, not bg-gray-200style={} only for truly dynamic values (animation delays, computed positions)cn() from lib/utils for conditional classesuseChat, useSession, usePointSelection../../lib/types@/lib/utils only for shadcn utilities (cn){ useState }, not {useState}ChatPanel)use prefix (useMediaQuery)handle prefix in component, on prefix in propsis/has/should prefix (isMobile, hasMessages)getByRole — accessible roles (button, heading, textbox)getByLabelText — form elementsgetByText — visible text contentgetByTestId — LAST resort onlyclassName.includes("bg-primary") is fragilefireEvent.click() or userEvent.click() to test callbacksexpect(onSend).toHaveBeenCalledWith("query")server.use(...))toBeInTheDocument() not .not.toBeNull()toHaveTextContent("expected") not textContent.includes("expected")toHaveBeenCalledWith(args) not toHaveBeenCalled()describe blocks to group by feature, not by methodit.each() for parameterized tests, not copy-pastemakePoint(), makeMessage(), makeResponse()| Anti-Pattern | Example | Fix |
|---|---|---|
| God component | AuthGate at 465 lines | Split into AuthProvider + LandingPage + AuthModal |
| Duplicated JSX | Timeline rendered twice (desktop + mobile) | Extract TimelineView component |
| Prop drilling | onSuggest through 3 levels | Create SuggestContext |
| Excessive mocking | 12 mocks in one test | Refactor component to be testable with fewer deps |
| CSS class assertion | className.includes("bg-primary") | Test visible state: toHaveAttribute("aria-pressed") |
| Missing interactions | Only render tests, no click/type tests | Add fireEvent/userEvent tests |
| Hardcoded i18n | TOOL_LABELS in English only | Use useDict() hook |
| Magic Tailwind colors | bg-blue-500 in CHIP_COLORS | Use bg-[var(--color-primary)] |
| Conditional test logic | if (img) { fireEvent... } | Assert img exists, then interact |
| Nested ternaries in JSX | locale === "ja" ? ... : locale === "zh" ? ... | Extract to helper function |
npx claudepluginhub lifeodyssey/reins --plugin reinsProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.