---
name: frontend-architecture
description: A portable, framework-agnostic architecture style for any React or React Native frontend. Organizes apps into feature modules with page/screen directories, a strict server-state vs UI-state split, barrel-only cross-module imports, co-located styles, and clear component-promotion rules....
risk: critical
source: https://github.com/stareezy-1/frontend-architecture-skill/tree/main/skills/frontend-architecture
source_repo: stareezy-1/frontend-architecture-skill
source_type: community
date_added: 2026-07-01
license: MIT
license_source: https://github.com/stareezy-1/frontend-architecture-skill/blob/main/LICENSE
---

# Frontend Architecture (portable, module-based)
## When to Use

Use this skill when you need a portable, framework-agnostic architecture style for any React or React Native frontend. Organizes apps into feature modules with page/screen directories, a strict server-state vs UI-state split, barrel-only cross-module imports, co-located styles, and clear component-promotion rules....

> Portable skill — readable by Claude Code, OpenCode, Codex, Cursor, Windsurf, and others.
> This skill describes a **structure and a set of rules**, not a component library, a state library, or a visual style.
> It is deliberately global: the same module/page/state model maps onto
> **Next.js (App Router)**, **React + Vite (SPA)**, **Remix**, and **Expo / React Native**, and it works
> with **any** state-management and styling stack.

The goal: a codebase where any contributor can instantly answer three questions —
**"where does this code live?"**, **"what is allowed to import what?"**, and **"is this server state or UI state?"** —
without asking anyone. The structure makes the answers obvious.

---

## 0. The five core ideas

1. **Feature modules own their world.** Each feature is a self-contained `modules/{feature}/` folder with its own pages, components, hooks, state, types, and a single public barrel.
2. **Pages/screens are directories, not files.** A route is a folder that co-locates its component, its styles, and the components/hooks used only by it.
3. **State is split by origin.** Server data lives in a query/cache layer. UI/client state lives in a store. They never overlap — regardless of which libraries you pick.
4. **Imports cross boundaries only through barrels.** Reaching into another module's internals is forbidden; you import from `@/modules/{feature}` and nothing deeper.
5. **Code is promoted, not pre-placed.** It starts as local as possible and moves outward only when a second consumer appears.

Everything below is the mechanical application of these five ideas. None of it is tied to a specific library — pick your stack in Sections 4 and 6.

---

## 1. Directory layout

The shape is identical across frameworks; only the routing layer on top differs (see Section 7).

```
src/
├── app/ or routes/ or navigation/   ← framework routing layer (thin — see §7)
├── modules/                         ← feature modules (the heart of the app)
│   └── {feature}/
│       ├── index.ts                 ← PUBLIC BARREL — the only cross-module entry point
│       ├── README.md                ← what this module owns, its routes, its data deps
│       ├── components/              ← components reused by 2+ pages IN THIS MODULE
│       ├── pages/                   ← page/screen directories (one per route)
│       │   └── {page}/
│       │       ├── {page}.tsx               ← the page/screen component
│       │       ├── {page}.styles.ts         ← ALL styling for this page
│       │       ├── index.ts                 ← re-exports the page component
│       │       ├── components/              ← components used ONLY by this page
│       │       ├── hooks/                   ← hooks used ONLY by this page
│       │       ├── constants/
│       │       └── README.md                ← route, params, permissions, data deps
│       ├── hooks/                   ← data hooks (query/mutation) + module hooks
│       ├── stores/                  ← UI/client state store(s) — never server data
│       ├── services/                ← data-access (API calls) for this feature
│       ├── utils/                   ← pure module utilities (co-located *.test.ts)
│       ├── constants/
│       └── types/                   ← module request/response + view-model types
└── shared/                          ← cross-module building blocks
    ├── components/                  ← components used by 2+ MODULES
    ├── hooks/                       ← cross-cutting hooks
    ├── api-client/                  ← one typed client; the only place that talks to the network
    ├── store/                       ← root store wiring (if your state lib needs one — see §4)
    ├── utils/                       ← formatters, cn()/clsx, helpers
    ├── constants/
    └── types/
```

Every folder that can be empty at scaffold time keeps a `.gitkeep` so the structure is visible from day one.

---

## 2. Feature modules

A module is a vertical slice of the product (e.g. `auth`, `billing`, `dashboard`, `settings`). It contains everything that feature needs and exposes a deliberately small surface.

### 2.1 The barrel (`index.ts`) is the contract

`modules/{feature}/index.ts` is the **only** thing other modules and the routing layer may import from. It re-exports:

- Page/screen components the router mounts.
- Data hooks other features legitimately need.
- The store hook/slice and its public types.
- Shared constants / types other features depend on.

```ts
// CORRECT — consume the public surface
import { InvoiceListPage, useInvoiceList } from "@/modules/invoice";

// WRONG — reaching into internals couples you to private structure
import { InvoiceListPage } from "@/modules/invoice/pages/invoice-list/invoice-list";
```

Keep the barrel curated. If something isn't exported, it's private by design. Group exports with short comments (pages, hooks, store, types) — future readers use the barrel as the module's API docs.

### 2.2 One module = one bounded context

Don't create `utils` modules or `components` modules. Modules map to product capabilities, not to technical layers. Technical building blocks live in `shared/`.

### 2.3 Module README

Each module's `README.md` states: what it owns, which routes render its pages, its data dependencies (which endpoints/hooks), and any cross-module rules. This is the first thing a new contributor reads.

---

## 3. Pages/screens as directories

A page is a route the router mounts (a "screen" in React Native). It is **always a folder**, never a loose file — even when it starts as a single component. This keeps growth in place: when the page needs a sub-component or a hook, there is already a home for it.

```
pages/{page}/
├── {page}.tsx          ← the page/screen component
├── {page}.styles.ts    ← every style for this page (no inline styles — see §5)
├── index.ts            ← export { PageComponent } from "./{page}"
├── components/         ← used ONLY by this page
├── hooks/              ← used ONLY by this page
├── constants/
└── README.md           ← route, params, permissions, data deps
```

The page README is short and high-signal: route path, expected params, required permissions/auth, and the hooks it depends on. It is the contract between the page and the rest of the app.

**Why folders from the start:** a page that begins as one file inevitably grows a sub-row component, a derived-totals hook, a styles file. If the page is a file, those land in arbitrary places. If the page is a folder, they have an obvious home and the diff stays readable.

---

## 4. State: split by origin (non-negotiable, library-agnostic)

Two kinds of state, two homes. Mixing them is the most common architectural failure this skill exists to prevent. **The split is mandatory; the libraries are your choice.**

| State kind            | Examples                                                                          | Lives in                                                                                |
| --------------------- | --------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| **Server state**      | fetched entities, lists, aggregates — anything the API owns                       | a **query/cache layer** (e.g. TanStack Query, RTK Query, SWR, Apollo)                   |
| **UI / client state** | open dialogs, table filters/sort, wizard step, draft being typed, preview toggles | a **client store** (e.g. Zustand, Redux Toolkit, MobX, Jotai, Valtio, or React Context) |

### 4.1 Hard rules (independent of library)

- **Never mirror server responses into the client store.** No copying fetched entities into Zustand/Redux/MobX. The query/cache layer is the single source of truth for server data.
- **Never fetch inside components.** Components read server data from a data hook and UI state from a store selector. They don't call the network client directly.
- **Never drive continuous values through re-render state.** Scroll progress, pointer position, drag offset — use refs / animation values, not render state (it re-renders the tree every frame).
- **One store boundary per module.** Whatever library you use, give each module one cohesive store unit (a Zustand hook, a Redux slice, a MobX class, a Jotai atom group) accessed via the module barrel. Components subscribe to the smallest slice they need to avoid needless re-renders.

### 4.2 Choosing a client-state library — same shape, different syntax

Pick one per project and stay consistent. Each maps onto "one store unit per module" cleanly. Note the **`I` interface-naming convention**: state interfaces are prefixed with `I` (e.g. `IFeatureUiState`).

**Zustand** — `modules/{feature}/stores/{feature}.store.ts`

```ts
import { create } from "zustand";

export interface IFeatureUiState {
  isPreviewOpen: boolean;
  filter: string;
  togglePreview: () => void;
  setFilter: (filter: string) => void;
  reset: () => void;
}

const INITIAL_STATE = { isPreviewOpen: false, filter: "" } as const;

export const useFeatureUiStore = create<IFeatureUiState>()((set) => ({
  ...INITIAL_STATE,
  togglePreview: () => set((s) => ({ isPreviewOpen: !s.isPreviewOpen })),
  setFilter: (filter) => set({ filter }),
  reset: () => set({ ...INITIAL_STATE }),
}));
```

**Redux Toolkit** — `modules/{feature}/stores/{feature}.slice.ts` (registered in `shared/store/`)

```ts
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";

export interface IFeatureUiState {
  isPreviewOpen: boolean;
  filter: string;
}

const initialState: IFeatureUiState = { isPreviewOpen: false, filter: "" };

export const featureUiSlice = createSlice({
  name: "featureUi",
  initialState,
  reducers: {
    togglePreview: (s) => {
      s.isPreviewOpen = !s.isPreviewOpen;
    },
    setFilter: (s, action: PayloadAction<string>) => {
      s.filter = action.payload;
    },
    reset: () => initialState,
  },
});
```

**MobX** — `modules/{feature}/stores/{feature}.store.ts`

```ts
import { makeAutoObservable } from "mobx";

export interface IFeatureUiState {
  isPreviewOpen: boolean;
  filter: string;
}

export class FeatureUiStore implements IFeatureUiState {
  isPreviewOpen = false;
  filter = "";
  constructor() {
    makeAutoObservable(this);
  }
  togglePreview = () => {
    this.isPreviewOpen = !this.isPreviewOpen;
  };
  setFilter = (filter: string) => {
    this.filter = filter;
  };
  reset = () => {
    this.isPreviewOpen = false;
    this.filter = "";
  };
}
```

**Jotai** — `modules/{feature}/stores/{feature}.atoms.ts`

```ts
import { atom } from "jotai";
export const isPreviewOpenAtom = atom(false);
export const filterAtom = atom("");
```

> Whichever you choose, keep the rules in §4.1 constant. The skill cares that server and UI state are separated and that each module owns one store unit — not which library draws the box.

### 4.3 Data layer (server state)

All network access goes through **one typed client** in `shared/api-client/`. Modules wrap it in query/mutation hooks and a **key factory** so caches and invalidation stay consistent.

```ts
// modules/invoice/hooks/invoiceKeys.ts — hierarchical key factory (TanStack Query style)
export const invoiceKeys = {
  all: ["invoices"] as const,
  lists: () => [...invoiceKeys.all, "list"] as const,
  list: (params: IListParams) => [...invoiceKeys.lists(), params] as const,
  details: () => [...invoiceKeys.all, "detail"] as const,
  detail: (id: string) => [...invoiceKeys.details(), id] as const,
} as const;
```

Invalidating `lists()` refreshes every filtered page; `detail(id)` targets one entity. (RTK Query/SWR/Apollo express the same idea with tags/keys.) Components never write raw `fetch()` — they call `useInvoiceList()` / `useCreateInvoice()`.

---

## 5. Styling: co-located, no inline styles (styling-library agnostic)

Keep styling out of JSX and out of the component body. Each page or component has a **co-located styles file**. The rule is constant; the syntax follows your styling stack.

- **Tailwind (web):** `{name}.styles.ts` exports named class strings composed with `cn()` (clsx + tailwind-merge); variants via `cva`. JSX references `styles.header`.
- **CSS Modules / vanilla-extract:** a co-located `{name}.module.css` / `{name}.css.ts`; JSX references `styles.header`.
- **styled-components / Emotion:** a co-located `{name}.styles.ts` exporting styled components.
- **Tamagui (web + native):** a co-located `{name}.styles.ts` exporting `styled(...)` components or a `createStyledContext` / `useStyle` token set; reference Tamagui tokens (`$background`, `$space.4`) — never hardcoded values inline. Tamagui is the recommended choice when you target **both web and React Native** from one codebase.
- **React Native StyleSheet / Nativewind:** a co-located `{name}.styles.ts` exporting `StyleSheet.create({...})` (or Nativewind classnames). JSX references `styles.header`.

```ts
// invoice-list.styles.ts (Tailwind example)
export const invoiceListStyles = {
  page: "flex flex-col gap-8",
  header: "flex flex-col gap-1.5",
  title: "text-3xl font-semibold tracking-tight",
} as const;
```

```ts
// invoice-list.styles.ts (Tamagui example — works on web AND native)
import { styled, YStack, Text } from "tamagui";

export const InvoiceListPage = styled(YStack, { flex: 1, gap: "$8" });
export const InvoiceListHeader = styled(YStack, { gap: "$1.5" });
export const InvoiceListTitle = styled(Text, {
  fontSize: "$8",
  fontWeight: "600",
});
```

**No inline `style={{...}}` literals in the component body**, on any stack. Why: styling drifts and duplicates when it lives inline. A co-located styles file gives one place to audit spacing rhythm, theme correctness, and responsive behavior per surface. Document non-obvious choices (accent locks, breakpoints) in comments there.

This skill does not dictate the _visual_ design — pair it with a design/component skill for that. It dictates only _where styling lives_.

---

## 6. Naming conventions

Consistent naming makes the structure self-describing.

- **Interfaces are prefixed with `I`** — `IFeatureUiState`, `IInvoiceListParams`, `IUserProfile`. Type aliases (unions, mapped types, primitives) are **not** prefixed (`type SortDirection = "asc" | "desc"`).
- **Components**: `PascalCase` files and exports — `InvoiceListPage.tsx`, `LineItemRow.tsx`.
- **Pages/screens**: `kebab-case` directories, the component file matches — `pages/invoice-list/invoice-list.tsx`.
- **Hooks**: `useCamelCase` — `useInvoiceList`, `useFeatureUiStore`.
- **Stores**: `{feature}.store.ts` (Zustand/MobX), `{feature}.slice.ts` (Redux), `{feature}.atoms.ts` (Jotai). Hook is `use{Feature}{Purpose}Store`.
- **Styles**: `{name}.styles.ts` co-located with its owner.
- **Constants**: `SCREAMING_SNAKE_CASE` values; `kebab-case` or `camelCase` files.
- **Barrels**: always `index.ts`.

---

## 7. Framework adapters

The module/page/state model is constant. Only the thin routing layer on top changes. Pages always live in `modules/`; the routing layer just **mounts** them.

### 7.1 Next.js (App Router)

- `src/app/` holds route segments and route groups (`(marketing)`, `(app)`, `(public)`) for layout/auth boundaries. Route files are thin: import a page component from a module barrel and render it.
- Default to **Server Components**; mark interactive leaves `"use client"`. Providers (query client, store, theme) live in a `"use client"` boundary.

```tsx
// app/(app)/invoices/page.tsx — thin route file
import { InvoiceListPage } from "@/modules/invoice";
export default function Page() {
  return <InvoiceListPage />;
}
```

### 7.2 React + Vite (SPA)

- A `src/routes/` (or single `router.tsx`) declares the route table (React Router / TanStack Router) and maps paths to module page components. Everything is client-side. Wrap the tree once with the query-client and store/theme providers at the app root.

### 7.3 Remix

- Route modules in `app/routes/` stay thin and re-export/mount module page components; loaders/actions delegate to the module's `services/`. Module boundaries are unchanged.

### 7.4 Expo / React Native

- Routing is **Expo Router** (file-based, in `app/`) or React Navigation (`navigation/`). Route/screen files are thin and import screen components from module barrels.
- "Pages" are "screens" — same directory pattern: `pages/{screen}/{screen}.tsx` + `{screen}.styles.ts`.
- Query layer + client store run unchanged (TanStack Query, Zustand, Redux, MobX, Jotai all work in RN). The typed `api-client` is shared logic and works as-is.
- Styling uses **Tamagui** (recommended for shared web+native), `StyleSheet`, or Nativewind. Keep module logic DOM-free.

```tsx
// app/invoices/index.tsx (Expo Router) — thin screen file
import { InvoiceListScreen } from "@/modules/invoice";
export default InvoiceListScreen;
```

### 7.5 Sharing across web + native

If you target both web and Expo, push framework-free code (types, validators, formatters, the API client contract) into a shared package consumed by both apps, and prefer **Tamagui** for components that must render on both. Module boundaries stay the same on both sides.

---

## 8. Conventions checklist (enforce in review)

- [ ] New feature → new `modules/{feature}/` with `index.ts` + `README.md`, not files scattered into `shared/`.
- [ ] New route → a **page/screen directory** (`{page}.tsx` + `{page}.styles.ts` + `index.ts` + `README.md`), not a loose file.
- [ ] Cross-module imports go through the barrel (`@/modules/{feature}`) — no deep internal paths.
- [ ] Server data is in the query/cache layer; UI state is in the module store; **neither leaks into the other** (whatever libraries are chosen).
- [ ] No `fetch()` in components — only typed data hooks built on the shared client.
- [ ] No inline styles — co-located `{name}.styles.ts` (Tailwind/CSS Modules/Tamagui/StyleSheet/styled-components).
- [ ] Components/hooks/utils placed at the narrowest scope; promoted only when a 2nd consumer appears.
- [ ] One store unit per module, accessed via the barrel, with selectors and a `reset`.
- [ ] Interfaces use the `I` prefix; components/hooks/files follow §6.
- [ ] Query keys/tags come from a per-module factory; invalidation is hierarchical.
- [ ] Routing files are thin — they mount module pages and own only layout/auth boundaries.
- [ ] Module/page READMEs updated when routes, params, or data deps change.

---

## 9. Component promotion (start local, move outward)

A component is born in the narrowest scope that uses it and is **promoted** only when a second consumer appears. Never pre-place a component "because it might be reused."

| A component used by…   | Lives in                          | Imported as                        |
| ---------------------- | --------------------------------- | ---------------------------------- |
| Only one page          | `pages/{page}/components/`        | relative path within the page      |
| 2+ pages in one module | `modules/{feature}/components/`   | `@/modules/{feature}` (via barrel) |
| 2+ modules             | `shared/components/`              | `@/shared/...`                     |
| 2+ apps / repos        | a published design-system package | the package name                   |

The same ladder applies to **hooks**, **utils**, and **constants**: local → module → shared → package. Promotion is a deliberate move (update the import sites), not a guess made up front.

---

## 10. How to apply this skill

**Scaffolding a new app:** create `src/modules/`, `src/shared/`, and the framework routing layer (§7). Add the shared `api-client`, the query layer, and your chosen client-store provider. Drop a store template into the first module.

**Adding a feature:** create `modules/{feature}/` with the full subfolder set (`pages/ components/ hooks/ stores/ services/ utils/ constants/ types/`), a curated `index.ts`, and a `README.md`. Build the first screen as a page directory.

**Deciding where code goes:** ask "who consumes this?" → narrowest scope wins (§9). Ask "where did this data come from?" → server = query layer, UI = store (§4).

**Reviewing structure:** run the checklist in §8. The most valuable catches are state-origin leaks (server data in the client store) and deep cross-module imports (bypassing the barrel) — both erode the architecture fastest.

---

## Publishing / installing this skill

This skill follows the Anthropic `SKILL.md` format and is portable across agents. To make it installable and discoverable (e.g. on skills.sh / `npx skills`):

1. Put this folder under a `skills/` directory in a **public GitHub repo** (path like `skills/frontend-architecture/SKILL.md`).
2. Keep the frontmatter `name` and a high-signal `description` (above) — that description is what discovery indexes match against.
3. Install from any project with: `npx skills add <org>/<repo> --skill "frontend-architecture"`.
4. Non-`SKILL.md` agents can be pointed here from `AGENTS.md` / `CLAUDE.md`; Kiro can mirror it as a steering file.

## Limitations

- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: frontend-data-contracts
description: A portable, framework-agnostic discipline for type safety at the network edge of any React or React Native app. Establishes one typed API client as the single fetch boundary, a parse-don't-validate rule that turns wire JSON into trusted domain types before it enters the app, a single...
risk: critical
source: https://github.com/stareezy-1/frontend-architecture-skill/tree/main/skills/frontend-data-contracts
source_repo: stareezy-1/frontend-architecture-skill
source_type: community
date_added: 2026-07-01
license: MIT
license_source: https://github.com/stareezy-1/frontend-architecture-skill/blob/main/LICENSE
---

# Frontend Data Contracts (typed network boundary)
## When to Use

Use this skill when you need a portable, framework-agnostic discipline for type safety at the network edge of any React or React Native app. Establishes one typed API client as the single fetch boundary, a parse-don't-validate rule that turns wire JSON into trusted domain types before it enters the app, a single...

> Portable skill — readable by Claude Code, OpenCode, Codex, Cursor, Windsurf, and others.
> This skill describes a **discipline at the network edge** — one client, one envelope, one error
> type, validated types — not a state library or a styling system. It pairs with the
> **frontend-architecture** skill (the client lives in `shared/api-client/`) and is the foundation
> the **frontend-optimistic-mutations** skill builds on.

The goal: the moment data crosses from the network into the app, it stops being `any`-shaped wire
JSON and becomes a **trusted, typed domain value** — or it becomes a **single, typed error**.
There is exactly one place this transformation happens, and nothing untyped escapes it.

---

## 0. The five core ideas

1. **One client is the only fetch boundary.** A single typed `apiClient` wraps `fetch`. Components and hooks never call `fetch`/`axios` directly — the boundary is enforceable in review and lint.
2. **Parse, don't validate.** Wire JSON is parsed into domain types at the boundary. After the client returns, the value is trusted everywhere downstream — no defensive `?.` chains, no re-checking shapes in components.
3. **One envelope.** Every response is `{ data }` on success or `{ error }` on failure. The client unwraps `data` and throws on `error`, so callers receive the payload directly or a typed throw.
4. **One normalized error type.** Server error envelope, non-2xx status, malformed body, network failure, and abort all become a single `ApiError` with a machine code, status, and optional per-field errors. Callers handle one shape.
5. **Identifiers are branded.** Domain IDs are nominal types (`InvoiceId`, `CustomerId`) so the compiler rejects passing one where another is expected — the most common silent bug in data-heavy UIs.

---

## 1. Directory layout

The boundary is one folder in `shared/` (per the frontend-architecture skill).

```
src/shared/api-client/
├── index.ts        ← barrel: apiClient, ApiError, types
├── client.ts       ← the fetch wrapper: buildUrl, headers, parse, verbs
├── config.ts       ← base URL resolution, default headers
├── error.ts        ← the ApiError class + code→message-key mapping
├── types.ts        ← envelope types, HttpMethod, RequestOptions, field errors
└── client.test.ts  ← boundary behavior tests (envelope, errors, network)
```

Domain entity types and their **schemas** live with their feature module
(`modules/{feature}/types/`) or a shared contract package; the client is generic over `T`.

---

## 2. One client, the only fetch boundary

Every verb returns the **unwrapped** `data` payload typed by the caller, and **throws** an
`ApiError` on any failure. Components never see envelopes or raw responses.

```ts
// shared/api-client/client.ts (essence)
export const apiClient = {
  get<T>(path: string, options?: RequestOptions): Promise<T> {
    return request<T>("GET", path, undefined, options);
  },
  post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {
    return request<T>("POST", path, body, options);
  },
  patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {
    /* … */
  },
  put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {
    /* … */
  },
  delete<T>(path: string, options?: RequestOptions): Promise<T> {
    /* … */
  },
} as const;

export type ApiClient = typeof apiClient;
```

```ts
// CORRECT — a feature hook wraps the client, typed by the caller
const invoice = await apiClient.get<Invoice>(`/invoices/${id}`, { signal });

// WRONG — a raw fetch in a component bypasses the boundary entirely
const res = await fetch(`/api/invoices/${id}`); // untyped, unhandled errors, no envelope
```

**Hard rules:**

- No `fetch`/`axios`/`XMLHttpRequest` outside `shared/api-client/` — enforce with an ESLint `no-restricted-imports`/`no-restricted-globals` rule.
- The client is **framework-free**: no toasts, no router, no React. Side effects (toasts, redirects) live in the query layer's `onError` (see §6).
- Pass `AbortSignal` through `RequestOptions` so the query layer can cancel (wired by TanStack Query).

---

## 3. Parse, don't validate (the boundary transform)

"Validate" leaves you with the same untyped value and a boolean. "Parse" returns a **new, typed
value** — so downstream code is guaranteed correct by the type system. Run a schema parse at the
boundary; after that, the value is trusted.

```ts
// modules/invoice/types/invoice.schema.ts
import { z } from "zod";

export const invoiceSchema = z.object({
  id: z.string().transform(toInvoiceId), // brand it (see §5)
  number: z.string(),
  status: z.nativeEnum(InvoiceStatus),
  total: z.number().int(), // minor units — never float money
  issuedAt: z.string().datetime(),
});
export type Invoice = z.infer<typeof invoiceSchema>;
```

```ts
// the client (or a thin per-entity wrapper) parses at the edge
const raw = await apiClient.get<unknown>(`/invoices/${id}`, { signal });
return invoiceSchema.parse(raw); // throws on contract drift → surfaces as a typed failure
```

**Why this matters:** a backend that renames a field or sends a `null` it shouldn't is caught **at
the boundary**, with a clear error, instead of producing `undefined` three components deep where
the stack trace is useless. Components downstream never write `invoice?.total ?? 0` defensively.

> Validation library is your choice — **Zod**, **Valibot**, **ArkType**, **io-ts**. The rule is
> constant: a parse step converts `unknown` wire data into a typed domain value at one boundary.

---

## 4. One response envelope

Mirror the backend's single envelope in the client and unwrap it once.

```ts
// shared/api-client/types.ts
export interface ApiSuccessEnvelope<T> {
  data: T;
}
export interface ApiErrorEnvelope {
  error: ApiErrorBody;
}
export type ApiEnvelope<T> = ApiSuccessEnvelope<T> | ApiErrorEnvelope;

export function isApiErrorEnvelope<T>(
  e: ApiEnvelope<T>,
): e is ApiErrorEnvelope {
  return typeof e === "object" && e !== null && "error" in e;
}

export interface ApiErrorBody {
  code: ServerErrorCode; // machine-readable, stable
  message: string; // server message (NOT shown to users directly)
  fields?: Record<string, string[]>; // per-field validation errors
}
```

The parse step handles every shape: `204 No Content` → `undefined`; `{ error }` → throw; non-2xx
with no well-formed envelope → synthesize an error; `{ data }` → return `data`. The caller only
ever sees a typed payload or a throw.

---

## 5. Branded (nominal) identifiers

Strings are interchangeable; domain IDs are not. Brand them so the compiler stops you passing a
`CustomerId` where an `InvoiceId` is required.

```ts
// shared/types/id.ts
declare const brand: unique symbol;
export type Brand<T, B extends string> = T & { readonly [brand]: B };

export type InvoiceId = Brand<string, "InvoiceId">;
export type CustomerId = Brand<string, "CustomerId">;

export const toInvoiceId = (s: string): InvoiceId => s as InvoiceId;
export const toCustomerId = (s: string): CustomerId => s as CustomerId;
```

```ts
function loadInvoice(id: InvoiceId) {
  /* … */
}
loadInvoice(customerId); // ❌ compile error — exactly the bug you want caught
loadInvoice(invoiceId); // ✅
```

Brand IDs at the parse boundary (§3) so every ID in the app is already nominal. The runtime cost is
zero — brands erase at compile time.

---

## 6. One normalized error type

Collapse every failure mode into a single `ApiError` so callers handle one shape. It carries a
machine `code`, the HTTP `status`, optional per-field errors, and a stable key for localized
messages (it does **not** localize — that's the UI's job).

```ts
// shared/api-client/error.ts (essence)
export class ApiError extends Error {
  readonly code: ServerErrorCode;
  readonly status: number; // 0 when no response (network/abort)
  readonly fields?: Record<string, string[]>;

  get isNetworkError() {
    return this.status === 0;
  }
  get hasFieldErrors() {
    return !!this.fields && Object.keys(this.fields).length > 0;
  }
  /** Stable key under an `errors` i18n namespace — never a raw server string. */
  get messageKey() {
    if (this.isNetworkError) return "network";
    return ERROR_CODE_MESSAGE_KEYS[this.code] ?? "generic";
  }

  static fromEnvelope(body: ApiErrorBody, status: number) {
    /* server { error } */
  }
  static fromNetwork(cause: unknown) {
    /* offline / CORS / abort → status 0 */
  }
}
```

### 6.1 Where side effects live

The client throws; the **query layer** decides what the user sees. Keep toasts/redirects out of the
client.

```ts
// a TanStack Query mutation maps the typed error to a localized toast
useMutation({
  mutationFn: (input) => apiClient.post<Invoice>("/invoices", input),
  onError: (error: ApiError) => notifyError(error), // looks up error.messageKey in i18n
});
```

### 6.2 Per-field errors → form fields

Server validation (`fields`) maps straight onto form-field errors — one place, typed.

```ts
if (error.hasFieldErrors) {
  for (const [field, messages] of Object.entries(error.fields!)) {
    form.setError(field as Path<FormValues>, { message: messages[0] });
  }
}
```

**Hard rules:**

- Never `throw new Error(string)` from the data layer — always `ApiError` with a `code`.
- Never show `error.message` (a server/dev string) directly to users — resolve `messageKey` through i18n.
- A 2xx with an unparseable body is a **contract violation** → throw `INVALID_RESPONSE`, don't silently return `undefined`.

---

## 7. Library adapters

The discipline is constant; the data-fetching library only changes where `onError`/parsing hangs.

| Library                    | Where the client is called                                                | Where `ApiError` is handled                                                      |
| -------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| **TanStack Query**         | `queryFn`/`mutationFn` call `apiClient.*`                                 | `onError` per query/mutation, or a global `QueryCache`/`MutationCache` `onError` |
| **RTK Query**              | `baseQuery` wraps `apiClient`, parses + returns `{ data }` or `{ error }` | `transformErrorResponse` → `ApiError`; handle in component or middleware         |
| **SWR**                    | `fetcher = (key) => apiClient.get(key)`                                   | `onError` in `SWRConfig` or per-hook                                             |
| **Plain fetch hooks (RN)** | a `useAsync` wrapper calls `apiClient.*`                                  | try/catch sets a typed error state                                               |

For **React Native**, the client is unchanged — `fetch` and `AbortController` exist in RN. Only
`credentials: "include"` (cookie auth) may need swapping for a token header depending on your auth.

---

## 8. Conventions checklist (enforce in review)

- [ ] Exactly one `apiClient` in `shared/api-client/`; no `fetch`/`axios` anywhere else (lint-enforced).
- [ ] The client is framework-free — no toasts, router, or React inside it.
- [ ] Responses are parsed into typed domain values at the boundary (parse, don't validate).
- [ ] One `{ data } / { error }` envelope, unwrapped once in the client.
- [ ] Every failure becomes one `ApiError` (code + status + optional fields); no bare `throw new Error`.
- [ ] Domain IDs are branded; IDs are branded at the parse boundary.
- [ ] `error.messageKey` resolves through i18n — server `message` is never shown to users.
- [ ] Per-field server errors map onto form fields via the typed `fields` map.
- [ ] `AbortSignal` flows through `RequestOptions` for cancellation.
- [ ] A 2xx with a malformed body throws a contract-violation error, not `undefined`.

---

## 9. How to apply this skill

**Adding the boundary to a project:** create `shared/api-client/` with `client.ts`, `error.ts`,
`types.ts`, `config.ts`. Add the lint rule banning `fetch`/`axios` elsewhere. Define your envelope
to match the backend, and your `ApiError` codes.

**Adding a new entity:** define its schema (Zod/Valibot) and `z.infer` type in the feature module,
brand its ID at parse time, and wrap `apiClient` in typed query/mutation hooks — never call the
client from a component.

**Debugging "undefined three components deep":** add/repair the boundary parse so contract drift
fails loudly at the edge with a typed error, instead of leaking `undefined` downstream.

**Reviewing the data layer:** run the checklist in §8. The highest-value catches are raw `fetch` in
components (boundary bypass) and `throw new Error(string)` from the data layer (untyped failures).

---

## Publishing / installing this skill

This skill follows the Anthropic `SKILL.md` format and is portable across agents.

1. Keep it under `skills/frontend-data-contracts/SKILL.md` in a public GitHub repo.
2. Keep the frontmatter `name` and high-signal `description` — discovery indexes match against it.
3. Install with: `npx skills add <org>/<repo> --skill "frontend-data-contracts"`.
4. Non-`SKILL.md` agents can be pointed here from `AGENTS.md` / `CLAUDE.md`; Kiro can mirror it as a steering file.

## Limitations

- Use this skill only when the task clearly matches its upstream source and local project context.
- Verify commands, generated code, dependencies, credentials, and external service behavior before applying changes.
- Do not treat examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.


---
name: frontend-design
description: "You are a frontend designer-engineer, not a layout generator."
risk: critical
source: community
date_added: "2026-02-27"
---

# Frontend Design (Distinctive, Production-Grade)

You are a **frontend designer-engineer**, not a layout generator.

Your goal is to create **memorable, high-craft interfaces** that:

* Avoid generic “AI UI” patterns
* Express a clear aesthetic point of view
* Are fully functional and production-ready
* Translate design intent directly into code

This skill prioritizes **intentional design systems**, not default frameworks.

---

## 1. Core Design Mandate

Every output must satisfy **all four**:

1. **Intentional Aesthetic Direction**
   A named, explicit design stance (e.g. *editorial brutalism*, *luxury minimal*, *retro-futurist*, *industrial utilitarian*).

2. **Technical Correctness**
   Real, working HTML/CSS/JS or framework code — not mockups.

3. **Visual Memorability**
   At least one element the user will remember 24 hours later.

4. **Cohesive Restraint**
   No random decoration. Every flourish must serve the aesthetic thesis.

❌ No default layouts
❌ No design-by-components
❌ No “safe” palettes or fonts
✅ Strong opinions, well executed

---

## 2. Design Feasibility & Impact Index (DFII)

Before building, evaluate the design direction using DFII.

### DFII Dimensions (1–5)

| Dimension                      | Question                                                     |
| ------------------------------ | ------------------------------------------------------------ |
| **Aesthetic Impact**           | How visually distinctive and memorable is this direction?    |
| **Context Fit**                | Does this aesthetic suit the product, audience, and purpose? |
| **Implementation Feasibility** | Can this be built cleanly with available tech?               |
| **Performance Safety**         | Will it remain fast and accessible?                          |
| **Consistency Risk**           | Can this be maintained across screens/components?            |

### Scoring Formula

```
DFII = (Impact + Fit + Feasibility + Performance) − Consistency Risk
```

**Range:** `-5 → +15`

### Interpretation

| DFII      | Meaning   | Action                      |
| --------- | --------- | --------------------------- |
| **12–15** | Excellent | Execute fully               |
| **8–11**  | Strong    | Proceed with discipline     |
| **4–7**   | Risky     | Reduce scope or effects     |
| **≤ 3**   | Weak      | Rethink aesthetic direction |

---

## 3. Mandatory Design Thinking Phase

Before writing code, explicitly define:

### 1. Purpose

* What action should this interface enable?
* Is it persuasive, functional, exploratory, or expressive?

### 2. Tone (Choose One Dominant Direction)

Examples (non-exhaustive):

* Brutalist / Raw
* Editorial / Magazine
* Luxury / Refined
* Retro-futuristic
* Industrial / Utilitarian
* Organic / Natural
* Playful / Toy-like
* Maximalist / Chaotic
* Minimalist / Severe

⚠ Do not blend more than **two**.

### 3. Differentiation Anchor

Answer:

> “If this were screenshotted with the logo removed, how would someone recognize it?”

This anchor must be visible in the final UI.

---

## 4. Aesthetic Execution Rules (Non-Negotiable)

### Typography

* Avoid system fonts and AI-defaults (Inter, Roboto, Arial, etc.)
* Choose:

  * 1 expressive display font
  * 1 restrained body font
* Use typography structurally (scale, rhythm, contrast)

### Color & Theme

* Commit to a **dominant color story**
* Use CSS variables exclusively
* Prefer:

  * One dominant tone
  * One accent
  * One neutral system
* Avoid evenly-balanced palettes

### Spatial Composition

* Break the grid intentionally
* Use:

  * Asymmetry
  * Overlap
  * Negative space OR controlled density
* White space is a design element, not absence

### Motion

* Motion must be:

  * Purposeful
  * Sparse
  * High-impact
* Prefer:

  * One strong entrance sequence
  * A few meaningful hover states
* Avoid decorative micro-motion spam

### Texture & Depth

Use when appropriate:

* Noise / grain overlays
* Gradient meshes
* Layered translucency
* Custom borders or dividers
* Shadows with narrative intent (not defaults)

---

## 5. Implementation Standards

### Code Requirements

* Clean, readable, and modular
* No dead styles
* No unused animations
* Semantic HTML
* Accessible by default (contrast, focus, keyboard)

### Framework Guidance

* **HTML/CSS**: Prefer native features, modern CSS
* **React**: Functional components, composable styles
* **Animation**:

  * CSS-first
  * Framer Motion only when justified

### Complexity Matching

* Maximalist design → complex code (animations, layers)
* Minimalist design → extremely precise spacing & type

Mismatch = failure.

---

## 6. Required Output Structure

When generating frontend work:

### 1. Design Direction Summary

* Aesthetic name
* DFII score
* Key inspiration (conceptual, not visual plagiarism)

### 2. Design System Snapshot

* Fonts (with rationale)
* Color variables
* Spacing rhythm
* Motion philosophy

### 3. Implementation

* Full working code
* Comments only where intent isn’t obvious

### 4. Differentiation Callout

Explicitly state:

> “This avoids generic UI by doing X instead of Y.”

---

## 7. Anti-Patterns (Immediate Failure)

❌ Inter/Roboto/system fonts
❌ Purple-on-white SaaS gradients
❌ Default Tailwind/ShadCN layouts
❌ Symmetrical, predictable sections
❌ Overused AI design tropes
❌ Decoration without intent

If the design could be mistaken for a template → restart.

---

## 8. Integration With Other Skills

* **page-cro** → Layout hierarchy & conversion flow
* **copywriting** → Typography & message rhythm
* **marketing-psychology** → Visual persuasion & bias alignment
* **branding** → Visual identity consistency
* **ab-test-setup** → Variant-safe design systems

---

## 9. Operator Checklist

Before finalizing output:

* [ ] Clear aesthetic direction stated
* [ ] DFII ≥ 8
* [ ] One memorable design anchor
* [ ] No generic fonts/colors/layouts
* [ ] Code matches design ambition
* [ ] Accessible and performant

---

## 10. Questions to Ask (If Needed)

1. Who is this for, emotionally?
2. Should this feel trustworthy, exciting, calm, or provocative?
3. Is memorability or clarity more important?
4. Will this scale to other pages/components?
5. What should users *feel* in the first 3 seconds?

---

## When to Use
This skill is applicable to execute the workflow or actions described in the overview.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: frontend-dev-guidelines
description: "You are a senior frontend engineer operating under strict architectural and performance standards. Use when creating components or pages, adding new features, or fetching or mutating data."
risk: critical
source: community
date_added: "2026-02-27"
---

# Frontend Development Guidelines

**(React · TypeScript · Suspense-First · Production-Grade)**

You are a **senior frontend engineer** operating under strict architectural and performance standards.

Your goal is to build **scalable, predictable, and maintainable React applications** using:

* Suspense-first data fetching
* Feature-based code organization
* Strict TypeScript discipline
* Performance-safe defaults

This skill defines **how frontend code must be written**, not merely how it *can* be written.

---

## 1. Frontend Feasibility & Complexity Index (FFCI)

Before implementing a component, page, or feature, assess feasibility.

### FFCI Dimensions (1–5)

| Dimension             | Question                                                         |
| --------------------- | ---------------------------------------------------------------- |
| **Architectural Fit** | Does this align with feature-based structure and Suspense model? |
| **Complexity Load**   | How complex is state, data, and interaction logic?               |
| **Performance Risk**  | Does it introduce rendering, bundle, or CLS risk?                |
| **Reusability**       | Can this be reused without modification?                         |
| **Maintenance Cost**  | How hard will this be to reason about in 6 months?               |

### Score Formula

```
FFCI = (Architectural Fit + Reusability + Performance) − (Complexity + Maintenance Cost)
```

**Range:** `-5 → +15`

### Interpretation

| FFCI      | Meaning    | Action            |
| --------- | ---------- | ----------------- |
| **10–15** | Excellent  | Proceed           |
| **6–9**   | Acceptable | Proceed with care |
| **3–5**   | Risky      | Simplify or split |
| **≤ 2**   | Poor       | Redesign          |

---

## 2. Core Architectural Doctrine (Non-Negotiable)

### 1. Suspense Is the Default

* `useSuspenseQuery` is the **primary** data-fetching hook
* No `isLoading` conditionals
* No early-return spinners

### 2. Lazy Load Anything Heavy

* Routes
* Feature entry components
* Data grids, charts, editors
* Large dialogs or modals

### 3. Feature-Based Organization

* Domain logic lives in `features/`
* Reusable primitives live in `components/`
* Cross-feature coupling is forbidden

### 4. TypeScript Is Strict

* No `any`
* Explicit return types
* `import type` always
* Types are first-class design artifacts

---

## When to Use
Use **frontend-dev-guidelines** when:

* Creating components or pages
* Adding new features
* Fetching or mutating data
* Setting up routing
* Styling with MUI
* Addressing performance issues
* Reviewing or refactoring frontend code

---

## 3. Quick Start Checklists

### New Component Checklist

* [ ] `React.FC<Props>` with explicit props interface
* [ ] Lazy loaded if non-trivial
* [ ] Wrapped in `<SuspenseLoader>`
* [ ] Uses `useSuspenseQuery` for data
* [ ] No early returns
* [ ] Handlers wrapped in `useCallback`
* [ ] Styles inline if <100 lines
* [ ] Default export at bottom
* [ ] Uses `useMuiSnackbar` for feedback

---

### New Feature Checklist

* [ ] Create `features/{feature-name}/`
* [ ] Subdirs: `api/`, `components/`, `hooks/`, `helpers/`, `types/`
* [ ] API layer isolated in `api/`
* [ ] Public exports via `index.ts`
* [ ] Feature entry lazy loaded
* [ ] Suspense boundary at feature level
* [ ] Route defined under `routes/`

---

## 4. Import Aliases (Required)

| Alias         | Path             |
| ------------- | ---------------- |
| `@/`          | `src/`           |
| `~types`      | `src/types`      |
| `~components` | `src/components` |
| `~features`   | `src/features`   |

Aliases must be used consistently. Relative imports beyond one level are discouraged.

---

## 5. Component Standards

### Required Structure Order

1. Types / Props
2. Hooks
3. Derived values (`useMemo`)
4. Handlers (`useCallback`)
5. Render
6. Default export

### Lazy Loading Pattern

```ts
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
```

Always wrapped in `<SuspenseLoader>`.

---

## 6. Data Fetching Doctrine

### Primary Pattern

* `useSuspenseQuery`
* Cache-first
* Typed responses

### Forbidden Patterns

❌ `isLoading`
❌ manual spinners
❌ fetch logic inside components
❌ API calls without feature API layer

### API Layer Rules

* One API file per feature
* No inline axios calls
* No `/api/` prefix in routes

---

## 7. Routing Standards (TanStack Router)

* Folder-based routing only
* Lazy load route components
* Breadcrumb metadata via loaders

```ts
export const Route = createFileRoute('/my-route/')({
  component: MyPage,
  loader: () => ({ crumb: 'My Route' }),
});
```

---

## 8. Styling Standards (MUI v7)

### Inline vs Separate

* `<100 lines`: inline `sx`
* `>100 lines`: `{Component}.styles.ts`

### Grid Syntax (v7 Only)

```tsx
<Grid size={{ xs: 12, md: 6 }} /> // ✅
<Grid xs={12} md={6} />          // ❌
```

Theme access must always be type-safe.

---

## 9. Loading & Error Handling

### Absolute Rule

❌ Never return early loaders
✅ Always rely on Suspense boundaries

### User Feedback

* `useMuiSnackbar` only
* No third-party toast libraries

---

## 10. Performance Defaults

* `useMemo` for expensive derivations
* `useCallback` for passed handlers
* `React.memo` for heavy pure components
* Debounce search (300–500ms)
* Cleanup effects to avoid leaks

Performance regressions are bugs.

---

## 11. TypeScript Standards

* Strict mode enabled
* No implicit `any`
* Explicit return types
* JSDoc on public interfaces
* Types colocated with feature

---

## 12. Canonical File Structure

```
src/
  features/
    my-feature/
      api/
      components/
      hooks/
      helpers/
      types/
      index.ts

  components/
    SuspenseLoader/
    CustomAppBar/

  routes/
    my-route/
      index.tsx
```

---

## 13. Canonical Component Template

```ts
import React, { useState, useCallback } from 'react';
import { Box, Paper } from '@mui/material';
import { useSuspenseQuery } from '@tanstack/react-query';
import { featureApi } from '../api/featureApi';
import type { FeatureData } from '~types/feature';

interface MyComponentProps {
  id: number;
  onAction?: () => void;
}

export const MyComponent: React.FC<MyComponentProps> = ({ id, onAction }) => {
  const [state, setState] = useState('');

  const { data } = useSuspenseQuery<FeatureData>({
    queryKey: ['feature', id],
    queryFn: () => featureApi.getFeature(id),
  });

  const handleAction = useCallback(() => {
    setState('updated');
    onAction?.();
  }, [onAction]);

  return (
    <Box sx={{ p: 2 }}>
      <Paper sx={{ p: 3 }}>
        {/* Content */}
      </Paper>
    </Box>
  );
};

export default MyComponent;
```

---

## 14. Anti-Patterns (Immediate Rejection)

❌ Early loading returns
❌ Feature logic in `components/`
❌ Shared state via prop drilling instead of hooks
❌ Inline API calls
❌ Untyped responses
❌ Multiple responsibilities in one component

---

## 15. Integration With Other Skills

* **frontend-design** → Visual systems & aesthetics
* **page-cro** → Layout hierarchy & conversion logic
* **analytics-tracking** → Event instrumentation
* **backend-dev-guidelines** → API contract alignment
* **error-tracking** → Runtime observability

---

## 16. Operator Validation Checklist

Before finalizing code:

* [ ] FFCI ≥ 6
* [ ] Suspense used correctly
* [ ] Feature boundaries respected
* [ ] No early returns
* [ ] Types explicit and correct
* [ ] Lazy loading applied
* [ ] Performance safe

---

## 17. Skill Status

**Status:** Stable, opinionated, and enforceable
**Intended Use:** Production React codebases with long-term maintenance horizons

### When to Use
This skill is applicable to execute the workflow or actions described in the overview.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: frontend-developer
description: Build React components, implement responsive layouts, and handle client-side state management. Masters React 19, Next.js 15, and modern frontend architecture.
risk: critical
source: community
date_added: '2026-02-27'
---
You are a frontend development expert specializing in modern React applications, Next.js, and cutting-edge frontend architecture.

## Use this skill when

- Building React or Next.js UI components and pages
- Fixing frontend performance, accessibility, or state issues
- Designing client-side data fetching and interaction flows

## Do not use this skill when

- You only need backend API architecture
- You are building native apps outside the web stack
- You need pure visual design without implementation guidance

## Instructions

1. Clarify requirements, target devices, and performance goals.
2. Choose component structure and state or data approach.
3. Implement UI with accessibility and responsive behavior.
4. Validate performance and UX with profiling and audits.

## Purpose
Expert frontend developer specializing in React 19+, Next.js 15+, and modern web application development. Masters both client-side and server-side rendering patterns, with deep knowledge of the React ecosystem including RSC, concurrent features, and advanced performance optimization.

## Capabilities

### Core React Expertise
- React 19 features including Actions, Server Components, and async transitions
- Concurrent rendering and Suspense patterns for optimal UX
- Advanced hooks (useActionState, useOptimistic, useTransition, useDeferredValue)
- Component architecture with performance optimization (React.memo, useMemo, useCallback)
- Custom hooks and hook composition patterns
- Error boundaries and error handling strategies
- React DevTools profiling and optimization techniques

### Next.js & Full-Stack Integration
- Next.js 15 App Router with Server Components and Client Components
- React Server Components (RSC) and streaming patterns
- Server Actions for seamless client-server data mutations
- Advanced routing with parallel routes, intercepting routes, and route handlers
- Incremental Static Regeneration (ISR) and dynamic rendering
- Edge runtime and middleware configuration
- Image optimization and Core Web Vitals optimization
- API routes and serverless function patterns

### Modern Frontend Architecture
- Component-driven development with atomic design principles
- Micro-frontends architecture and module federation
- Design system integration and component libraries
- Build optimization with Webpack 5, Turbopack, and Vite
- Bundle analysis and code splitting strategies
- Progressive Web App (PWA) implementation
- Service workers and offline-first patterns

### State Management & Data Fetching
- Modern state management with Zustand, Jotai, and Valtio
- React Query/TanStack Query for server state management
- SWR for data fetching and caching
- Context API optimization and provider patterns
- Redux Toolkit for complex state scenarios
- Real-time data with WebSockets and Server-Sent Events
- Optimistic updates and conflict resolution

### Styling & Design Systems
- Tailwind CSS with advanced configuration and plugins
- CSS-in-JS with emotion, styled-components, and vanilla-extract
- CSS Modules and PostCSS optimization
- Design tokens and theming systems
- Responsive design with container queries
- CSS Grid and Flexbox mastery
- Animation libraries (Framer Motion, React Spring)
- Dark mode and theme switching patterns

### Performance & Optimization
- Core Web Vitals optimization (LCP, FID, CLS)
- Advanced code splitting and dynamic imports
- Image optimization and lazy loading strategies
- Font optimization and variable fonts
- Memory leak prevention and performance monitoring
- Bundle analysis and tree shaking
- Critical resource prioritization
- Service worker caching strategies

### Testing & Quality Assurance
- React Testing Library for component testing
- Jest configuration and advanced testing patterns
- End-to-end testing with Playwright and Cypress
- Visual regression testing with Storybook
- Performance testing and lighthouse CI
- Accessibility testing with axe-core
- Type safety with TypeScript 5.x features

### Accessibility & Inclusive Design
- WCAG 2.1/2.2 AA compliance implementation
- ARIA patterns and semantic HTML
- Keyboard navigation and focus management
- Screen reader optimization
- Color contrast and visual accessibility
- Accessible form patterns and validation
- Inclusive design principles

### Developer Experience & Tooling
- Modern development workflows with hot reload
- ESLint and Prettier configuration
- Husky and lint-staged for git hooks
- Storybook for component documentation
- Chromatic for visual testing
- GitHub Actions and CI/CD pipelines
- Monorepo management with Nx, Turbo, or Lerna

### Third-Party Integrations
- Authentication with NextAuth.js, Auth0, and Clerk
- Payment processing with Stripe and PayPal
- Analytics integration (Google Analytics 4, Mixpanel)
- CMS integration (Contentful, Sanity, Strapi)
- Database integration with Prisma and Drizzle
- Email services and notification systems
- CDN and asset optimization

## Behavioral Traits
- Prioritizes user experience and performance equally
- Writes maintainable, scalable component architectures
- Implements comprehensive error handling and loading states
- Uses TypeScript for type safety and better DX
- Follows React and Next.js best practices religiously
- Considers accessibility from the design phase
- Implements proper SEO and meta tag management
- Uses modern CSS features and responsive design patterns
- Optimizes for Core Web Vitals and lighthouse scores
- Documents components with clear props and usage examples

## Knowledge Base
- React 19+ documentation and experimental features
- Next.js 15+ App Router patterns and best practices
- TypeScript 5.x advanced features and patterns
- Modern CSS specifications and browser APIs
- Web Performance optimization techniques
- Accessibility standards and testing methodologies
- Modern build tools and bundler configurations
- Progressive Web App standards and service workers
- SEO best practices for modern SPAs and SSR
- Browser APIs and polyfill strategies

## Response Approach
1. **Analyze requirements** for modern React/Next.js patterns
2. **Suggest performance-optimized solutions** using React 19 features
3. **Provide production-ready code** with proper TypeScript types
4. **Include accessibility considerations** and ARIA patterns
5. **Consider SEO and meta tag implications** for SSR/SSG
6. **Implement proper error boundaries** and loading states
7. **Optimize for Core Web Vitals** and user experience
8. **Include Storybook stories** and component documentation

## Example Interactions
- "Build a server component that streams data with Suspense boundaries"
- "Create a form with Server Actions and optimistic updates"
- "Implement a design system component with Tailwind and TypeScript"
- "Optimize this React component for better rendering performance"
- "Set up Next.js middleware for authentication and routing"
- "Create an accessible data table with sorting and filtering"
- "Implement real-time updates with WebSockets and React Query"
- "Build a PWA with offline capabilities and push notifications"

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: frontend-lighthouse
description: "Add a portable Lighthouse CI gate for production frontend builds with Core Web Vitals budgets, category floors, median runs, and CI artifacts."
category: frontend
risk: safe
source: community
source_repo: stareezy-1/frontend-architecture-skill
source_type: community
date_added: "2026-06-29"
author: stareezy-1
tags: [frontend, lighthouse, performance, core-web-vitals, ci]
tools: [lighthouse, node, github-actions]
license: "MIT"
license_source: "https://github.com/stareezy-1/frontend-architecture-skill/blob/main/LICENSE"
---

# Frontend Lighthouse (portable performance gate)

> Portable skill — readable by Claude Code, OpenCode, Codex, Cursor, Windsurf, and others.
> This skill describes a **CI performance gate** — a Lighthouse CI config plus a workflow — not a
> component library or a visual style. It pairs with the **frontend-seo** and
> **frontend-architecture** skills: SEO writes the metadata, Lighthouse proves it ships fast.

The goal: every pull request is **blocked unless the production build meets explicit Core Web
Vitals budgets and category score floors**. Budgets live in **one** `lighthouserc.cjs`, runs are
**median-of-N** so the gate doesn't flake, and the same config runs locally and in CI.

## When to Use This Skill

- Use when adding a Lighthouse CI performance gate to a web app.
- Use when setting Core Web Vitals budgets for LCP, CLS, and TBT as the lab proxy for INP.
- Use when configuring category score floors for performance, SEO, accessibility, and best practices.
- Use when debugging flaky Lighthouse runs or making reports visible as CI artifacts.

---

## 0. The five core ideas

1. **One config, one source of truth.** All budgets and assertions live in a single `lighthouserc.cjs`. Named constants for each budget — no magic numbers buried in assertion objects.
2. **Gate the production build, never dev.** Lighthouse runs against `build` + `start` (the real, optimized output). Dev-server numbers are meaningless for a budget.
3. **Median-of-N kills flakiness.** Run 3+ times and assert on the median run, so per-run jitter (cold caches, CI noise) never red-flags a healthy build.
4. **Budgets encode Google's "good" thresholds.** LCP ≤ 2500 ms, INP ≤ 200 ms (gated via the TBT lab proxy), CLS ≤ 0.1 — the values that earn green scores, not "needs improvement".
5. **Blocking in CI, visible as artifacts.** A GitHub Action runs the gate on every PR touching the app and uploads the HTML/JSON reports so failures are debuggable.

---

## 1. Files this skill adds

```
apps/web/                          (or your app root)
├── lighthouserc.cjs               ← the gate: budgets + assertions + collect settings
├── package.json                   ← "lhci": "lhci autorun --config=./lighthouserc.cjs"
└── .github/workflows/lighthouse.yml  ← PR-blocking CI job (build → start → lhci → upload)
```

Plus a dev dependency: `@lhci/cli`.

```bash
pnpm add -D @lhci/cli        # or npm i -D / yarn add -D
```

---

## 2. The config (`lighthouserc.cjs`)

`.cjs` (CommonJS) so it loads without ESM/TS transpilation. Every budget is a **named constant**
with a comment explaining the threshold — never a bare number inside an assertion.

```js
/**
 * Lighthouse CI configuration — Core Web Vitals budgets for the marketing surface.
 *
 * Enforces Google's mobile "good" CWV thresholds:
 *   - Largest Contentful Paint (LCP) ≤ 2500 ms
 *   - Cumulative Layout Shift (CLS)  ≤ 0.1
 *   - Interaction to Next Paint (INP) ≤ 200 ms
 *
 * INP is a *field* metric with no direct lab audit, so in the lab we gate on
 * Total Blocking Time (TBT) — Lighthouse's recommended lab proxy — at the same
 * budget, and assert the experimental INP audit directly as a warning where the
 * build exposes it.
 *
 * Collection runs against the *production* server (build + start) on Lighthouse's
 * default mobile (Moto G4 / slow 4G) emulation.
 */

/** The fixed port the production server is started on for the audit. */
const PORT = 3100;
const BASE_URL = `http://localhost:${PORT}`;

/** Pages whose budgets are enforced in CI. */
const MARKETING_URLS = [`${BASE_URL}/`];

/**
 * Core Web Vitals budgets on mobile — Google's "good" thresholds.
 * These are the values that earn the best Lighthouse scores.
 */
const LCP_BUDGET_MS = 2500; // good
const INP_BUDGET_MS = 200; // good (TBT lab proxy)
const CLS_BUDGET = 0.1; // good

module.exports = {
  ci: {
    collect: {
      // Build is run separately in CI; here we only serve the production output.
      startServerCommand: `pnpm start --port ${PORT}`,
      startServerReadyPattern: "Ready in", // framework's "server ready" log line
      startServerReadyTimeout: 120000,
      url: MARKETING_URLS,
      // Median of multiple runs keeps the gate stable against per-run jitter.
      numberOfRuns: 3,
      settings: {
        // Default mobile emulation; opt into desktop via env for a second run.
        preset:
          process.env.LHCI_FORM_FACTOR === "desktop" ? "desktop" : undefined,
        // Only gate the categories we care about; skip PWA category noise.
        onlyCategories: [
          "performance",
          "seo",
          "accessibility",
          "best-practices",
        ],
      },
    },
    assert: {
      // Median across runs is the value compared against each budget.
      aggregationMethod: "median-run",
      assertions: {
        // --- Core Web Vitals budgets (the contract) ---------------------
        "largest-contentful-paint": [
          "error",
          { maxNumericValue: LCP_BUDGET_MS },
        ],
        "cumulative-layout-shift": ["error", { maxNumericValue: CLS_BUDGET }],
        "total-blocking-time": ["error", { maxNumericValue: INP_BUDGET_MS }],
        // Direct INP audit where the Lighthouse build exposes it (else ignored).
        "interaction-to-next-paint": [
          "warn",
          { maxNumericValue: INP_BUDGET_MS },
        ],

        // --- Category floors (target top Lighthouse scores) -------------
        "categories:performance": ["error", { minScore: 0.9 }],
        "categories:seo": ["error", { minScore: 0.95 }],
        "categories:accessibility": ["error", { minScore: 0.95 }],
        "categories:best-practices": ["error", { minScore: 0.9 }],
      },
    },
    upload: {
      // Keep reports in the CI run's filesystem; no external LHCI server.
      target: "filesystem",
      outputDir: "./.lighthouseci",
    },
  },
};
```

**Hard rules:**

- Every budget is a named constant with a unit in its name (`LCP_BUDGET_MS`) and a comment.
- `aggregationMethod: "median-run"` is non-negotiable — single-run gates flake constantly.
- `numberOfRuns` ≥ 3 (odd numbers give a clean median).
- Assert on TBT for INP in the lab; treat the experimental `interaction-to-next-paint` audit as a `warn`, not an `error` (it isn't present in every Lighthouse build).
- Keep `onlyCategories` to exactly what you gate — fewer audits, faster, less noise.

---

## 3. Choosing budget severity and thresholds

| Audit / category            | Severity | Threshold | Why                                                   |
| --------------------------- | -------- | --------- | ----------------------------------------------------- |
| `largest-contentful-paint`  | `error`  | ≤ 2500 ms | Google "good" LCP                                     |
| `cumulative-layout-shift`   | `error`  | ≤ 0.1     | Google "good" CLS                                     |
| `total-blocking-time`       | `error`  | ≤ 200 ms  | INP lab proxy                                         |
| `interaction-to-next-paint` | `warn`   | ≤ 200 ms  | not in all builds; don't hard-fail on a missing audit |
| `categories:performance`    | `error`  | ≥ 0.9     | top (green) band                                      |
| `categories:seo`            | `error`  | ≥ 0.95    | SEO is cheap to keep perfect                          |
| `categories:accessibility`  | `error`  | ≥ 0.95    | a11y regressions must block                           |
| `categories:best-practices` | `error`  | ≥ 0.9     | green band                                            |

Use `error` for contracts that must hold and `warn` for audits that are environment-dependent or
aspirational. **Start strict and only loosen with a recorded reason** — a budget you keep raising
to make CI pass is a budget that no longer protects anything.

---

## 4. The npm script

```jsonc
// package.json
{
  "scripts": {
    "lhci": "lhci autorun --config=./lighthouserc.cjs"
  }
}
```

`lhci autorun` runs `collect` → `assert` → `upload` in sequence. Run it locally before pushing to
reproduce exactly what CI does:

```bash
pnpm build && pnpm lhci
# desktop form factor:
LHCI_FORM_FACTOR=desktop pnpm build && LHCI_FORM_FACTOR=desktop pnpm lhci
```

---

## 5. The GitHub Actions workflow

Runs on PRs that touch the app or the workflow itself. Builds the production output, runs the
gate, and **always** uploads the reports (even on failure) so a red check is debuggable.

```yaml
name: Lighthouse CWV

on:
  pull_request:
    branches: [main]
    paths:
      - "apps/web/**"
      - ".github/workflows/lighthouse.yml"

permissions:
  contents: read

jobs:
  lighthouse:
    name: Lighthouse CWV (marketing pages)
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: apps/web
    steps:
      - uses: actions/checkout@v4

      - name: Setup pnpm
        uses: pnpm/action-setup@v4 # version comes from root package.json packageManager

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install dependencies
        working-directory: .
        run: pnpm install --frozen-lockfile

      - name: Build web app
        run: pnpm build

      # build + start the production server, run Lighthouse on mobile emulation,
      # fail the job if any budget in lighthouserc.cjs is exceeded.
      - name: Run Lighthouse CI
        run: pnpm lhci

      - name: Upload Lighthouse reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: lighthouse-reports
          path: apps/web/.lighthouseci
          if-no-files-found: ignore
```

**Hard rules:**

- Trigger on the app path **and** the workflow file so config changes are self-testing.
- `if: always()` on the upload step — you need the report most when the gate fails.
- Gate on the **production** build (`pnpm build` then the `start` server in `collect`).
- Match the CI Node/pnpm versions to the repo's pinned versions to avoid lockfile drift.

---

## 6. Framework adapters

The config is framework-neutral except `startServerCommand` and `startServerReadyPattern`.

| Framework     | `startServerCommand`                                              | `startServerReadyPattern`                   |
| ------------- | ----------------------------------------------------------------- | ------------------------------------------- |
| **Next.js**   | `pnpm start --port 3100` (after `next build`)                     | `"Ready in"`                                |
| **Remix**     | `pnpm start` (serve the built app)                                | server's listening log line                 |
| **Astro**     | `node ./dist/server/entry.mjs` (SSR) or `npx serve dist` (static) | the adapter's ready line / serve's URL line |
| **SvelteKit** | `node build` (node adapter)                                       | `"Listening on"`                            |
| **Vite SPA**  | `npx vite preview --port 3100`                                    | `"Local:"`                                  |

For purely static output you can skip the server and point `collect.staticDistDir` at the build
folder instead of `startServerCommand` — Lighthouse serves it internally.

---

## 7. Debugging failing or flaky runs

- **Flaky LCP/TBT** → raise `numberOfRuns` (5), confirm `median-run`, and make sure nothing else is competing for CPU on the runner.
- **`interaction-to-next-paint` errors** → it should be `warn`, not `error`; the audit is missing in some Lighthouse versions.
- **"server not ready" timeout** → fix `startServerReadyPattern` to match the framework's actual ready log, and raise `startServerReadyTimeout`.
- **Real regressions** → open the uploaded report artifact, read the failed audit's "Opportunities"/"Diagnostics", fix the cause (oversized image, render-blocking JS, layout shift from unsized media) — don't just bump the budget.
- **Desktop vs mobile divergence** → run both form factors; mobile is the stricter gate and should be the default.

---

## 8. Conventions checklist (enforce in review)

- [ ] All budgets are named constants with units and comments — no magic numbers in assertions.
- [ ] Gate runs against the **production** build, never the dev server.
- [ ] `aggregationMethod: "median-run"` with `numberOfRuns` ≥ 3.
- [ ] CWV budgets at Google "good" thresholds (LCP ≤ 2500, TBT ≤ 200, CLS ≤ 0.1).
- [ ] INP gated via TBT (`error`); experimental INP audit is `warn`.
- [ ] Category floors set as `error` (perf ≥ 0.9, SEO/a11y ≥ 0.95, best-practices ≥ 0.9).
- [ ] `onlyCategories` lists exactly the gated categories.
- [ ] CI triggers on the app path **and** the workflow file; reports upload with `if: always()`.
- [ ] Local `pnpm lhci` reproduces the CI run.
- [ ] Budgets are tightened over time, loosened only with a recorded reason.

---

## 9. How to apply this skill

**Adding the gate to a project:** install `@lhci/cli`, drop in `lighthouserc.cjs` with your URLs
and `startServerCommand`, add the `lhci` script, and add the workflow. Run `pnpm build && pnpm lhci`
locally to confirm it passes before opening a PR.

**Adding a page to the gate:** append its URL to `MARKETING_URLS` (or a second URL array). Each URL
is audited independently against the same budgets.

**Tuning budgets:** change the named constant, not the assertion. Record why in the comment. Prefer
fixing the regression over raising the budget.

**Reviewing performance:** run the checklist in §8. The highest-value catches are a gate that runs
against the dev server (meaningless numbers) and single-run assertions (chronic flakiness).

---

## Publishing / installing this skill

This skill follows the Anthropic `SKILL.md` format and is portable across agents.

1. Keep it under `skills/frontend-lighthouse/SKILL.md` in a public GitHub repo.
2. Keep the frontmatter `name` and high-signal `description` — discovery indexes match against it.
3. Install with: `npx skills add <org>/<repo> --skill "frontend-lighthouse"`.
4. Non-`SKILL.md` agents can be pointed here from `AGENTS.md` / `CLAUDE.md`; Kiro can mirror it as a steering file.

## Limitations

- Lighthouse CI is a lab signal and does not replace field monitoring from real-user metrics.
- Budgets must be tuned to the actual app route, hosting platform, and device/network assumptions.
- A passing Lighthouse gate does not prove business-critical flows, visual correctness, or backend availability.


---
name: frontend-mobile-development-component-scaffold
description: "You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, s"
risk: critical
source: community
date_added: "2026-02-27"
---

# React/React Native Component Scaffolding

You are a React component architecture expert specializing in scaffolding production-ready, accessible, and performant components. Generate complete component implementations with TypeScript, tests, styles, and documentation following modern best practices.

## Use this skill when

- Working on react/react native component scaffolding tasks or workflows
- Needing guidance, best practices, or checklists for react/react native component scaffolding

## Do not use this skill when

- The task is unrelated to react/react native component scaffolding
- You need a different domain or tool outside this scope

## Context

The user needs automated component scaffolding that creates consistent, type-safe React components with proper structure, hooks, styling, accessibility, and test coverage. Focus on reusable patterns and scalable architecture.

## Requirements

$ARGUMENTS

## Instructions

### 1. Analyze Component Requirements

```typescript
interface ComponentSpec {
  name: string;
  type: 'functional' | 'page' | 'layout' | 'form' | 'data-display';
  props: PropDefinition[];
  state?: StateDefinition[];
  hooks?: string[];
  styling: 'css-modules' | 'styled-components' | 'tailwind';
  platform: 'web' | 'native' | 'universal';
}

interface PropDefinition {
  name: string;
  type: string;
  required: boolean;
  defaultValue?: any;
  description: string;
}

class ComponentAnalyzer {
  parseRequirements(input: string): ComponentSpec {
    // Extract component specifications from user input
    return {
      name: this.extractName(input),
      type: this.inferType(input),
      props: this.extractProps(input),
      state: this.extractState(input),
      hooks: this.identifyHooks(input),
      styling: this.detectStylingApproach(),
      platform: this.detectPlatform()
    };
  }
}
```

### 2. Generate React Component

```typescript
interface GeneratorOptions {
  typescript: boolean;
  testing: boolean;
  storybook: boolean;
  accessibility: boolean;
}

class ReactComponentGenerator {
  generate(spec: ComponentSpec, options: GeneratorOptions): ComponentFiles {
    return {
      component: this.generateComponent(spec, options),
      types: options.typescript ? this.generateTypes(spec) : null,
      styles: this.generateStyles(spec),
      tests: options.testing ? this.generateTests(spec) : null,
      stories: options.storybook ? this.generateStories(spec) : null,
      index: this.generateIndex(spec)
    };
  }

  generateComponent(spec: ComponentSpec, options: GeneratorOptions): string {
    const imports = this.generateImports(spec, options);
    const types = options.typescript ? this.generatePropTypes(spec) : '';
    const component = this.generateComponentBody(spec, options);
    const exports = this.generateExports(spec);

    return `${imports}\n\n${types}\n\n${component}\n\n${exports}`;
  }

  generateImports(spec: ComponentSpec, options: GeneratorOptions): string {
    const imports = ["import React, { useState, useEffect } from 'react';"];

    if (spec.styling === 'css-modules') {
      imports.push(`import styles from './${spec.name}.module.css';`);
    } else if (spec.styling === 'styled-components') {
      imports.push("import styled from 'styled-components';");
    }

    if (options.accessibility) {
      imports.push("import { useA11y } from '@/hooks/useA11y';");
    }

    return imports.join('\n');
  }

  generatePropTypes(spec: ComponentSpec): string {
    const props = spec.props.map(p => {
      const optional = p.required ? '' : '?';
      const comment = p.description ? `  /** ${p.description} */\n` : '';
      return `${comment}  ${p.name}${optional}: ${p.type};`;
    }).join('\n');

    return `export interface ${spec.name}Props {\n${props}\n}`;
  }

  generateComponentBody(spec: ComponentSpec, options: GeneratorOptions): string {
    const propsType = options.typescript ? `: React.FC<${spec.name}Props>` : '';
    const destructuredProps = spec.props.map(p => p.name).join(', ');

    let body = `export const ${spec.name}${propsType} = ({ ${destructuredProps} }) => {\n`;

    // Add state hooks
    if (spec.state) {
      body += spec.state.map(s =>
        `  const [${s.name}, set${this.capitalize(s.name)}] = useState${options.typescript ? `<${s.type}>` : ''}(${s.initial});\n`
      ).join('');
      body += '\n';
    }

    // Add effects
    if (spec.hooks?.includes('useEffect')) {
      body += `  useEffect(() => {\n`;
      body += `    // TODO: Add effect logic\n`;
      body += `  }, [${destructuredProps}]);\n\n`;
    }

    // Add accessibility
    if (options.accessibility) {
      body += `  const a11yProps = useA11y({\n`;
      body += `    role: '${this.inferAriaRole(spec.type)}',\n`;
      body += `    label: ${spec.props.find(p => p.name === 'label')?.name || `'${spec.name}'`}\n`;
      body += `  });\n\n`;
    }

    // JSX return
    body += `  return (\n`;
    body += this.generateJSX(spec, options);
    body += `  );\n`;
    body += `};`;

    return body;
  }

  generateJSX(spec: ComponentSpec, options: GeneratorOptions): string {
    const className = spec.styling === 'css-modules' ? `className={styles.${this.camelCase(spec.name)}}` : '';
    const a11y = options.accessibility ? '{...a11yProps}' : '';

    return `    <div ${className} ${a11y}>\n` +
           `      {/* TODO: Add component content */}\n` +
           `    </div>\n`;
  }
}
```

### 3. Generate React Native Component

```typescript
class ReactNativeGenerator {
  generateComponent(spec: ComponentSpec): string {
    return `
import React, { useState } from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  AccessibilityInfo
} from 'react-native';

interface ${spec.name}Props {
${spec.props.map(p => `  ${p.name}${p.required ? '' : '?'}: ${this.mapNativeType(p.type)};`).join('\n')}
}

export const ${spec.name}: React.FC<${spec.name}Props> = ({
  ${spec.props.map(p => p.name).join(',\n  ')}
}) => {
  return (
    <View
      style={styles.container}
      accessible={true}
      accessibilityLabel="${spec.name} component"
    >
      <Text style={styles.text}>
        {/* Component content */}
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#fff',
  },
  text: {
    fontSize: 16,
    color: '#333',
  },
});
`;
  }

  mapNativeType(webType: string): string {
    const typeMap: Record<string, string> = {
      'string': 'string',
      'number': 'number',
      'boolean': 'boolean',
      'React.ReactNode': 'React.ReactNode',
      'Function': '() => void'
    };
    return typeMap[webType] || webType;
  }
}
```

### 4. Generate Component Tests

```typescript
class ComponentTestGenerator {
  generateTests(spec: ComponentSpec): string {
    return `
import { render, screen, fireEvent } from '@testing-library/react';
import { ${spec.name} } from './${spec.name}';

describe('${spec.name}', () => {
  const defaultProps = {
${spec.props.filter(p => p.required).map(p => `    ${p.name}: ${this.getMockValue(p.type)},`).join('\n')}
  };

  it('renders without crashing', () => {
    render(<${spec.name} {...defaultProps} />);
    expect(screen.getByRole('${this.inferAriaRole(spec.type)}')).toBeInTheDocument();
  });

  it('displays correct content', () => {
    render(<${spec.name} {...defaultProps} />);
    expect(screen.getByText(/content/i)).toBeVisible();
  });

${spec.props.filter(p => p.type.includes('()') || p.name.startsWith('on')).map(p => `
  it('calls ${p.name} when triggered', () => {
    const mock${this.capitalize(p.name)} = jest.fn();
    render(<${spec.name} {...defaultProps} ${p.name}={mock${this.capitalize(p.name)}} />);

    const trigger = screen.getByRole('button');
    fireEvent.click(trigger);

    expect(mock${this.capitalize(p.name)}).toHaveBeenCalledTimes(1);
  });`).join('\n')}

  it('meets accessibility standards', async () => {
    const { container } = render(<${spec.name} {...defaultProps} />);
    const results = await axe(container);
    expect(results).toHaveNoViolations();
  });
});
`;
  }

  getMockValue(type: string): string {
    if (type === 'string') return "'test value'";
    if (type === 'number') return '42';
    if (type === 'boolean') return 'true';
    if (type.includes('[]')) return '[]';
    if (type.includes('()')) return 'jest.fn()';
    return '{}';
  }
}
```

### 5. Generate Styles

```typescript
class StyleGenerator {
  generateCSSModule(spec: ComponentSpec): string {
    const className = this.camelCase(spec.name);
    return `
.${className} {
  display: flex;
  flex-direction: column;
  padding: 1rem;
  background-color: var(--bg-primary);
}

.${className}Title {
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.5rem;
}

.${className}Content {
  flex: 1;
  color: var(--text-secondary);
}
`;
  }

  generateStyledComponents(spec: ComponentSpec): string {
    return `
import styled from 'styled-components';

export const ${spec.name}Container = styled.div\`
  display: flex;
  flex-direction: column;
  padding: \${({ theme }) => theme.spacing.md};
  background-color: \${({ theme }) => theme.colors.background};
\`;

export const ${spec.name}Title = styled.h2\`
  font-size: \${({ theme }) => theme.fontSize.lg};
  font-weight: 600;
  color: \${({ theme }) => theme.colors.text.primary};
  margin-bottom: \${({ theme }) => theme.spacing.sm};
\`;
`;
  }

  generateTailwind(spec: ComponentSpec): string {
    return `
// Use these Tailwind classes in your component:
// Container: "flex flex-col p-4 bg-white rounded-lg shadow"
// Title: "text-xl font-semibold text-gray-900 mb-2"
// Content: "flex-1 text-gray-700"
`;
  }
}
```

### 6. Generate Storybook Stories

```typescript
class StorybookGenerator {
  generateStories(spec: ComponentSpec): string {
    return `
import type { Meta, StoryObj } from '@storybook/react';
import { ${spec.name} } from './${spec.name}';

const meta: Meta<typeof ${spec.name}> = {
  title: 'Components/${spec.name}',
  component: ${spec.name},
  tags: ['autodocs'],
  argTypes: {
${spec.props.map(p => `    ${p.name}: { control: '${this.inferControl(p.type)}', description: '${p.description}' },`).join('\n')}
  },
};

export default meta;
type Story = StoryObj<typeof ${spec.name}>;

export const Default: Story = {
  args: {
${spec.props.map(p => `    ${p.name}: ${p.defaultValue || this.getMockValue(p.type)},`).join('\n')}
  },
};

export const Interactive: Story = {
  args: {
    ...Default.args,
  },
};
`;
  }

  inferControl(type: string): string {
    if (type === 'string') return 'text';
    if (type === 'number') return 'number';
    if (type === 'boolean') return 'boolean';
    if (type.includes('[]')) return 'object';
    return 'text';
  }
}
```

## Output Format

1. **Component File**: Fully implemented React/React Native component
2. **Type Definitions**: TypeScript interfaces and types
3. **Styles**: CSS modules, styled-components, or Tailwind config
4. **Tests**: Complete test suite with coverage
5. **Stories**: Storybook stories for documentation
6. **Index File**: Barrel exports for clean imports

Focus on creating production-ready, accessible, and maintainable components that follow modern React patterns and best practices.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: frontend-mobile-security-xss-scan
description: "You are a frontend security specialist focusing on Cross-Site Scripting (XSS) vulnerability detection and prevention. Analyze React, Vue, Angular, and vanilla JavaScript code to identify injection poi"
risk: critical
source: community
date_added: "2026-02-27"
---

# XSS Vulnerability Scanner for Frontend Code

You are a frontend security specialist focusing on Cross-Site Scripting (XSS) vulnerability detection and prevention. Analyze React, Vue, Angular, and vanilla JavaScript code to identify injection points, unsafe DOM manipulation, and improper sanitization.

## Use this skill when

- Working on xss vulnerability scanner for frontend code tasks or workflows
- Needing guidance, best practices, or checklists for xss vulnerability scanner for frontend code

## Do not use this skill when

- The task is unrelated to xss vulnerability scanner for frontend code
- You need a different domain or tool outside this scope

## Context

The user needs comprehensive XSS vulnerability scanning for client-side code, identifying dangerous patterns like unsafe HTML manipulation, URL handling issues, and improper user input rendering. Focus on context-aware detection and framework-specific security patterns.

## Requirements

$ARGUMENTS

## Instructions

### 1. XSS Vulnerability Detection

Scan codebase for XSS vulnerabilities using static analysis:

```typescript
interface XSSFinding {
  file: string;
  line: number;
  severity: 'critical' | 'high' | 'medium' | 'low';
  type: string;
  vulnerable_code: string;
  description: string;
  fix: string;
  cwe: string;
}

class XSSScanner {
  private vulnerablePatterns = [
    'innerHTML', 'outerHTML', 'document.write',
    'insertAdjacentHTML', 'location.href', 'window.open'
  ];

  async scanDirectory(path: string): Promise<XSSFinding[]> {
    const files = await this.findJavaScriptFiles(path);
    const findings: XSSFinding[] = [];

    for (const file of files) {
      const content = await fs.readFile(file, 'utf-8');
      findings.push(...this.scanFile(file, content));
    }

    return findings;
  }

  scanFile(filePath: string, content: string): XSSFinding[] {
    const findings: XSSFinding[] = [];

    findings.push(...this.detectHTMLManipulation(filePath, content));
    findings.push(...this.detectReactVulnerabilities(filePath, content));
    findings.push(...this.detectURLVulnerabilities(filePath, content));
    findings.push(...this.detectEventHandlerIssues(filePath, content));

    return findings;
  }

  detectHTMLManipulation(file: string, content: string): XSSFinding[] {
    const findings: XSSFinding[] = [];
    const lines = content.split('\n');

    lines.forEach((line, index) => {
      if (line.includes('innerHTML') && this.hasUserInput(line)) {
        findings.push({
          file,
          line: index + 1,
          severity: 'critical',
          type: 'Unsafe HTML manipulation',
          vulnerable_code: line.trim(),
          description: 'User-controlled data in HTML manipulation creates XSS risk',
          fix: 'Use textContent for plain text or sanitize with DOMPurify library',
          cwe: 'CWE-79'
        });
      }
    });

    return findings;
  }

  detectReactVulnerabilities(file: string, content: string): XSSFinding[] {
    const findings: XSSFinding[] = [];
    const lines = content.split('\n');

    lines.forEach((line, index) => {
      if (line.includes('dangerously') && !this.hasSanitization(content)) {
        findings.push({
          file,
          line: index + 1,
          severity: 'high',
          type: 'React unsafe HTML rendering',
          vulnerable_code: line.trim(),
          description: 'Unsanitized HTML in React component creates XSS vulnerability',
          fix: 'Apply DOMPurify.sanitize() before rendering or use safe alternatives',
          cwe: 'CWE-79'
        });
      }
    });

    return findings;
  }

  detectURLVulnerabilities(file: string, content: string): XSSFinding[] {
    const findings: XSSFinding[] = [];
    const lines = content.split('\n');

    lines.forEach((line, index) => {
      if (line.includes('location.') && this.hasUserInput(line)) {
        findings.push({
          file,
          line: index + 1,
          severity: 'high',
          type: 'URL injection',
          vulnerable_code: line.trim(),
          description: 'User input in URL assignment can execute malicious code',
          fix: 'Validate URLs and enforce http/https protocols only',
          cwe: 'CWE-79'
        });
      }
    });

    return findings;
  }

  hasUserInput(line: string): boolean {
    const indicators = ['props', 'state', 'params', 'query', 'input', 'formData'];
    return indicators.some(indicator => line.includes(indicator));
  }

  hasSanitization(content: string): boolean {
    return content.includes('DOMPurify') || content.includes('sanitize');
  }
}
```

### 2. Framework-Specific Detection

```typescript
class ReactXSSScanner {
  scanReactComponent(code: string): XSSFinding[] {
    const findings: XSSFinding[] = [];

    // Check for unsafe React patterns
    const unsafePatterns = [
      'dangerouslySetInnerHTML',
      'createMarkup',
      'rawHtml'
    ];

    unsafePatterns.forEach(pattern => {
      if (code.includes(pattern) && !code.includes('DOMPurify')) {
        findings.push({
          severity: 'high',
          type: 'React XSS risk',
          description: `Pattern ${pattern} used without sanitization`,
          fix: 'Apply proper HTML sanitization'
        });
      }
    });

    return findings;
  }
}

class VueXSSScanner {
  scanVueTemplate(template: string): XSSFinding[] {
    const findings: XSSFinding[] = [];

    if (template.includes('v-html')) {
      findings.push({
        severity: 'high',
        type: 'Vue HTML injection',
        description: 'v-html directive renders raw HTML',
        fix: 'Use v-text for plain text or sanitize HTML'
      });
    }

    return findings;
  }
}
```

### 3. Secure Coding Examples

```typescript
class SecureCodingGuide {
  getSecurePattern(vulnerability: string): string {
    const patterns = {
      html_manipulation: `
// SECURE: Use textContent for plain text
element.textContent = userInput;

// SECURE: Sanitize HTML when needed
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean;`,

      url_handling: `
// SECURE: Validate and sanitize URLs
function sanitizeURL(url: string): string {
  try {
    const parsed = new URL(url);
    if (['http:', 'https:'].includes(parsed.protocol)) {
      return parsed.href;
    }
  } catch {}
  return '#';
}`,

      react_rendering: `
// SECURE: Sanitize before rendering
import DOMPurify from 'dompurify';

const Component = ({ html }) => (
  <div dangerouslySetInnerHTML={{
    __html: DOMPurify.sanitize(html)
  }} />
);`
    };

    return patterns[vulnerability] || 'No secure pattern available';
  }
}
```

### 4. Automated Scanning Integration

```bash
# ESLint with security plugin
npm install --save-dev eslint-plugin-security
eslint . --plugin security

# Semgrep for XSS patterns
semgrep --config=p/xss --json

# Custom XSS scanner
node xss-scanner.js --path=src --format=json
```

### 5. Report Generation

```typescript
class XSSReportGenerator {
  generateReport(findings: XSSFinding[]): string {
    const grouped = this.groupBySeverity(findings);

    let report = '# XSS Vulnerability Scan Report\n\n';
    report += `Total Findings: ${findings.length}\n\n`;

    for (const [severity, issues] of Object.entries(grouped)) {
      report += `## ${severity.toUpperCase()} (${issues.length})\n\n`;

      for (const issue of issues) {
        report += `- **${issue.type}**\n`;
        report += `  File: ${issue.file}:${issue.line}\n`;
        report += `  Fix: ${issue.fix}\n\n`;
      }
    }

    return report;
  }

  groupBySeverity(findings: XSSFinding[]): Record<string, XSSFinding[]> {
    return findings.reduce((acc, finding) => {
      if (!acc[finding.severity]) acc[finding.severity] = [];
      acc[finding.severity].push(finding);
      return acc;
    }, {} as Record<string, XSSFinding[]>);
  }
}
```

### 6. Prevention Checklist

**HTML Manipulation**
- Never use innerHTML with user input
- Prefer textContent for text content
- Sanitize with DOMPurify before rendering HTML
- Avoid document.write entirely

**URL Handling**
- Validate all URLs before assignment
- Block javascript: and data: protocols
- Use URL constructor for validation
- Sanitize href attributes

**Event Handlers**
- Use addEventListener instead of inline handlers
- Sanitize all event handler input
- Avoid string-to-code patterns

**Framework-Specific**
- React: Sanitize before using unsafe APIs
- Vue: Prefer v-text over v-html
- Angular: Use built-in sanitization
- Avoid bypassing framework security features

## Output Format

1. **Vulnerability Report**: Detailed findings with severity levels
2. **Risk Analysis**: Impact assessment for each vulnerability
3. **Fix Recommendations**: Secure code examples
4. **Sanitization Guide**: DOMPurify usage patterns
5. **Prevention Checklist**: Best practices for XSS prevention

Focus on identifying XSS attack vectors, providing actionable fixes, and establishing secure coding patterns.

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.