frontend · git:20261021.2ea469d · 2026-10-21 · sha256 9b1242aa2bbe890a

frontend git:20261021.2ea469dA

Immutable. This exact content never changes and is served at /api/v1/blob/9b1242aa2bbe890a.

---
name: frontend
description: "Use when building or changing anything in the frontend: a page, a component, a layout, styling, copy on screen. Produces distinctive, production-grade UI that fits this project's existing design, covers every state, is accessible, and is checked in a browser before it is called done."
---

# Frontend

Build interfaces that look designed for this product, work in every state, and are verified by
looking at them.

## 1. Read before drawing

- Find the project's design system before inventing one: tokens (CSS variables, Tailwind config,
  theme file), the component library, how an existing page of the same kind is built. Grep for
  the pattern you are about to write; reuse it. Two ways to draw a button is one too many.
- Read the repository instructions (`CLAUDE.md` or `AGENTS.md`) for who the users are. A dashboard for engineers wants density; a landing page
  wants one idea per screen.

## 2. Commit to a direction

When there is no system yet, or the ask is a new surface, choose a clear aesthetic direction and
execute it with precision — refined minimalism and bold maximalism both work; intentionality is
the point. Never the generic AI look:

- choose typography for the product; preserve established app fonts and use native system fonts
  where appropriate; introduce a display face only when the content benefits from one
- no purple-to-blue gradient on white, no evenly spread timid palette: one dominant colour, sharp
  accents, and colour reserved for meaning
- no card grid because it is the default; compose for the content — asymmetry, overlap, generous
  space or deliberate density
- colours, spacing, radii and type sizes are tokens (CSS variables), never literals scattered
  through components

## 3. Every state, not the happy one

For each surface, design and build: empty, loading, partial, error (with what to do next),
permission denied, offline or stale, and far more data than expected (long names, 10,000 rows).
A blank area with no reason is a bug. Skeletons match the final layout so nothing jumps.

## 4. Accessible by default

Semantic elements first (`button`, `a`, `label`, `nav`, headings in order). Everything works from
the keyboard with visible focus. Text contrast at least 4.5:1 in light and dark. Images have alt
text; icon-only buttons have labels. Honour `prefers-reduced-motion` and `prefers-color-scheme`.

## 5. Motion and performance

Motion carries information — an arrival, a change, a relationship — or it goes. Prefer CSS
transitions; animate `transform` and `opacity` only. Nothing animates between a person and the
thing they are waiting for. Images are sized and lazy below the fold; fonts are subset and
`font-display: swap`; no layout shift when data lands.

## 6. Responsive

Check the supported minimum width and a comfortable desktop width with no accidental horizontal
scroll. For responsive websites, check 375, 768 and 1440px. Use at least 44px touch targets on
touch interfaces; keep desktop controls consistent with the existing application.

## 7. Look at it

Read the `oya-browser` skill and use Oya Browser exclusively. If Oya is unavailable,
report the blocker without substituting another browser or harness. Run the dev server and open
the page in an owned Oya tab. Take a screenshot at each width and in each theme and look
at it; read the console for errors and warnings. Check the empty and error states by forcing
them. A UI change is not done until you have seen it render — the type checker cannot see a
button that is white on white.

## Report

Say which direction you chose and why, the states you built, what you verified by looking (with
the widths and themes), and anything you could not check.