# Figure Design Guidelines — coded figures

Design rules and conventions for **coded figures**: SVG/HTML sources rendered to
PNG with `${CLAUDE_PLUGIN_ROOT}/render/build-figur.mjs`. Coded is the **primary
route for data figures** (charts, diagrams, comparisons — anything whose content
is real numbers or real structure): precision and reproducibility beat generative
output, and the figure re-renders identically after a correction. Generative
images (`mcp-image`) remain the route for **illustrative** work (cover art, mood,
metaphor). Strategy-level guidance on *when* a post needs a visual at all lives in
`linkedin-visual-style.md`.

## The three render targets

| Target | Size | Use |
|--------|------|-----|
| `article` (default) | 1200 × content-driven height (aspect derived from the source's `viewBox`/dimensions; fallback 16:9) | inline figures in long-form articles |
| `carousel` | 1080 × 1350 (4:5) | carousel/document-post slides |
| `single` | 1200 × 1200 (1:1) | standalone feed-post image |

`--width N` / `--height N` override any target. If an `article`-target source
declares no dimensions, the renderer warns and falls back to 16:9 — set an
explicit `viewBox` (SVG) or `--height`.

```bash
node "${CLAUDE_PLUGIN_ROOT}/render/build-figur.mjs" figN.svg --target article --out figN.png
```

CLI and importable module (`renderFigure` is async). No npm dependencies; needs
headless Chrome (auto-discovered: app bundle → PATH → error with install hint).
No network access during render — sources must be local and self-contained.

## Design rules

Enforced as **warn-only validation** by the renderer (never a hard fail); treat
warnings as a review checklist, not noise:

1. **Text ≥ 24pt/px effective size.** Figures are read on a phone in the feed —
   smaller text is illegible at feed scale. (Same floor as the carousel rules in
   `linkedin-visual-style.md`.)
2. **Color budget: max 1 primary + 1 secondary beyond neutrals.** Grays, white,
   and black are free; every additional hue must earn its place. More colors read
   as noise, not information.
3. **≥ 40 % whitespace — an editorial rule, not machine-checked.** Density is the
   most common coded-figure failure. If the figure needs a legend to be parsed,
   it probably needs to be two figures.

## Brand-token convention

The renderer injects design tokens as CSS variables (`--figur-*`) so figure
sources stay brand-agnostic. Tokens are **user data**, read from the data root
(see `data-path-convention.md`):

```
${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/profile/brand-tokens.json
```

| Key | CSS variable | Neutral default |
|-----|--------------|-----------------|
| `background` | `--figur-background` | `#FFFFFF` |
| `ink` | `--figur-ink` | `#1A1A1A` |
| `muted` | `--figur-muted` | `#5B5B5B` |
| `accent` | `--figur-accent` | `#2F6F9F` |
| `rule` | `--figur-rule` | `#D9D9D9` |
| `fontFamily` | `--figur-fontFamily` | `system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif` |

Missing or unparsable file → neutral defaults with a warning. Partial files are
merged over the defaults. The user's brand is **data, never hardcoded** — figure
sources reference `var(--figur-accent)` etc. and render correctly for any user.

**Authoring rule:** use the `--figur-*` variables for every color and font in a
figure source. A hex value hardcoded in the source defeats the token seam and
counts against the color budget.
