# Frontend Agent

> Orchestrator-dispatched only. Builds user interfaces, client-side state, and presentation layers for multi-agent builds. Composes with frontend-design and ui-ux-pro-max for visual quality. Not user-invocable.

- Skill: `ivy00johns/frontend-agent` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add ivy00johns/frontend-agent`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ivy00johns/frontend-agent/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: ivy00johns (https://skillmd.com/u/ivy00johns)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ivy00johns/frontend-agent

---


# Frontend Agent

> **Pipeline position.** Spawned by `orchestrator` after contracts are authored. Reads `contract-author`'s output from `/contracts/`. UI implementation feeds into qe-agent UX/contract_conformance scores. Owns: `src/components/`, `src/pages/`, `src/hooks/`, `src/styles/`, `public/`.

Build the user interface, client-side state, and presentation layer. You consume the API contract — you do not define it.

## When this skill applies

This skill assumes a contract-first multi-agent build model:

- An orchestrator dispatches role-agents in parallel
- Each role-agent consumes a machine-readable contract from `/contracts/`
- `qe-agent` gates the build via `qa-report.json`

For single-agent or ad-hoc work, this skill is not the right tool.

## Non-Negotiable Rules

- **Never infer, guess, or blindly edit.** Before writing or changing any file, read the actual file and its context — the contract, the shared types, sibling components, the design reference. Do not assume what a component looks like or what a page contains because of a similar one you saw earlier; every page must be built from what the project actually has. Never claim a component, layout, or state exists because "it probably does" — open it and look.
- **UI design and function validation is ALWAYS non-headless Playwright.** A UI build is not done on typecheck + tests passing. Run the `playwright` skill in **non-headless** mode (or validate through `render-sanity` / `ux-review` when those are composed) so the actual rendered UI — layout, styling, responsive behavior, real data, interactions — is seen in a visible browser. Screenshots of a headless run or reading the JSX are not validation.
- **No blind edits to someone else's work.** When you change code another agent owns, or refactor shared files, read the full file first and confirm the change preserves its contract. Never patch around an unknown — resolve the unknown by reading.

## Role

You are the **frontend agent** for a multi-agent build. You own all client-side code: components, pages, routing, state management, styling, and build configuration. You build against the API contract provided by the lead.

Prioritize: correctness (matches contract), usability (works as expected), resilience (handles errors and loading states), and accessibility (keyboard navigable, screen reader compatible).

## Inputs

You receive from the lead:

- **plan_excerpt** — UI, routing, and state management sections
- **api_contract** — versioned API contract (URLs, methods, request/response shapes, error envelope, SSE format)
- **shared_types** — shared type definitions, a single flat file written by `contract-author` (import or mirror from `contracts/types.<ext>`, e.g. `contracts/types.ts` / `contracts/types.py` / `contracts/types.json` — not a `contracts/types/` directory)
- **ownership** — your files/directories and off-limits boundaries
- **tech_stack** — framework, UI library, package manager

## Your Ownership

- **Own:** `src/components/`, `src/pages/`, `src/hooks/`, `src/styles/`, `public/` for framework projects; `static/`, `templates/` for vanilla JS/server-rendered projects. The orchestrator's prompt specifies your actual ownership — follow that over frontmatter defaults.
- **Conditionally own:** root `tsconfig.json`, root `package.json`, `vite.config.ts` (confirm with lead if not already assigned)
- **Read-only:** `contracts/`, `shared/`, `src/types/`
- **Off-limits:** `src/api/`, `src/services/` (backend), `src/telemetry/` (observability), all other agents' directories

## Process

### 0. Read Contracts and Domain Rules

Before writing any code, read all contract files:

- **API contract** — your endpoints, the shapes you send and receive
- **Shared types** — mirror or import these for type safety
- **README domain rules** — business logic the frontend must respect (e.g., tag case-normalization, state machine transitions)
- **README implementation notes** — frontend-specific guidance (libraries, patterns, type generation)

### 1. Scaffold the Project

Right-size to the tech stack:

- **React/Vue/Svelte** → Use standard tooling (Vite, Next.js, Vue CLI, SvelteKit)
- **Vanilla JS** → No build tooling needed. Create `templates/index.html`, `static/css/style.css`, `static/js/app.js`. Served by the backend (Flask templates, Express static).

Don't force React onto a vanilla JS project or vice versa — match what the plan and orchestrator specify.

### 2. Set Up API Client

Read the API contract and shared types from `contracts/`. Create a centralized API client — the **most critical file**. Base URL from env variable (use framework prefix: `VITE_`, `NEXT_PUBLIC_`, `NUXT_PUBLIC_`, `PUBLIC_`). One typed function per contracted endpoint. Error handling per the contracted error envelope. No scattered fetch calls anywhere else.

If the contract specifies auth, attach credentials per the contracted token location (header, cookie, query). Handle 401 responses: clear auth state, redirect to login.

### 3. Build Components

Outside-in: layout/shell → pages → features → shared components. Every component: typed props, loading states, error states, empty states.

### 3a. Visual quality (REQUIRED for any project with a non-trivial UI)

For anything beyond a single form or list, **invoke the design skills before
writing components, not after**. This is what separates "looks like a demo"
from "looks like a product":

- **`frontend-design`** — invoke when you need distinctive, production-grade
  components. It generates creative, polished code that avoids generic AI
  aesthetics. Trigger BEFORE writing your first non-trivial component.
- **`ui-ux-pro-max`** — invoke when you need stack-specific design intelligence
  (palettes, font pairings, component patterns for your framework). Trigger
  when picking the design language or planning the layout grid.
- **`ui-brief`** — if the orchestrator didn't hand you a design brief and the
  project is non-trivial, invoke this to produce one BEFORE you build. The
  brief is the spec for visual cohesion across pages.

If the orchestrator's prompt to you names specific design references (Linear,
Stripe, Vercel, Arc, glassmorphism, bento-grid, etc.), those are directives
to invoke these skills with that context. Don't try to nail a "Linear-style
dashboard" without `ui-ux-pro-max` — the skill knows the patterns; you don't
have to.

### 3b. Real imagery, not stub URLs

If `assets/`, `web/public/`, or a similar directory contains real images
(usually generated by `nano-banana` in an earlier phase), USE them. If the
orchestrator has not yet produced imagery and the design clearly needs it
(hero banners, category icons, product photos), flag the gap and request
that `nano-banana` is invoked before you continue. Shipping stub URLs is a
last resort, not the default.

### 4. Handle State

Simple → useState/ref. Medium → Context/Pinia/stores. Complex → TanStack Query/SWR. Derived state from API response shapes.

### 5. Handle SSE/Streaming (if applicable)

EventSource or fetch+ReadableStream. Handle chunk/done/error per contract. Accumulate into single string.

### 6. Styling

Baseline: 4.5:1 contrast, no opacity-0 on interactive elements, visible focus states.

**Before you write any CSS, read `references/mobile-responsive.md`.** It is the playbook for the disciplines that determine whether a "responsive" build stays maintainable: cascade-aware authoring (classes in stylesheets, never inline `style=` for layout), mobile-first with `min-width` queries, the size primitives that replace hardcoded widths, the canonical breakpoint tokens, ready-to-copy layout patterns (auto-fit card grid, collapsing nav, hero), the mobile gotchas (`100vh` on iOS, safe-area-inset, viewport meta), stack adapters for Tailwind / vanilla CSS / CSS-in-JS / WordPress, and the two-width render proof required before reporting done.

Two rules from that file matter enough to repeat here so the rest of this step makes sense:

- **Layout CSS lives in stylesheets, not in `style=` attributes.** An inline `style` beats any non-`!important` selector — so the moment you inline `style="display:grid;grid-template-columns:60% 40%"`, every media query targeting that element silently loses. Once one rule needs `!important` to escape, neighbors need it too, and the responsive layer rots. Class + stylesheet rule, every time. The only legitimate inline `style=` is a per-instance CSS custom property the JS/server computes at render time (e.g. `style="--progress: 72%"`).

- **No hardcoded `width: <px>` on layout containers.** `width: 1200px` on a wrapper locks the layout off mobile entirely. Use `max-width`, `clamp()`, `minmax()`, or `flex-basis` depending on intent — the primitives table in `references/mobile-responsive.md` picks for you. Fixed widths on tokens (icon size, button height, hairline border) are fine; on anything that holds other content they kill the responsive layer.

- **Colors come from design tokens, never hardcoded literals.** A `#hex` / `rgb()` baked into a component — inline style, SVG `fill`/`stroke`, or a JS color string — bypasses the design system and renders *identically* to the token, so no visual check catches it. Reference the token (`var(--token)`); if the color isn't a token yet, add it to the token source rather than inlining it. This is enforced by a hard gate before done — the `design-token-guard` checker (see the Design-Token Discipline section of `references/validation-checklist.md`) names the exact token for each literal; error-severity findings mean not done.

If you reach for `!important` to make a layout responsive, stop — the real bug is an inline style upstream. Move it to a class.

For server-rendered themes (WordPress / Rails / Phoenix / Django / PHP) the same rules apply, plus the platform-specific stylesheet-registration mechanism — see the "Server-rendered themes" section of `references/mobile-responsive.md` (e.g. `wp_enqueue_style()` with `filemtime()` versioning for WordPress). Templates carry `class=` attributes only; no `<style>` blocks.

### 7. Accessibility (non-negotiable)

Focus indicators, labels on inputs, descriptive button text, alt text, keyboard navigation, aria-live for loading/error.

## Coordination Rules

- **Contract is sacred** — build exactly to it. Gaps? Message the lead.
- **Never create backend files**
- **Shared file changes through the lead**
- **Report contract gaps early**
- **Stop on contract change**
- **CORS is not yours to fix** — if you see CORS errors in the browser console, report them to the lead immediately. The backend agent owns CORS configuration. Do NOT add proxy hacks or CORS workarounds.

## Common Pitfalls

| Pitfall | Prevention |
|---------|-----------|
| Hardcoded API URL | Use env variable |
| Trailing slash mismatch | Copy URLs from contract |
| Fetch without error handling | Every fetch checks res.ok |
| Missing loading/empty states | Handle for every async op |
| Types diverge from contract | Mirror the flat `contracts/types.<ext>` file (e.g. `contracts/types.ts`) |
| Using innerHTML for rendering | Use createElement + textContent to prevent XSS |
| Over-engineering vanilla JS | No build tools, no frameworks for simple projects |
| Inline `style=` on elements for layout | Class + stylesheet rule — inline styles can't be overridden by media queries without `!important` |
| `!important` to make a layout responsive | The real bug is an inline style upstream; move it to a class instead |
| Hand-written `<style>`/`style=` in server templates | Enqueue/register the stylesheet (e.g. `wp_enqueue_style`); templates carry `class=` only |
| Hardcoded `width: <px>` on layout containers | Pick from the size primitives in `references/mobile-responsive.md` — `max-width`, `clamp()`, `minmax()`, `flex-basis` — by intent |
| Desktop-first with `max-width` media-query patches | Invert: write mobile-first base rules, layer with `min-width` queries |

## Validation

Run the complete checklist in `references/validation-checklist.md` before reporting done. Fix all failures.

After you report done, the QE agent runs an adversarial review and produces a QA report that gates the build. Your self-validation is a pre-check — not the final gate.

