# CLAUDE.md template: design in Paper, build with Claude Code and HeroUI

> Source: <https://gist.github.com/jrgarciadev/b6d4befd14b27eaa514a48160ddd2ca6>
> Published: 2026-10-29 15:35:34+00:00

is <one line: what it is and what it's built with, e.g. "a Next.js app in a pnpm monorepo">.

Designs live in Paper, and the code follows them. Match spacing, sizes and type exactly.

**One file per feature**, named " ", each holding that feature's boards on one page. Before you design or build, open the file for the feature you're on:

- " Home" ([https://app.paper.design/file/](https://app.paper.design/file/) ): 01 to 01f (the page, loading, empty, the ⋯ menu), D01 in dark.
- " Settings" ([https://app.paper.design/file/](https://app.paper.design/file/) ): 10 to 10h (General, Account, Billing, Notifications).
- " Shell" ([https://app.paper.design/file/](https://app.paper.design/file/) ): 20 to 20d (sidebar, command menu, errors, offline).
- …

Board rules:

- **Every board has an ID that's unique across all files** , like "12c Settings / Billing: Card declined". Code and messages cite boards by it.
- **A flow takes a number, its states take letters** : 12, 12b, 12c… A flow that runs past z takes the next unused number. Next unused: . Update this line when you take one.
- **A flow ends with a "How it behaves" board** : the rules a screenshot can't show (what Esc does, what happens with 0 or 1,000 items, what can be undone).
- **A dark copy's ID starts with D** ("D12 Settings / Billing (Dark)").
- **A new screen goes in its feature's file, and a new feature gets a file of its own** , added to the list above. When a file nears Paper's size limit, split it by part (" Mail / Thread", " Mail / Drafts") and update the list.

I follow along by opening the file in Paper and watching, so:

- **Name the file with the board.** Write the board's full name and its file, linked: "12c Settings / Billing: Card declined" in [Settings](https://app.paper.design/file/%3Cid%3E) , never "board 12c" alone.
- **Say where before you change it.** Before your first write to a file, name the file and the boards you're about to add or change.
- **End with what changed where.** The final message lists each file you changed, linked, with its boards.
- **Paper matches the code.** A change that alters a screen updates its board in the same task, without asking. A board that's out of date is a bug.
- **Draw with the real parts.** Boards use the same tokens, type scale and components as the code, so building one is assembling, not interpreting.

- **Read values, don't eyeball them.** Use Paper's MCP (`get_jsx` ,`get_computed_styles` ) for exact sizes, colors and spacing. Never measure a screenshot.
- **Text in rem, everything else in px.** Divide a Paper text size or line height by the root font size (<16>) to get rem. Spacing, radius and sizes stay px and match Paper 1:1.
- **Compare before calling it done.** Open the built screen in the browser, screenshot it beside the board, and fix what differs.
- **Never reuse real data from screenshots or references.** Names, companies and numbers come from the made-up data in`<path/to/fixtures>` .

- **Components first:** HeroUI (`@heroui/react` ) and HeroUI Pro (`@heroui-pro/react` ) . Write a custom component only when none fits, and make it look like one.
- **One icon set:** <Gravity UI (`@gravity-ui/icons` )>.
- **Colors come from tokens** in`<path/to/theme.css>` , light and dark. No raw hex in components except brand marks.
- **Rows, tabs and segments change instantly** on hover and focus, with no transitions. A fading highlight makes an app feel slow.
- **Keyboard focus is a thin 1px border** , not a 2px accent ring. Text fields keep the ring, which shows where typing goes.
- **Every empty list says why** : an icon, what's empty in a few words, what will show up there, and a button when only the user can fix it. Never a bare "No items".
- **Loading is a skeleton** shaped like the rows it turns into, not "Loading…" text. A spinner is only for an action the user started.
- **A button that waits shows it** : a spinner inside, at the same width. Don't also disable it; disable the buttons around it.
- **Lists that can grow are virtualized** from the start.
- **Copy:** short, plain words. No em dashes in UI copy.

Anything a screen does can be done without the pointer. A new screen ships with its keys, not after them.

- **Plan the keys with the screen.** Before building, list what it does and give each frequent action a key. Check`<path/to/shortcuts.ts>` first, so a key means the same thing everywhere (↵ open, Esc close or go back, ⌘K command menu, J/K move, Z undo).
- **Single-letter keys never fire while typing** or inside a menu or dialog.
- **Map every key** in`<path/to/shortcuts.ts>` , which draws the shortcuts sheet and Settings > Shortcuts.
- **Check it by keyboard alone** before finishing: move, open, act, undo, close.

- No query or fetch per item: one query with a join, or the provider's batch endpoint.
- Run independent calls together (`Promise.all` ), never one after another.
- Never hold a lock or transaction across a network call.
- Fetch only as far as the screen needs.

- Before writing a migration or backfill script: say what data changes and why, then wait for a yes.

Each feature's details live in a CLAUDE.md in its folder, which Claude Code reads once it opens a file there. Keep this file to rules that apply across the app.

- `<src/features/billing/CLAUDE.md>` : Billing (plans, invoices, the card form).
- …

`<pnpm dev>`, `<pnpm typecheck>`, `<pnpm lint>`, `<pnpm test>`.
