Code skills
These skills shape how Claude writes and reviews code. Each one loads automatically when the task matches its trigger, whether the task comes from your request or from work Claude or a sub-agent is already doing. You don’t call them by name.
The React app’s skills live in frontend/.claude/skills/ and load in sessions that work on the app. tdd and naming-conventions live in the root .claude/skills/ and apply anywhere in the repository.
TypeScript
Section titled “TypeScript”Patterns and conventions for TypeScript (opens in a new tab):
typerather thaninterface(why (opens in a new tab)).- Explicit return types on exported functions (why (opens in a new tab)).
- No enums: use
as constobjects with types derived from them (why (opens in a new tab)). - No
switch: use an object map orif/else. - At most three positional parameters, with an options object beyond that (why (opens in a new tab)).
undefinedrather thannullfor a missing value your own code sets.- Zod (opens in a new tab) 4’s
z.literal([...])(opens in a new tab) rather thanz.enum(), with values sorted alphabetically.
One source of truth. Before Claude defines a constant list, union type, schema, or lookup map, it searches for an existing one and imports it. Every other form of the same set (a type, a schema) is derived from that one source. A map keyed by a union uses satisfies Record<Union, ...>, so when you add a member to the union, TypeScript reports an error everywhere the new member is not handled yet (why (opens in a new tab)).
Triggers when writing or reviewing TypeScript: typing exports, choosing between type and interface, defining Zod schemas, defining a shared constant list or union, structuring function parameters, or avoiding switch statements and enums.
Example phrasing: “type this Zod schema”, “should this be a type or an interface”, “add a status to this union”.
Naming Conventions
Section titled “Naming Conventions”Descriptive names that explain themselves, in any language: TypeScript, bash, workflow YAML, Dockerfiles. It flags vague names, abbreviations, and names that just repeat the type. The always-loaded no-abbreviations rule holds everything Claude writes to the same standard.
Triggers when naming or renaming an identifier, variable, constant, function, parameter, environment variable, workflow job, or Dockerfile ARG/ENV, or reviewing names.
Example phrasing: “name this hook better”, “is cfg a good name here”, “rename these env vars”.
React Code
Section titled “React Code”Conventions for React components, pages, routes, hooks, and forms. Claude runs four checks before writing code:
- Hook check: before a
useEffect, decide whether it should be an Effect at all (an Effect is for syncing with something outside React, like a browser API or the network). NouseMemo,useCallback, ormemo(see below). For auseStatewith no initial value, useundefined, notnull. - Form element check: plain HTML form elements (
<input>,<select>,<textarea>, checkboxes, and buttons) are replaced with the shadcn/ui (opens in a new tab) controls in~/components/ui/(Input,NativeSelect,Textarea,Checkbox,RadioGroup,Button). Those controls are grouped inFieldparts and connected to the form with Conform (opens in a new tab), the form library.@conform-to/zodis always imported from its/v4subpath; importing it without the subpath fails at runtime, and lint catches it. - Translation check: every string the user sees comes from
t(). - React 19 idiom check:
refas an ordinary prop (noforwardRef),use()rather thanuseContext,<Context>rather than<Context.Provider>, guarding againstcount && <X />rendering a stray0, and React Router (opens in a new tab)’s form handling instead ofuseActionState,useFormStatus, oruseOptimistic.
Memoization is left to the compiler. React Compiler is on by default. It caches values and components for you so they are not recomputed on every render, so plain code needs no useMemo, useCallback, or memo. A manual memo is allowed in only two cases: a value is passed into code the compiler does not compile, or the compiler skips a component (a bailout) and measurement shows the memo matters. Each manual memo carries a comment naming its case.
Data loading follows one pattern. Data that belongs to the URL loads through the route’s server loader by default (or a clientLoader). Data that belongs to a component loads through TanStack Query (opens in a new tab), when your project has it enabled. A Query reference covers query keys, derived state with select, dependent and paginated queries, error handling, and optimistic updates.
Before adding a dependency or writing a helper from scratch, the skill tries options in this order: code already in the project, then built-in browser APIs (Intl, URL, crypto.randomUUID(), structuredClone(), modern CSS), then a dependency you already have, then a new dependency, and only then custom code. It also covers when to split out a component, and how routes and pages divide the work: files in app/routes/ stay thin, and the UI lives in app/pages/.
Triggers when writing or reviewing React components, hooks, or event handlers, deciding whether to extract a component, deciding whether a manual memo or "use no memo" is justified, debugging stale closures or infinite re-renders, weighing a new dependency, or choosing a React 19 idiom.
Example phrasing: “this useEffect is firing twice”, “extract this section into its own component”, “wire up a Conform form for sign-up”.
React Performance
Section titled “React Performance”You point it at one specific interaction, like a button click, and it finds the component that re-renders when it shouldn’t and explains what is causing the extra work. It diagnoses; it does not fix anything itself. You can also run it as /gaia-react-perf <target>. See React performance for the full workflow.
Triggers only when you clearly ask about rendering: “profile renders”, “measure renders”, “why is X re-rendering”, or “diagnose render performance”. It does not trigger on a vague “feels slow” or “janky”; React Doctor (opens in a new tab) covers those.
Styling
Section titled “Styling”Tailwind
Section titled “Tailwind”Every color uses a shadcn role token, a color named for its job rather than its hue (bg-background, text-foreground, text-muted-foreground, bg-primary, border-input, text-destructive). One token covers both the light and dark themes. @gaia-react/lint fails raw palette classes (like bg-blue-500), color literals, and arbitrary color classes. Other arbitrary values are allowed when no scale step or token fits.
cn (from the cn package) is the only helper for combining classes. It joins classes and resolves conflicts between them, so a className passed in by the caller comes last and overrides the component’s default. Write a conditional class as cond && 'class'; lint rejects the object form and ternaries with an empty branch. Variants with several classes use cva. Custom sizes use rem, never px, and a new role token is named for what the element is.
Triggers when writing Tailwind (opens in a new tab) classes, combining classes with cn, writing conditional classes, building variants, defining @theme tokens, picking rem or px, or working with breakpoints.
Example phrasing: “this needs a hover variant”, “name this color token”, “let the caller override the padding”.
Skeleton Loaders
Section titled “Skeleton Loaders”Loading placeholders (skeletons) that match the real content’s layout exactly. A skeleton uses the same HTML elements and font classes as the real content, plus a shared shimmer class; anything that is not text stays an empty <div>. The skill includes a 200ms delay so a fast load does not flash the skeleton on screen.
Fixed text in a skeleton that gets translated uses t() or <Trans>, the same call the real component uses, so the widths match. The skeleton’s root element carries aria-hidden, so screen readers skip it. The region that receives the content carries aria-busy="true" or a visually hidden role="status" live region.
Triggers when adding loading states, building skeletons for async data, handling pending route transitions, or preventing layout shift.
Test-driven development
Section titled “Test-driven development”tdd runs the red-green-refactor loop Claude uses to build features and fix bugs, and its React companion, tdd-react, holds the frontend testing reference: stories as component tests, hook tests, MSW (opens in a new tab), and Playwright (opens in a new tab). See Test-driven development for how the loop works and how GAIA checks that each test can catch a bug.
Triggers when building features or fixing bugs, mentioning “red-green-refactor”, asking for integration tests, or asking for test-first development.
Example phrasing: “let’s TDD this”, “write a red test for the login flow”.
Playwright CLI
Section titled “Playwright CLI”Microsoft’s Playwright CLI (opens in a new tab) skill for browser automation, copied unchanged from Microsoft’s repository. It can navigate, click, type, take screenshots, manage cookies and storage, run several browser sessions in parallel, and find page elements using the references in the page snapshots it saves. It runs the playwright-cli command installed on your machine, which /setup-gaia installs once per machine, and /update-deps offers to upgrade it when it is out of date.
Triggers when navigating websites, filling forms, taking screenshots, testing web applications, or extracting information from pages. You can also say “use playwright for …”.
ESLint Fixes
Section titled “ESLint Fixes”Claude usually handles ESLint (opens in a new tab) errors well. This skill covers specific errors where this project expects a particular fix, and autofix conflicts where one rule undoes another. If you see Claude fixing an error the wrong way (for example, turning the rule off instead of fixing the cause), ask it to improve the skill for that error:
You fixed that lint error by turning the rule off. Update the eslint-fixes skill so it fixes <rule> properly next time.Lint requires every eslint-disable comment to give a reason after -- (@eslint-community/eslint-comments/require-description).
Accessibility Fixes
Section titled “Accessibility Fixes”Fixes specific accessibility errors reported by axe-core (opens in a new tab), the accessibility checker: color-contrast, label, image-alt, button-name, link-name, region, heading-order, the aria-* rules, tabindex, duplicate-id, and others. For an error not on its list, it makes a fix and then re-runs the check to confirm. Fixes use role tokens and the ui controls, so they work in both light and dark themes. General guidance lives in the accessibility rule; this skill handles the specific fix once an error is reported.
Triggers when an axe rule name (like color-contrast) appears in the output of a story test under Vitest (opens in a new tab) (addon-a11y), the Playwright accessibility helper (frontend/.playwright/a11y.ts), or code-audit-frontend’s accessibility findings.