Art bible plus a build-blocking SVG contract, so any AI tool can add art

Split your art rules into a style guide people judge by eye and an SVG contract a script checks before every build, so AI-drawn art can't collide, leak or break the sprite.

Claude Code intermediate Tested Opus 5.5 · Nov 2026May be dated

The prompt

Run this in a Claude Code session at the root of an app repo with an npm build, before the first piece of art goes in. Any tool that adds art later only needs docs/ART_BIBLE.md and the checker.

Fill in the blanks3 placeholders · empty ones stay as {{…}}

Example

Run in a fresh Vite, React and TypeScript repo with no art yet.

Input:

  • project: Simmer, a recipe web app (Vite, React, TypeScript). Icons appear on recipe cards, in filter chips and in the shopping list, and on printable A6 recipe cards.
  • asset_categories: ingredients: single foods for filters and the shopping list, 24–64 px on white cards, transparent background. tools: kitchen equipment a recipe needs, same sizes, transparent. badges: round diet labels (vegetarian, vegan, gluten-free, spicy) in 20 px chips and on the printed cards.
  • style_direction: Friendly cookbook illustration: warm off-black ink outlines, flat fills with one soft highlight, light from the top left, herb greens and tomato red on cream. Must read at 24 px and print cleanly at 300 dpi with 3 mm bleed.

Output (trimmed: excerpts from both halves of docs/ART_BIBLE.md, then the checker with the planted file in place, and the build after deleting it):

## Contract (machine-checked)

npm run check:art enforces this section; a failure blocks the build.

### Categories

| Category | Folder | viewBox | Background | Content bounds |
|---|---|---|---|---|
| ingredients | `src/art/ingredients/` | `-50 -50 100 100` | transparent | radius 46 |
| tools | `src/art/tools/` | `-50 -50 100 100` | transparent | inset 4 |
| badges | `src/art/badges/` | `-50 -50 100 100` | disc, `<circle cx="0" cy="0" r="44">`, drawn first | radius 47 |

…

3. **Ids.** Every internal id starts with `<asset id>-` (`ingredients-tomato-body`) and is unique across all art: it never appears in another file or equals another asset's id. Every `href="#…"` and `url(#…)` points to an id in the same file. All art ends up in one sprite, so a bare `id="grad"` in two files collides and one file silently paints with the other's gradient.

…

7. **Hairline floor: 1.25 units.** No stroke is thinner than this after transforms. It is 0.1 mm at the smallest printed size: every category prints at 8 mm on the A6 card, so 100 units = 8 mm and 0.1 mm = 1.25 units. `stroke-width` is a plain number in viewBox units; an unset width is 1, under the floor.

…

## Style (judged by eye)

Everything here is judged in the gallery: the **Art** tab of `npm run dev` (or `/#art` on any build). …

### Line

| Line | Width (units) | At 20 px | At 24 px | At 64 px | Used for |
|---|---|---|---|---|---|
| Silhouette | 5–6 | 1.0–1.2 px | 1.2–1.4 px | 3.2–3.8 px | the outer contour of the object, the badge rim, the badge symbol's outline |
| Inner detail | 2.5–3 | 0.5–0.6 px | 0.6–0.7 px | 1.6–1.9 px | veins, seams, rims, calyx, stems, folds |

- Nothing between 3 and 5 units: the gap keeps the two weights apart at every size.
- The silhouette is at least 1 px at the smallest display size: 100 ÷ 20 px = 5 units for badges, 100 ÷ 24 px = 4.2 units for ingredients and tools, so 5 units minimum everywhere.
…

npm run check:art with the planted src/art/ingredients/lemon.svg (exit code 1):

> simmer@0.1.0 check:art
> node scripts/check-art.mjs

src/art/ingredients/lemon.svg: no catalogue entry for "ingredients-lemon": add { "id": "ingredients-lemon", "name": "…", "status": "placeholder", "notes": "" } to "assets" in src/art/catalogue.json
src/art/ingredients/lemon.svg:2: id "body" lacks the "ingredients-lemon-" prefix: rename it "ingredients-lemon-body" and update its href/url(#…) references; bare ids collide in the sprite
src/art/ingredients/lemon.svg:3: forbidden <text>: draw shapes; labels and numbers come from the UI
check-art: 3 problems in 4 SVGs; see docs/ART_BIBLE.md

npm run build after deleting it (exit code 0):

> simmer@0.1.0 build
> node scripts/check-art.mjs && tsc -b && vite build

check-art: 3 SVGs OK
vite v7.3.7 building client environment for production...
…
✓ built in 864ms

The checker listed all three planted problems, each with its fix, and exited 1, which also failed npm run build; once the file was gone the build, which now runs the checker first, exited 0. The Contract turns the 8 mm print size into a 1.25-unit hairline floor, and the checker reads that floor, the size cap and the palette from the bible itself.

Why it works

  • Two halves, split by who checks them. Every Contract rule has a check that blocks the build, so the doc never claims enforcement it doesn't have, and only taste is left to the gallery. Most AI tools never see what they draw, so colours, base shapes, bounds and line floors are written as numbers a script measures.
  • Prefixed, unique ids and an element allowlist make one shared sprite safe. SVG ids are global within a page, so two files with id="grad" paint each other's gradient with no error; the prefix and the cross-file check rule that out. The loader puts every file into the page, and an allowlist keeps scripts, CSS and external fetches out because anything new fails by default.
  • Errors that carry the fix, and a planted failure. Collecting every problem with its path and fix lets the tool that drew the file repair it in one round without reading the bible. Breaking three rules on purpose proves the checker fails when it should.
  • One catalogue, and art by id. The checker, the gallery and the loader read each category's values from the catalogue. The checker fails if the bible's table disagrees with the catalogue, so the copy written for people can't drift. Because code names art by id, swapping a placeholder for final art is a file replacement and a status bump.

Variations

  • Warn while exploring. While the palette is still changing, have the palette check warn instead of fail, and switch it to fail before the first asset goes final.
  • Bitmap art. For PNG or WebP from an image model, keep the two halves but make the Contract check pixel size per category, alpha channel, format and a byte cap, read from the file header.
  • Its own CI step. Also run npm run check:art as a separate CI step, so a pull request shows art errors apart from type errors.
  • Generalised from a game prototype.