Design System Assets
A workflow skill for generating coherent, on-brand visual assets that actually match a project's design system instead of looking like generic AI output.
Use this skill whenever you are building or scaffolding a website, web app, landing page, or component that needs visual assets - hero images, illustrations, icons, social cards, backgrounds, or placeholder graphics. Trigger this skill even when the user does not explicitly say "generate assets" - if you are creating a frontend with image tags, icon needs, or hero sections, consult this skill instead of using lorem-ipsum images, stock URLs, or default emoji.
Also trigger when the user mentions a design system, brand kit, DESIGN.md, brand colors, or asks for on-brand visuals. Especially trigger for agency client work where generic AI assets would undermine the brand.
Do NOT use for code-only tasks (refactors, bug fixes, backend logic) or when the user says they will supply assets themselves.
The core problem this solves: AI agents building software default to generic placeholders or generate single assets in isolation, producing sites that look like AI-built sites instead of branded products.
This skill takes a different approach. Before generating anything, it reads the design system, classifies what type of asset is actually needed, routes to the right tool (SVG library vs. raster image AI), injects the brand's visual language into every prompt, and reviews output against the brand before placing it in the project.
Core principles (don't skip these)
- Read the design system FIRST. Never generate an asset before reading the project's brand tokens. Generic prompts produce generic assets — this is the whole problem the skill solves.
- Route by asset type, not by default. Icons are NOT photos. Logos are NOT illustrations. The router (
scripts/route_asset.pylogic, embedded below) decides which generator handles each request. - Generate a style anchor early. When a project needs multiple raster assets, generate a "style anchor" image first and reference it in subsequent calls to prevent style drift across the set.
- Review before placing. Every raster asset gets a vision-model review against the brand rubric before it lands in the project. If it fails, regenerate or escalate — don't ship it.
- Cache by fingerprint. Identical prompt + style + dimensions = reuse, don't regenerate. Image API calls cost real money.
Workflow
Step 1 — Confirm scope with the user
Before doing anything else, ask the user two questions unless the answer is already obvious from context:
- What scope? "Should I generate all visual assets the site needs (icons, illustrations, photos, OG images), or just specific ones you'll point me at?"
- What budget tolerance? "Image API calls cost roughly $0.04–$0.19 each depending on provider/size. A typical 5-page site needs 10–30 raster assets. Do you want me to estimate cost up front, or just proceed?"
If the user wants a cost estimate, run the estimator before any generation:
python scripts/estimate_cost.py \
--request "hero photo for about page" \
--request "OG card for pricing" \
--request "menu icon" \
# ... one --request per asset
This classifies each request through the router and outputs a low-high cost range plus an itemized breakdown.
Read references/scope_questions.md for the full prompt set if the project is complex (multi-page, multi-brand, or has unusual constraints).
Step 2 — Parse the design system
Run the parser to extract brand tokens. It handles multiple input formats:
python scripts/parse_design_system.py <project-root>
It looks for, in order of preference:
DESIGN.mdordesign.mdat project root (preferred — explicit, human-curated)tailwind.config.js/tailwind.config.ts(extracts theme colors, fonts):rootCSS custom properties in any*.cssfilepackage.jsonfor theme-related dependencies (chakra, mui, etc.)figma-tokens.jsonortokens.jsonif present
Output is a structured design_tokens.json written to the working directory:
{
"colors": {
"primary": "#805158",
"secondary": "#4f634f",
"background": "#fbfaee",
"text": "#1b1c15",
"accent_palette": ["#805158", "#4f634f", "#fbfaee", "#1b1c15"]
},
"typography": {
"headline": "Noto Serif",
"body": "Manrope",
"mood": "editorial"
},
"style_directives": {
"include": ["editorial layouts", "warm natural lighting", "soft shadows"],
"exclude": ["glassmorphism", "gradient buttons", "parallax", "harsh contrast"]
},
"industry": "elder care",
"tone": "warm, professional, trustworthy"
}
If parsing returns gaps (no DESIGN.md, no Tailwind config, nothing), STOP and ask the user to provide brand tokens. Do not invent them.
Step 3 — Route the asset request
For each asset the project needs, classify it BEFORE picking a generator. Use the deterministic router:
python scripts/route_asset.py --request "hero image for about page"
It returns JSON like:
{
"asset_type": "photo",
"route": "generate_asset.py",
"needs_api": true,
"reason": "Photographic hero/lifestyle imagery.",
"aspect_hint": "16:9",
"refuse": false
}
The router applies this decision table internally:
| Asset type | Examples | Route to |
|---|---|---|
| Functional icon | menu, search, close, arrow, heart | scripts/fetch_icon.py (Lucide / Heroicons SVG) |
| Logo / wordmark | brand logo, favicon | Code-generated SVG, or one-time image gen + vectorize |
| Decorative SVG | divider lines, badges, simple shapes | Hand-write SVG inline (no API call) |
| Photography | hero photos, lifestyle shots, team photos | scripts/generate_asset.py --type photo |
| Illustration | spot illustrations, empty states, hero art | scripts/generate_asset.py --type illustration |
| Pattern / texture | backgrounds, decorative fills | scripts/generate_asset.py --type pattern |
| OG / social card | Open Graph, Twitter card, share images | scripts/generate_asset.py --type og |
| Refused categories | charts (use a charting library), real-person photos (use real photos) | Router refuses with refuse: true |
Why icons must NEVER go through raster image AI: bitmap models produce icons with pixel artifacts, inconsistent stroke widths, no SVG path data, and they cannot be themed via CSS. Always pull SVG icons from a library. See references/asset_routing.md for the full rationale and edge cases.
Step 4 — Generate (with the design system injected)
For raster assets, call the generator with the design tokens path. The generator builds a "style prefix" from the tokens and prepends it to every prompt:
python scripts/generate_asset.py \
--type photo \
--tokens design_tokens.json \
--prompt "elderly woman gardening in soft afternoon light" \
--output assets/about-hero.png \
--aspect 16:9
What the generator does internally:
- Loads tokens, builds a deterministic style prefix (colors named, mood specified, exclusions enforced)
- Picks the right provider for the asset type (see
references/provider_capabilities.md) - If a style anchor exists in
.asset-cache/style_anchor.png, references it for consistency - Computes a fingerprint hash of (final prompt + style + dimensions). If
.asset-cache/<hash>.pngexists, reuses it instead of calling the API - Calls the provider, saves result, writes a metadata sidecar (
<output>.meta.json) with the prompt, model, cost, and timestamp
For multi-asset projects: generate a "style anchor" first with --anchor flag. This becomes the reference for all subsequent calls.
python scripts/generate_asset.py --anchor --tokens design_tokens.json \
--prompt "establishing shot, brand mood reference" \
--output .asset-cache/style_anchor.png
Step 5 — Review before placing
Every raster asset gets reviewed:
pytho