svg-infographic
Use this skill when the user wants a technical or structured infographic/diagram from a description, or asks to export an SVG to PNG.
Good for: architecture diagrams, cloud/network topology, component/layer diagrams, before/after, process/data flow, nested "onion" models, roadmap/risk maps, decision matrices, social-ready technical one-pagers.
Do not use for: photo/illustration-heavy marketing graphics; data-accurate statistical charts (bar/line/scatter/heatmap — use a chart tool); mascots or character illustration; custom logo design or bespoke icon design.
Nuances: a simple qualitative 2×2/3×3 matrix or a status-count badge is fine (it's structure, not a data-accurate chart). Using the built-in line-icon set is expected and encouraged — the non-goal is designing new brand marks, not using icons. A hand-drawn / sketchnote feel is supported as the opt-in sketch preset (references/sketch.md) — "tidy hand-drawn": hand-drawn surface, computed layout; mascots and scene illustration stay out of scope.
Package map — this skill ships as a directory; load the parts on demand:
| File |
When to read |
references/design-kernel.md |
Before choosing colors, tokens or typography — the canonical skin contract: 11-role token model, domain aliases, versioned profiles + skin.mjs resolver, typography profile SSoT(references/typography/), approved contact sheet |
references/types/selection.md |
Before choosing a type — the generated routing view: content signal → TypePack → its spec (derived from the TypePack manifest; migrated types only) |
references/archetypes.md |
For the cross-type premium base recipe (the default visual language). Every per-type rule set has moved to its TypePack spec; the per-archetype sections here are pointer tombstones only |
references/authoring.md |
Always, before writing SVG — detailed geometry/connector/panel/emphasis/color rules and the full icon set; also the manual render fallback |
references/sketch.md |
Only when the user asks for a hand-drawn / sketchnote / handwritten feel — the opt-in sketch preset (paper, handwriting font, rough filters, highlighter) |
scripts/render.mjs |
Canonical renderer (Node 18+, stdlib only) — lint gate → browser discovery → 2× render → exact IHDR verification in one entrypoint; works from any shell incl. Windows CMD/PowerShell without Git Bash |
references/presentation/contract.md |
Only when the user explicitly asks to place an already verified canonical result on a notebook, wall, board, portrait monitor, ambient screen, or a custom registered surface |
scripts/projection.mjs |
Explicit opt-in derived-sibling builder/verifier; never replaces or post-processes the canonical SVG/PNG |
scripts/render.sh |
Thin POSIX/Git-Bash wrapper that delegates to render.mjs (adds only the no-Node diagnostics) |
scripts/check-svg.mjs |
Source lint gate (Node 18+, standard library only; no npm install) — the renderer runs it automatically; run it directly while iterating on the SVG source |
scripts/check-layout.mjs — layout contract guard (container padding/symmetry, equal-gap distribution, atomic card clusters; design-kernel §7). The renderer runs it automatically on layout-annotated SVGs.
scripts/compose.mjs — composition tooling: validate a Composition Plan, place TypePack fragments into template slots (translation-only), and verify the composite against its receipt (design-kernel §8).
scripts/skin.mjs typography-check — effective-font cascade guard (missing wrapper, inherited weight, unannotated secondary, remote font; it inspects the final file itself — standalone and composite alike). The renderer runs it automatically on sketch/typography-annotated SVGs.
scripts/font-probe.mjs — browser runtime font receipt (FontFaceSet load + computed family/weight of scoped KO/EN samples; evidence level: computed+load, NOT rendered-face proof).
| scripts/skin.mjs | Skin profile resolver — validate/resolve/registry over references/skins/ (palette SSoT); use resolved tokens instead of inventing hex values |
0. Preflight — confirm, then offer to change
Before drawing, confirm visual intent, audience, output ratio, and language. Then state the defaults (§6) and note the user can change any of them. Propose an output directory inside the current project and confirm before writing files.
Before promising automated lint or using the bundled render.sh, check whether node --version reports Node.js
18 or newer.
Node is not required to install or discover this skill, or to author an editable SVG. It is required for the
bundled source lint and therefore for a machine-linted handoff.
If Node 18+ is missing:
- Detect the operating system and an available trusted package manager. Confirm that its candidate package
provides Node 18+ before showing the exact install command and asking in the conversation language:
"Would you like me to install Node.js 18+ and continue with automated validation?"
- Install only after the user explicitly approves that system change. Use the detected package manager; do not
substitute a remote
curl | sh installer. If the command needs elevated privileges, surface that approval
rather than hiding it.
- Verify
node --version after installation, then resume the source lint and render workflow. If installation
fails or still provides an older version, report that result and do not retry through a different source
without another approval.
- If the user declines, or no safe package-manager route is available, keep the pre-v0.7.1 quality floor:
complete every item in the manual source checklist, do not invoke
render.sh, and use the Node-free Chromium
path in references/authoring.md §8 to render a 2× PNG. Verify its dimensions and complete the §7 visual QA.
State: "Automated source lint was not run because Node.js 18+ was unavailable. The manual source checklist
and PNG render/visual QA passed." Never label this fallback as machine-linted.
- Only if a Chromium-based browser is also unavailable, deliver an SVG-only draft and state that neither
automated source lint nor PNG visual verification ran.
Classify the input mode first (it sets how much to ask before drawing):
- brief-first — only a topic/goal is given → ask up to ~3 focused questions (audience, key message, ratio), then propose the structure.
- source-first — a doc, notes, or paste is provided → summarize the source and agree the key message before drawing; don't transcribe it box-for-box.
- research-first — "just draft it" → state your assumptions (and any missing data) up front; external lookup may be unavailable, so proceed from the given material plus explicit assumptions.
Lead with the conclusion. Prefer a conclusion-style title over a topic label — "Phased Migration Cuts Cutover Risk" beats "Migration Plan". Sketch the one-to-two-sentence story spine (context → tension → resolution) before placing boxes. Exception: for a named pattern or archetype showcase, a concise topic/pattern title is fine as long as the subtitle or <desc> carries the conclusion.
1. Pick an archetype (shape first)
Pick from the content signal. Read references/types/selection.md first: if the signal routes to a TypePack, that spec is the only rule set — it owns the input contract, the fit/variant contract, the layout formulas and the checks, and references/archetypes.md holds nothing but a pointer for it. If the signal does not appear there, the type has not been migrated yet: read its section in references/archetypes.md, which stays normative for exactly those types.
| Content signal |
Archetype |
| systems/components and their links |
Topology / component |
| ordered steps or handoffs |
Flow (swimlane variant for parallel rails) |
| approval / gate on a simple request path |
Approval / sequence-lite |
| options, trade-offs, qualitative scoring |
Decision / risk matrix, or Cards |
| old vs new |
Before / after |
| layered capability or containment |
Layer stack, or Nested / onion |
| time, phases, milestones |
Roadmap / timeline |
| a few headline items or numbers |
Cards / KPI stat grid (not a chart) |
2. Layout pass — numbers before SVG (required)
This step is the main defense against render-fix loops. Do the arithmetic first; never place a box at an eyeballed coordinate. Produce a short numeric plan (a scratch table of coordinates is enough), then author the SVG from those numbers.
- Canvas. Pick a preset: compact doc 680w · wide architecture 1400×900 · 16:9 slide 1600×900 · social portrait 1080×1350 (4:5). Height is flexible for the doc width. Fix outer margins (≥ 40px wide canvases, ≥ 24px at 680w).
- Regions (PageFrame). Derive the page from the PageFrame contract (
references/design-kernel.md §6): safe area → header region → breathing gap → content region (the TypePack contentBox) → optional support region → optional footer, each with its own gap token; absent elements collapse with their gaps. The page header is the H-C editorial stack: optional muted eyebrow row → H1 (1–2 lines) with the computed --focus title-keyline at its left (canonical default — derived from the H1 line-box, design-kernel §6; the eyebrow square locator is the explicit alternative variant, never doubled with the keyline) → optional muted subtitle → generous breathing room. No vertical accent rail (rejected composition — do not regenerate it). For every panel header, compute headerH = top padding + title line box + title/subtitle gap + subtitle line box + bottom padding; place the divider only after that budget. Do not squeeze the title area until the copy happens to fit.
- Grid arithmetic. For each row of n cards inside a region: choose
cardW and gap (gutter 24–32px), then verify the last edge before drawing: start + (n−1)·(cardW+gap) + cardW ≤ region_right − padding. Same check vertically for columns/stacks. If it doesn't fit: shrink cardW/gap, wrap to a second row, or widen the canvas — decide now, not after a render.
- Text budget. For each box, set lines × chars/line from the box width: ~28–36 Latin chars per line at body size, Korean ≈ 60% of that; 2–3 lines max per box. Edit the copy to fit the budget before writing SVG — abbreviate long tokens now. SVG has no auto-wrap; every line you plan here becomes one
<tspan>.
- Type scale — unified across the diagram (never vary per box):
- 1080-wide social: H1 46 / section 24 / card title 25 / body 19 / caption 16
- 1400–1600 wide: H1 40–44 / section 22 / card title 20–22 / body 16–17 / caption 13–14
- 680-wide docs: title 22 / box label 14 / caption 11
- Icons and copy clusters. Derive every icon-circle center from card geometry (e.g.
cy = card_y + card_h/2). Compute the text cluster's visual bounds from its planned line boxes and center that cluster on the same card_y + card_h/2; never hand-tune either side per language. EN and KO variants must share the same formulas.
- Connector corridors. Budget connectors like cards:
corridor = target_visual_left − source_visual_right, and subtract the marker's real footprint (markerUnits="strokeWidth" multiplies it by the stroke width — formulas in authoring.md §3). If no readable shaft survives, choose a compact arrow, a transition glyph, or a reflow now, not after a render.
3. Author the SVG — core rules
Read references/authoring.md for the detailed rules and the reusable icon set. The render-critical core:
- Root:
<svg xmlns viewBox="0 0 W H" width=W height=H role="img" style="font-family:..."> with <title>/<desc> — font identity (face, weight, asset, digest, license) is owned by the typography SSoT (references/typography/typography-v1.yaml), and how the font travels by the font-delivery policy (references/delivery/font-delivery-v1.yaml). The two axes stay separate, so adding fonts or output modes does not multiply the combinations.
- portable (default, acceptance-grade): from flat's bundled Pretendard 400/700 faces, subset only the glyphs that artifact actually used and embed them as an
@font-face data URI. The alias is the neutral name the policy fixes — Pretendard is a Reserved Font Name, so a subset (a Modified Version) may not carry it. The artifact renders with the same geometry even where the target environment lacks the font. Gallery and release evidence are produced in this mode only.
- system (lightweight, environment-dependent): no embedding; relies on the installed font stack. Light and editable, but a diverging fallback changes wrapping and geometry, so it is never used as acceptance evidence.
- What an editable artifact requires: an embedded subset serves viewing and render fidelity, not editability (edit the copy and any glyph outside the subset becomes tofu). To open and edit the SVG, or to edit it after importing into PowerPoint, Pretendard must be installed in the target environment — PowerPoint does not preserve fonts embedded in an SVG.
- The subset tool is a pinned build-only dependency:
pyftsubset from fonttools 4.53.1 (plus brotli), with fixed options. When the tool is absent it fails rather than sliding quietly into a full embed or a system fallback. Viewing or verifying an existing artifact (lint, layout, render, audit) does not need it.
- The sketch treatment embeds a Hi Melody glyph subset the same way (neutral alias, weight fixed at 400).
- Verification:
skin.mjs typography-check (the static cascade — the renderer runs it automatically as a hard gate when it detects sketch/typography annotation), skin.mjs delivery (policy, alias and RFN cross-check), and scripts/font-probe.mjs when needed (a runtime load/computed receipt — not proof of the rendered face). If flat KO shows tofu (□) on Linux in system mode, install Pretendard or fonts-noto-cjk.
- Direct paint + role annotations — colors encode role, not decoration, but the canonical SVG is authored in the PPT-oriented portable form from the start (
references/design-kernel.md §5): every paint-bearing shape carries direct fill/stroke values plus data-fill-role/data-stroke-role annotations. Recolor by re-running the skin.mjs materializer against a profile, never by hand-editing hex. No var(--…), currentColor or paint classes in canonical output:
<!-- Paint values come from the resolver — get them with
`node scripts/skin.mjs resolve current --mode light`. Never invent hex values:
the palette SSoT is references/skins/ + registry (design-kernel.md §3).
Roles: canvas/surface/surface-tint/ink/muted/rule/focus/positive/warning/
danger/on-focus; aliases: edge/api/compute/data/external/icon. -->
<rect data-fill-role="canvas" fill="#F7F7F5" width="720" height="1020"/>
<rect data-fill-role="surface" data-stroke-role="edge-line"
fill="#FFFFFF" stroke="#2E6DA4" rx="10"/>
<text data-fill-role="edge-ink" fill="#193C5A">…</text>
<path data-stroke-role="edge-line" stroke="#2E6DA4" fill="none" …/>
- Boxes: rounded rect
rx="8" (wide bands rx="12–22"), hairline border stroke-width:1. Each box = tinted fill + same-family border + same-family text (one semantic color family per box).
- Vertical centering: center text with
dominant-baseline="central" and y at the box's vertical center. Two lines straddle the center: title at center−11, sub at center+10, both central. Never rely on the default alphabetic baseline for box labels — it sits high.
- Composite scenes (intent-first selection): one request may need MORE than one type — do not invent a new single type for it. Example prompt: "Use svg-infographic to summarise the key points as four cards along the top and show how the items relate as a tree below. Make it 4:5 for a social post." → plan this as a summary cards (primary) + tree (supporting) composite scene: the composition layer owns the slot split, gaps, hierarchy and reading order; each TypePack owns only its slot interior; both share one skin/typography/icon/connector contract (design-kernel §8). timeline+comparison, process+callout, architecture+decision matrix follow the same rule. Relationships default to numbered/labelled semantic bindings — draw connector lines only when the line itself carries meaning. Bounded: one primary + one or two supporting modules; if space runs out, reduce a supporting variant or split pages — never shrink text/arrows.
- Layout contracts for repeated structures: annotate panel headers and icon-text cards with the opt-in
data-layout-role contract from authoring.md §4/§7; a card contract names its actual background rect card-frame, so frame, icon, and complete text cluster derive from one center. The legacy page-title-header rail contract applies only to pre-kernel examples — new headers follow the H-C cluster (design-kernel §6); the cluster lint contract replaces the rail contract in CP3. Keep unsupported transforms or unusual typography outside annotations and verify them manually.
- Wrapping: one planned line = one
<tspan x=.. dy=..>; keep to the §2 text budget. Inside an opt-in layout contract, use plain numeric y/dy and non-nested <tspan> lines so the lint can accumulate the complete vertical line box.
- Arrows: define one
<marker> arrowhead, use marker-end. Solid = sync/request, dashed (stroke-dasharray="5 4") = async/batch/private. The default head is an open-V stroked marker sized by visible geometry: visible ≈ markerWidth × 8/12, aim visible ≈3× the shaft → markerWidth ≈ 4.5 × shaft (sizing table in authoring.md §3) — filled triangles only as a deliberate choice with the same visible-extent arithmetic. markerUnits="userSpaceOnUse" is mandatory on every referenced marker and the lint gate enforces it — the default markerUnits="strokeWidth" multiplies the head by the stroke width. Set refX so the tip lands on the path endpoint; leave an 8–12px gap between tip and target box and keep a visible shaft behind the head; pick each connector's form (standard / compact / curved / transition glyph / reflow) from the corridor budget, and prefer the gentle single-bend curve recipe when boxes sit at different heights (authoring.md §3).
- On-focus text is light: any label on a saturated fill carries
data-fill-role="on-focus" with a direct light fill — never dark text on a mid/dark saturated fill, and never rely on a blanket text{fill} rule to sort it out.
- Emphasis toolkit: stroke + soft shadow + a number/status badge + a corner label + a filled icon badge. No top accent bar on cards (corner-smear and badge-collision failure modes — details and narrow exception in
authoring.md).
- Icon-first (default on): a line icon in a soft tinted circle (
r≈34–38, data-fill-role="icon-tint" + direct fill) per card/node, authored via the icon library (<use> as convenience), expanded to concrete role-annotated paths with direct paint in canonical output (authoring.md §7). Number badge only when sequence or cross-reference matters — never icon + redundant number.
4. Pre-render checklist (source-level — run before every render)
Run the lint gate first for a verified handoff — it machine-checks the deterministic subset of this list
(ids/references, root viewBox, marker units and footprint, opt-in header/card layout geometry, high-confidence Latin/CJK text overflow) with file/line,
measured values, and a suggested fix per finding. If Node 18+ is unavailable, follow the §0 approval/fallback
branch; the manual fallback does not count as a machine-linted handoff.
node scripts/check-svg.mjs diagram.svg # render.sh runs this again as a hard gate
Hard errors must be fixed before rendering (render.sh refuses at exit 5). Warnings are low-confidence estimates — disposition each one deliberately (fix it, or verify it in the §7 PNG pass); never treat a warning as a pass. Then walk the rest of the checklist — layout arithmetic and design judgement stay yours:
- Containment re-check: the §2 last-edge/bottom-edge arithmetic still holds for what you actually wrote (cards, arrows, badges, labels — including any element you added while authoring). Judge visual bounds, not just the fill rect — half the stroke width, shadow spread, and children drawn outside the base rect count; in a padded panel, an edge that merely touches the parent is a fail, not a pass (formula in
authoring.md §1).
- Text budget: no
<text>/<tspan> line exceeds its planned chars/line; box labels use dominant-baseline="central" with computed y. Pill/badge fit: every pill/badge background covers its actual label width plus ≥ 14–16px padding per side, and EN/KO shared geometry fits the wider language's label (formula in authoring.md §2).
- References resolve: every
<use href="#id"> matches a defined <symbol id>; every marker-end references a defined <marker>; no dangling url(#…).
- Contrast roles: every label on a saturated fill carries
data-fill-role="on-focus" with a direct light fill; no blanket text{fill} rule that overrides on-focus labels via inheritance.
- Corner clearance: badges / status labels / corner icons in the same card corner region have ≥ 20–24px between bounding boxes.
- EN/KO parity: if generating both, the two variants share identical geometry formulas — only text (and text budget) differs.
- Root sanity:
viewBox matches the intended W×H; <title>/<desc> present; font stack on the root.
- Connector budget: every connector's corridor and marker footprint are computed (§2.7); each standard arrow keeps a visible shaft (≥ 12–16px, not hidden under a card/panel by paint order) plus the 8–12px tip gap — a head-only arrow is a fail even when the gap is right; tight corridors carry a deliberate form choice (compact arrow, transition glyph, or reflow —
authoring.md §3); EN/KO share connector geometry and semantics.
5. Render to PNG (2×)
The canonical renderer is render.mjs (Node 18+ stdlib): it runs the §4 source lint as a hard gate (exit 5 =
lint errors), discovers a Chromium-based browser (PATH plus the documented known paths), discloses the exact
executable/version, renders at 2×, and verifies the PNG IHDR dimensions exactly. It needs no shell beyond Node —
on Windows run it directly from CMD or PowerShell, verbatim, with no PowerShell scripting and no Git Bash. Use the
path of the skills root this package is actually installed under.
Claude Code install (.claude\skills):
node .claude\skills\svg-infographic\scripts\render.mjs out\diagram.svg
Codex install (.agents\skills):
node .agents\skills\svg-infographic\scripts\render.mjs out\diagram.svg
On POSIX/Git-Bash environments the familiar wrapper delegates to the same core (exit 6 = Node 18+ missing —
return to the §0 approval/fallback branch rather than bypassing the gate):
bash scripts/render.sh diagram.svg # → diagram.png (2×)
bash scripts/render.sh diagram.svg out.png --transparent
SVG_INFOGRAPHIC_BROWSER=<executable> overrides discovery for owner-approved retries — it must still point at a
Chromium-based browser.
Keep wrapper/intermediate files in the session scratchpad, not the repo. In native PowerShell/CMD the canonical path is still node scripts/render.mjs (above) — the manual fallback in references/authoring.md §8 exists only for the §0 no-Node branch. If no Chromium-based browser is available at all, deliver the SVG only and state the limitation.
The documented Chromium path is the canonical renderer. Non-negotiables:
- Record the exact browser executable/version (render.mjs prints a
renderer: line — include it in the handoff) and the render command.
- On Windows the canonical
node render.mjs path needs neither PowerShell scripting nor Git Bash and probes the four Program Files known paths itself; do not conclude "no browser" from a shell probe alone — run the canonical entrypoint, and only its exit-2 candidate report counts as a discovery failure.
- If the browser launch is denied by a sandbox boundary, render.mjs stops and prints the exact same render command — hand it to the owner for approved outside-sandbox execution; do not silently switch renderer.
- Never substitute ImageMagick, resvg, an app visualization helper, or any other renderer and label the result as a Chromium render; never post-process PNG pixels. If the canonical render remains unavailable, stop with the editable SVG and an explicit unverified-PNG/SVG-only limitation.
6. Defaults to state (and let the user change)
- Style: muted technical · light background · icons = soft circular bg + line icon. Opt-in alternative: sketch preset (tidy hand-drawn — paper, Korean handwriting font, rough strokes; see
references/sketch.md) when the user asks for that feel
- Font: follow the typography profile SSoT — flat: Pretendard plus the normalized fallback (system); sketch: an embedded Hi Melody subset (weight 400). Never declare a stack outside the profile
- Changeable: brand color, ratio (docs vs 4:5 social), dark mode (a separate direct-paint artifact re-materialized from the profile —
diagram.dark.svg; never a media query in one SVG), icon style, Korean/English, SVG-only vs SVG+PNG
- Optional attribution/footer layer: off by default. On request, add a small footer strip (source, author, or date) as its own bottom layer — a labeled footer, not a watermark laid over the content.
- Optional presentation surface: off by default. Only after the editable SVG + exact canonical 2× PNG pass,
and only when the user explicitly asks for presentation projection, read
references/presentation/contract.md and create a separately named projection PNG + receipt. Never describe
it as the canonical PNG or use it as acceptance evidence for the source artifact. Use only the surface
manifest's registered blend profile; never improvise opacity, filters, or per-run color tuning. If projection
is requested without a named surface, use the bundled paper-notebook; an explicit bundled or user manifest
always overrides it.
7. Verify the PNG (quality bar)
The pre-render checklist covered the source; now check what only the pixels show:
- Rendering: no text overflow or clipped glyphs; text vertically centered in its box; correct Korean/CJK rendering (no tofu); PNG is exactly 2× the viewBox (render.sh reports this); icons visible (no blank circles); labels on accent fills read clearly (AA-like separation).
- Containment (visual): every child sits inside its container plus inner padding — scan the PNG for anything touching or crossing a panel edge; an element can spill without touching any text. For canonical output, annotate containers/groups and run
scripts/check-layout.mjs (design-kernel §7) so padding, inset symmetry, and repeated-gap uniformity are machine-verified from the layout receipt.
- Connectors: every arrow reads as shaft + head — no head-only arrows and no head buried under a panel or card; the head joins its shaft cleanly and neither touches the target border nor floats detached; head/shaft proportion reads natural — a visible head ≈4× its shaft width or more is a fail for newly authored diagrams, and a visible head below ≈2.5× reads weak (aim ≈3×; the lint computes the visible ratio from the marker's glyph extent —
authoring.md §3). Pre-contract legacy examples keep their approved visuals under an explicit data-lint-allow="marker-footprint" exception — never add that attribute to silence a new diagram. A curve keeps one readable bend (an accidental S-shape is a fail); a transition glyph reads as flow, not as an icon or play button.
- Two-pass inspection (required): first view the PNG at fit-to-page scale — every major stage connector must be immediately recognizable as an arrow (not a hairline or a tiny decorative glyph), and the reading order must survive the zoom-out; then inspect close-up for connector/text detail. A lint warning that maps to a hard visual rule (head proportion, containment) is never accepted just because the artifact is otherwise attractive.
- Explicit fail examples (each has shipped as a defect at least once): an edge label crossed by its own path or ambiguous about which route it labels; icon/text overlap or a feedback loop crowded against cards; a major-flow connector that disappears at fit-to-page scale; a rendered EN/KO pair whose CJK glyphs break or whose connectors differ between languages.
- Message: the archetype fits the content; one clear reading order; the title states a conclusion, not just a topic; text density stays low per box; any matrix/labels read unambiguously; depth and language fit the stated audience.
- The SVG stays editable — direct per-shape paint with
data-fill-role/data-stroke-role annotations (recolor via the skin.mjs materializer), no flattened or rasterized text. The source SVG is authoritative: never patch or redraw a connector only in the PNG.
If a check fails: fix the SVG source, re-run §4, re-render. Track which §4 item would have caught it — if none, the checklist is missing a rule.
8. Output & handoff
Save both the SVG (editable source of truth) and the PNG (2× export for slides/docs/social). For the flat treatment the renderer uses locally installed fonts (KO/CJK falls back to the platform default — Apple SD Gothic Neo on macOS, Malgun Gothic on Windows, Noto Sans KR on Linux); sketch artifacts embed their handwriting subset, so they render identically everywhere. Verify no tofu in the PNG either way.
1---2name: svg-infographic3description: Author technical/structured SVG infographics and diagrams, then render them to crisp PNG with a headless browser. Best for architecture diagrams, topology maps, flows, before/after comparisons, nested/onion layer models, roadmaps, decision matrices, and social-ready technical one-pagers. Prefers clean line icons in soft tinted circles. First-class Korean/CJK text. Includes an opt-in "tidy hand-drawn" sketch preset (paper background, Korean handwriting font, rough strokes, highlighter). Not for photo-heavy or illustration-heavy graphics, statistical charts, or mascot/character illustration.4license: LICENSE.txt5---67# svg-infographic89Use this skill when the user wants a technical or structured infographic/diagram from a description, or asks to export an SVG to PNG.1011Good for: architecture diagrams, cloud/network topology, component/layer diagrams, before/after, process/data flow, nested "onion" models, roadmap/risk maps, decision matrices, social-ready technical one-pagers.1213Do **not** use for: photo/illustration-heavy marketing graphics; **data-accurate** statistical charts (bar/line/scatter/heatmap — use a chart tool); mascots or character illustration; custom logo design or bespoke icon design.1415Nuances: a **simple qualitative** 2×2/3×3 matrix or a status-count badge is fine (it's structure, not a data-accurate chart). Using the built-in line-icon set is expected and encouraged — the non-goal is *designing new brand marks*, not using icons. A hand-drawn / sketchnote feel is supported as the **opt-in sketch preset** (`references/sketch.md`) — "tidy hand-drawn": hand-drawn surface, computed layout; mascots and scene illustration stay out of scope.1617**Package map** — this skill ships as a directory; load the parts on demand:1819| File | When to read |20| --- | --- |21| `references/design-kernel.md` | **Before choosing colors, tokens or typography** — the canonical skin contract: 11-role token model, domain aliases, versioned profiles + `skin.mjs` resolver, typography profile SSoT(`references/typography/`), approved contact sheet |22| `references/types/selection.md` | **Before choosing a type** — the generated routing view: content signal → TypePack → its spec (derived from the TypePack manifest; migrated types only) |23| `references/archetypes.md` | **For the cross-type premium base recipe** (the default visual language). Every per-type rule set has moved to its TypePack spec; the per-archetype sections here are pointer tombstones only |24| `references/authoring.md` | **Always, before writing SVG** — detailed geometry/connector/panel/emphasis/color rules and the full icon set; also the manual render fallback |25| `references/sketch.md` | Only when the user asks for a hand-drawn / sketchnote / handwritten feel — the opt-in sketch preset (paper, handwriting font, rough filters, highlighter) |26| `scripts/render.mjs` | **Canonical renderer** (Node 18+, stdlib only) — lint gate → browser discovery → 2× render → exact IHDR verification in one entrypoint; works from any shell incl. Windows CMD/PowerShell without Git Bash |27| `references/presentation/contract.md` | Only when the user explicitly asks to place an already verified canonical result on a notebook, wall, board, portrait monitor, ambient screen, or a custom registered surface |28| `scripts/projection.mjs` | Explicit opt-in derived-sibling builder/verifier; never replaces or post-processes the canonical SVG/PNG |29| `scripts/render.sh` | Thin POSIX/Git-Bash wrapper that delegates to `render.mjs` (adds only the no-Node diagnostics) |30| `scripts/check-svg.mjs` | Source lint gate (Node 18+, standard library only; no npm install) — the renderer runs it automatically; run it directly while iterating on the SVG source |31- `scripts/check-layout.mjs` — layout contract guard (container padding/symmetry, equal-gap distribution, atomic card clusters; design-kernel §7). The renderer runs it automatically on layout-annotated SVGs.32- `scripts/compose.mjs` — composition tooling: validate a Composition Plan, place TypePack fragments into template slots (translation-only), and verify the composite against its receipt (design-kernel §8).33- `scripts/skin.mjs typography-check` — effective-font cascade guard (missing wrapper, inherited weight, unannotated secondary, remote font; it inspects the final file itself — standalone and composite alike). The renderer runs it automatically on sketch/typography-annotated SVGs.34- `scripts/font-probe.mjs` — browser runtime font receipt (FontFaceSet load + computed family/weight of scoped KO/EN samples; evidence level: computed+load, NOT rendered-face proof).35| `scripts/skin.mjs` | Skin profile resolver — `validate`/`resolve`/`registry` over `references/skins/` (palette SSoT); use resolved tokens instead of inventing hex values |3637## 0. Preflight — confirm, then offer to change3839Before drawing, confirm visual intent, audience, output ratio, and language. Then state the defaults (§6) and note the user can change any of them. Propose an output directory **inside the current project** and confirm before writing files.4041Before promising automated lint or using the bundled `render.sh`, check whether `node --version` reports Node.js4218 or newer.43Node is not required to install or discover this skill, or to author an editable SVG. It is required for the44bundled source lint and therefore for a machine-linted handoff.4546If Node 18+ is missing:47481. Detect the operating system and an available trusted package manager. Confirm that its candidate package49 provides Node 18+ before showing the exact install command and asking in the conversation language:50 **"Would you like me to install Node.js 18+ and continue with automated validation?"**512. Install only after the user explicitly approves that system change. Use the detected package manager; do not52 substitute a remote `curl | sh` installer. If the command needs elevated privileges, surface that approval53 rather than hiding it.543. Verify `node --version` after installation, then resume the source lint and render workflow. If installation55 fails or still provides an older version, report that result and do not retry through a different source56 without another approval.574. If the user declines, or no safe package-manager route is available, keep the pre-v0.7.1 quality floor:58 complete every item in the manual source checklist, do not invoke `render.sh`, and use the Node-free Chromium59 path in `references/authoring.md` §8 to render a 2× PNG. Verify its dimensions and complete the §7 visual QA.60 State: **"Automated source lint was not run because Node.js 18+ was unavailable. The manual source checklist61 and PNG render/visual QA passed."** Never label this fallback as machine-linted.625. Only if a Chromium-based browser is also unavailable, deliver an SVG-only draft and state that neither63 automated source lint nor PNG visual verification ran.6465**Classify the input mode first** (it sets how much to ask before drawing):6667- **brief-first** — only a topic/goal is given → ask up to ~3 focused questions (audience, key message, ratio), then propose the structure.68- **source-first** — a doc, notes, or paste is provided → summarize the source and agree the key message before drawing; don't transcribe it box-for-box.69- **research-first** — "just draft it" → state your assumptions (and any missing data) up front; external lookup may be unavailable, so proceed from the given material plus explicit assumptions.7071**Lead with the conclusion.** Prefer a conclusion-style title over a topic label — "Phased Migration Cuts Cutover Risk" beats "Migration Plan". Sketch the one-to-two-sentence story spine (context → tension → resolution) before placing boxes. Exception: for a **named pattern or archetype showcase**, a concise topic/pattern title is fine as long as the subtitle or `<desc>` carries the conclusion.7273## 1. Pick an archetype (shape first)7475Pick from the content signal. Read `references/types/selection.md` first: if the signal routes to a **TypePack**, that spec is the only rule set — it owns the input contract, the fit/variant contract, the layout formulas and the checks, and `references/archetypes.md` holds nothing but a pointer for it. If the signal does not appear there, the type has not been migrated yet: read its section in `references/archetypes.md`, which stays normative for exactly those types.7677| Content signal | Archetype |78| --- | --- |79| systems/components and their links | Topology / component |80| ordered steps or handoffs | Flow (swimlane variant for parallel rails) |81| approval / gate on a simple request path | Approval / sequence-lite |82| options, trade-offs, qualitative scoring | Decision / risk matrix, or Cards |83| old vs new | Before / after |84| layered capability or containment | Layer stack, or Nested / onion |85| time, phases, milestones | Roadmap / timeline |86| a few headline items or numbers | Cards / KPI stat grid (not a chart) |8788## 2. Layout pass — numbers before SVG (required)8990**This step is the main defense against render-fix loops. Do the arithmetic first; never place a box at an eyeballed coordinate.** Produce a short numeric plan (a scratch table of coordinates is enough), then author the SVG from those numbers.91921. **Canvas.** Pick a preset: **compact doc** 680w · **wide architecture** 1400×900 · **16:9 slide** 1600×900 · **social portrait** 1080×1350 (4:5). Height is flexible for the doc width. Fix outer margins (≥ 40px wide canvases, ≥ 24px at 680w).932. **Regions (PageFrame).** Derive the page from the PageFrame contract (`references/design-kernel.md` §6): safe area → header region → breathing gap → content region (the TypePack contentBox) → optional support region → optional footer, each with its own gap token; absent elements collapse with their gaps. The page header is the **H-C editorial stack**: optional muted eyebrow row → H1 (1–2 lines) with the computed `--focus` title-keyline at its left (canonical default — derived from the H1 line-box, design-kernel §6; the eyebrow square locator is the explicit alternative variant, never doubled with the keyline) → optional muted subtitle → generous breathing room. **No vertical accent rail** (rejected composition — do not regenerate it). For every panel header, compute `headerH = top padding + title line box + title/subtitle gap + subtitle line box + bottom padding`; place the divider only after that budget. Do not squeeze the title area until the copy happens to fit.943. **Grid arithmetic.** For each row of n cards inside a region: choose `cardW` and `gap` (gutter 24–32px), then **verify the last edge before drawing**: `start + (n−1)·(cardW+gap) + cardW ≤ region_right − padding`. Same check vertically for columns/stacks. If it doesn't fit: shrink `cardW`/`gap`, wrap to a second row, or widen the canvas — decide *now*, not after a render.954. **Text budget.** For each box, set lines × chars/line from the box width: ~28–36 Latin chars per line at body size, **Korean ≈ 60% of that**; 2–3 lines max per box. **Edit the copy to fit the budget before writing SVG** — abbreviate long tokens now. SVG has no auto-wrap; every line you plan here becomes one `<tspan>`.965. **Type scale — unified across the diagram** (never vary per box):97 - 1080-wide social: H1 46 / section 24 / card title 25 / body 19 / caption 1698 - 1400–1600 wide: H1 40–44 / section 22 / card title 20–22 / body 16–17 / caption 13–1499 - 680-wide docs: title 22 / box label 14 / caption 111006. **Icons and copy clusters.** Derive every icon-circle center from card geometry (e.g. `cy = card_y + card_h/2`). Compute the text cluster's visual bounds from its planned line boxes and center that cluster on the **same** `card_y + card_h/2`; never hand-tune either side per language. EN and KO variants must share the same formulas.1017. **Connector corridors.** Budget connectors like cards: `corridor = target_visual_left − source_visual_right`, and subtract the marker's real footprint (`markerUnits="strokeWidth"` multiplies it by the stroke width — formulas in `authoring.md` §3). If no readable shaft survives, choose a compact arrow, a transition glyph, or a reflow *now*, not after a render.102103## 3. Author the SVG — core rules104105Read `references/authoring.md` for the detailed rules and the reusable icon set. The render-critical core:106107- **Root:** `<svg xmlns viewBox="0 0 W H" width=W height=H role="img" style="font-family:...">` with `<title>`/`<desc>` — font identity (face, weight, asset, digest, license) is owned by the **typography SSoT** (`references/typography/typography-v1.yaml`), and **how the font travels** by the **font-delivery policy** (`references/delivery/font-delivery-v1.yaml`). The two axes stay separate, so adding fonts or output modes does not multiply the combinations.108 - **portable (default, acceptance-grade):** from flat's bundled Pretendard 400/700 faces, subset **only the glyphs that artifact actually used** and embed them as an `@font-face` data URI. The alias is the neutral name the policy fixes — Pretendard is a **Reserved Font Name**, so a subset (a Modified Version) may not carry it. The artifact renders with the same geometry even where the target environment lacks the font. Gallery and release evidence are produced in this mode only.109 - **system (lightweight, environment-dependent):** no embedding; relies on the installed font stack. Light and editable, but a diverging fallback changes wrapping and geometry, so it is **never used as acceptance evidence**.110 - **What an editable artifact requires:** an embedded subset serves viewing and render fidelity, not editability (edit the copy and any glyph outside the subset becomes tofu). To open and edit the SVG, or to edit it after importing into PowerPoint, **Pretendard must be installed in the target environment** — PowerPoint does not preserve fonts embedded in an SVG.111 - **The subset tool is a pinned build-only dependency:** `pyftsubset` from `fonttools 4.53.1` (plus `brotli`), with fixed options. When the tool is absent it **fails rather than sliding quietly** into a full embed or a system fallback. Viewing or verifying an existing artifact (lint, layout, render, audit) does not need it.112 - The sketch treatment embeds a **Hi Melody** glyph subset the same way (neutral alias, weight fixed at 400).113 - Verification: `skin.mjs typography-check` (the static cascade — the renderer runs it automatically as a hard gate when it detects sketch/typography annotation), `skin.mjs delivery` (policy, alias and RFN cross-check), and `scripts/font-probe.mjs` when needed (a runtime load/computed receipt — not proof of the rendered face). If flat KO shows tofu (□) on Linux in system mode, install Pretendard or `fonts-noto-cjk`.114- **Direct paint + role annotations** — colors encode role, not decoration, but the canonical SVG is authored in the PPT-oriented portable form from the start (`references/design-kernel.md` §5): every paint-bearing shape carries direct `fill`/`stroke` values plus `data-fill-role`/`data-stroke-role` annotations. Recolor by re-running the `skin.mjs` materializer against a profile, never by hand-editing hex. No `var(--…)`, `currentColor` or paint classes in canonical output:115116```xml117<!-- Paint values come from the resolver — get them with118 `node scripts/skin.mjs resolve current --mode light`. Never invent hex values:119 the palette SSoT is references/skins/ + registry (design-kernel.md §3).120 Roles: canvas/surface/surface-tint/ink/muted/rule/focus/positive/warning/121 danger/on-focus; aliases: edge/api/compute/data/external/icon. -->122<rect data-fill-role="canvas" fill="#F7F7F5" width="720" height="1020"/>123<rect data-fill-role="surface" data-stroke-role="edge-line"124 fill="#FFFFFF" stroke="#2E6DA4" rx="10"/>125<text data-fill-role="edge-ink" fill="#193C5A">…</text>126<path data-stroke-role="edge-line" stroke="#2E6DA4" fill="none" …/>127```128129- **Boxes:** rounded rect `rx="8"` (wide bands `rx="12–22"`), hairline border `stroke-width:1`. Each box = tinted fill + same-family border + same-family text (one semantic color family per box).130- **Vertical centering:** center text with `dominant-baseline="central"` and `y` at the box's vertical center. Two lines straddle the center: title at `center−11`, sub at `center+10`, both `central`. Never rely on the default alphabetic baseline for box labels — it sits high.131- **Composite scenes (intent-first selection):** one request may need MORE than one type — do **not** invent a new single type for it. Example prompt: "Use svg-infographic to summarise the key points as four cards along the top and show how the items relate as a tree below. Make it 4:5 for a social post." → plan this as a **summary cards (primary) + tree (supporting) composite scene**: the composition layer owns the slot split, gaps, hierarchy and reading order; each TypePack owns only its slot interior; both share one skin/typography/icon/connector contract (design-kernel §8). timeline+comparison, process+callout, architecture+decision matrix follow the same rule. Relationships default to numbered/labelled semantic bindings — draw connector lines only when the line itself carries meaning. Bounded: one primary + one or two supporting modules; if space runs out, reduce a supporting variant or split pages — never shrink text/arrows.132- **Layout contracts for repeated structures:** annotate panel headers and icon-text cards with the opt-in `data-layout-role` contract from `authoring.md` §4/§7; a card contract names its actual background rect `card-frame`, so frame, icon, and complete text cluster derive from one center. The legacy `page-title-header` rail contract applies **only to pre-kernel examples** — new headers follow the H-C cluster (design-kernel §6); the cluster lint contract replaces the rail contract in CP3. Keep unsupported transforms or unusual typography outside annotations and verify them manually.133- **Wrapping:** one planned line = one `<tspan x=.. dy=..>`; keep to the §2 text budget. Inside an opt-in layout contract, use plain numeric `y`/`dy` and non-nested `<tspan>` lines so the lint can accumulate the complete vertical line box.134- **Arrows:** define one `<marker>` arrowhead, use `marker-end`. Solid = sync/request, dashed (`stroke-dasharray="5 4"`) = async/batch/private. The default head is an **open-V stroked marker sized by visible geometry: `visible ≈ markerWidth × 8/12`, aim visible ≈3× the shaft → `markerWidth ≈ 4.5 × shaft`** (sizing table in `authoring.md` §3) — filled triangles only as a deliberate choice with the same visible-extent arithmetic. `markerUnits="userSpaceOnUse"` is **mandatory** on every referenced marker and the lint gate enforces it — the default `markerUnits="strokeWidth"` multiplies the head by the stroke width. Set `refX` so the tip lands on the path endpoint; leave an 8–12px gap between tip and target box **and** keep a visible shaft behind the head; pick each connector's form (standard / compact / curved / transition glyph / reflow) from the corridor budget, and prefer the gentle single-bend curve recipe when boxes sit at different heights (`authoring.md` §3).135- **On-focus text is light:** any label on a saturated fill carries `data-fill-role="on-focus"` with a direct light fill — never dark text on a mid/dark saturated fill, and never rely on a blanket `text{fill}` rule to sort it out.136- **Emphasis toolkit:** stroke + soft shadow + a number/status badge + a corner label + a filled icon badge. **No top accent bar on cards** (corner-smear and badge-collision failure modes — details and narrow exception in `authoring.md`).137- **Icon-first (default on):** a line icon in a soft tinted circle (`r≈34–38`, `data-fill-role="icon-tint"` + direct fill) per card/node, authored via the icon library (`<use>` as convenience), expanded to concrete role-annotated paths with direct paint in canonical output (`authoring.md` §7). Number badge **only when sequence or cross-reference matters** — never icon + redundant number.138139## 4. Pre-render checklist (source-level — run before every render)140141**Run the lint gate first for a verified handoff** — it machine-checks the deterministic subset of this list142(ids/references, root viewBox, marker units and footprint, opt-in header/card layout geometry, high-confidence Latin/CJK text overflow) with file/line,143measured values, and a suggested fix per finding. If Node 18+ is unavailable, follow the §0 approval/fallback144branch; the manual fallback does not count as a machine-linted handoff.145146```bash147node scripts/check-svg.mjs diagram.svg # render.sh runs this again as a hard gate148```149150Hard errors must be fixed before rendering (`render.sh` refuses at exit 5). Warnings are low-confidence estimates — disposition each one deliberately (fix it, or verify it in the §7 PNG pass); never treat a warning as a pass. Then walk the rest of the checklist — layout arithmetic and design judgement stay yours:1511521. **Containment re-check:** the §2 last-edge/bottom-edge arithmetic still holds for what you actually wrote (cards, arrows, badges, labels — including any element you added while authoring). Judge **visual bounds**, not just the fill rect — half the stroke width, shadow spread, and children drawn outside the base rect count; in a padded panel, an edge that merely touches the parent is a fail, not a pass (formula in `authoring.md` §1).1532. **Text budget:** no `<text>`/`<tspan>` line exceeds its planned chars/line; box labels use `dominant-baseline="central"` with computed `y`. **Pill/badge fit:** every pill/badge background covers its actual label width plus ≥ 14–16px padding per side, and EN/KO shared geometry fits the wider language's label (formula in `authoring.md` §2).1543. **References resolve:** every `<use href="#id">` matches a defined `<symbol id>`; every `marker-end` references a defined `<marker>`; no dangling `url(#…)`.1554. **Contrast roles:** every label on a saturated fill carries `data-fill-role="on-focus"` with a direct light fill; no blanket `text{fill}` rule that overrides on-focus labels via inheritance.1565. **Corner clearance:** badges / status labels / corner icons in the same card corner region have ≥ 20–24px between bounding boxes.1576. **EN/KO parity:** if generating both, the two variants share identical geometry formulas — only text (and text budget) differs.1587. **Root sanity:** `viewBox` matches the intended W×H; `<title>`/`<desc>` present; font stack on the root.1598. **Connector budget:** every connector's corridor and marker footprint are computed (§2.7); each standard arrow keeps a visible shaft (≥ 12–16px, not hidden under a card/panel by paint order) plus the 8–12px tip gap — a head-only arrow is a fail even when the gap is right; tight corridors carry a deliberate form choice (compact arrow, transition glyph, or reflow — `authoring.md` §3); EN/KO share connector geometry and semantics.160161## 5. Render to PNG (2×)162163The **canonical renderer is `render.mjs`** (Node 18+ stdlib): it runs the §4 source lint as a hard gate (exit 5 =164lint errors), discovers a Chromium-based browser (PATH plus the documented known paths), **discloses the exact165executable/version**, renders at 2×, and verifies the PNG IHDR dimensions exactly. It needs no shell beyond Node —166on Windows run it directly from CMD or PowerShell, verbatim, with no PowerShell scripting and no Git Bash. Use the167path of the skills root this package is actually installed under.168169Claude Code install (`.claude\skills`):170171```172node .claude\skills\svg-infographic\scripts\render.mjs out\diagram.svg173```174175Codex install (`.agents\skills`):176177```178node .agents\skills\svg-infographic\scripts\render.mjs out\diagram.svg179```180181On POSIX/Git-Bash environments the familiar wrapper delegates to the same core (exit 6 = Node 18+ missing —182return to the §0 approval/fallback branch rather than bypassing the gate):183184```bash185bash scripts/render.sh diagram.svg # → diagram.png (2×)186bash scripts/render.sh diagram.svg out.png --transparent187```188189`SVG_INFOGRAPHIC_BROWSER=<executable>` overrides discovery for owner-approved retries — it must still point at a190Chromium-based browser.191192Keep wrapper/intermediate files in the session scratchpad, not the repo. In native PowerShell/CMD the canonical path is still `node scripts/render.mjs` (above) — the manual fallback in `references/authoring.md` §8 exists only for the §0 no-Node branch. If no Chromium-based browser is available at all, deliver the SVG only and state the limitation.193194**The documented Chromium path is the canonical renderer.** Non-negotiables:195196- Record the exact browser executable/version (render.mjs prints a `renderer:` line — include it in the handoff) and the render command.197- On Windows the canonical `node render.mjs` path needs neither PowerShell scripting nor Git Bash and probes the four Program Files known paths itself; do not conclude "no browser" from a shell probe alone — run the canonical entrypoint, and only its exit-2 candidate report counts as a discovery failure.198- If the browser launch is denied by a sandbox boundary, render.mjs stops and prints the **exact same render command** — hand it to the owner for approved outside-sandbox execution; do not silently switch renderer.199- Never substitute ImageMagick, resvg, an app visualization helper, or any other renderer and label the result as a Chromium render; never post-process PNG pixels. If the canonical render remains unavailable, stop with the editable SVG and an explicit unverified-PNG/SVG-only limitation.200201## 6. Defaults to state (and let the user change)202203- Style: muted technical · light background · icons = soft circular bg + line icon. Opt-in alternative: **sketch preset** (tidy hand-drawn — paper, Korean handwriting font, rough strokes; see `references/sketch.md`) when the user asks for that feel204- Font: follow the typography profile SSoT — flat: Pretendard plus the normalized fallback (system); sketch: an embedded Hi Melody subset (weight 400). Never declare a stack outside the profile205- Changeable: brand color, ratio (docs vs 4:5 social), dark mode (a separate direct-paint artifact re-materialized from the profile — `diagram.dark.svg`; never a media query in one SVG), icon style, Korean/English, SVG-only vs SVG+PNG206- Optional attribution/footer layer: **off by default.** On request, add a small footer strip (source, author, or date) as its own bottom layer — a labeled footer, not a watermark laid over the content.207- Optional presentation surface: **off by default.** Only after the editable SVG + exact canonical 2× PNG pass,208 and only when the user explicitly asks for presentation projection, read209 `references/presentation/contract.md` and create a separately named projection PNG + receipt. Never describe210 it as the canonical PNG or use it as acceptance evidence for the source artifact. Use only the surface211 manifest's registered blend profile; never improvise opacity, filters, or per-run color tuning. If projection212 is requested without a named surface, use the bundled `paper-notebook`; an explicit bundled or user manifest213 always overrides it.214215## 7. Verify the PNG (quality bar)216217The pre-render checklist covered the source; now check what only the pixels show:218219- **Rendering:** no text overflow or clipped glyphs; text vertically centered in its box; correct Korean/CJK rendering (no tofu); PNG is exactly 2× the viewBox (render.sh reports this); icons visible (no blank circles); labels on accent fills read clearly (AA-like separation).220- **Containment (visual):** every child sits inside its container plus inner padding — scan the PNG for anything touching or crossing a panel edge; an element can spill without touching any text. For canonical output, annotate containers/groups and run `scripts/check-layout.mjs` (design-kernel §7) so padding, inset symmetry, and repeated-gap uniformity are machine-verified from the layout receipt.221- **Connectors:** every arrow reads as shaft + head — no head-only arrows and no head buried under a panel or card; the head joins its shaft cleanly and neither touches the target border nor floats detached; **head/shaft proportion reads natural — a *visible* head ≈4× its shaft width or more is a fail for newly authored diagrams, and a visible head below ≈2.5× reads weak** (aim ≈3×; the lint computes the visible ratio from the marker's glyph extent — `authoring.md` §3). Pre-contract legacy examples keep their approved visuals under an explicit `data-lint-allow="marker-footprint"` exception — never add that attribute to silence a new diagram. A curve keeps one readable bend (an accidental S-shape is a fail); a transition glyph reads as flow, not as an icon or play button.222- **Two-pass inspection (required):** first view the PNG at **fit-to-page** scale — every major stage connector must be immediately recognizable as an arrow (not a hairline or a tiny decorative glyph), and the reading order must survive the zoom-out; then inspect **close-up** for connector/text detail. A lint warning that maps to a hard visual rule (head proportion, containment) is never accepted just because the artifact is otherwise attractive.223- **Explicit fail examples** (each has shipped as a defect at least once): an edge label crossed by its own path or ambiguous about which route it labels; icon/text overlap or a feedback loop crowded against cards; a major-flow connector that disappears at fit-to-page scale; a rendered EN/KO pair whose CJK glyphs break or whose connectors differ between languages.224- **Message:** the archetype fits the content; one clear reading order; the title states a conclusion, not just a topic; text density stays low per box; any matrix/labels read unambiguously; depth and language fit the stated audience.225- The SVG stays editable — direct per-shape paint with `data-fill-role`/`data-stroke-role` annotations (recolor via the `skin.mjs` materializer), no flattened or rasterized text. **The source SVG is authoritative: never patch or redraw a connector only in the PNG.**226227If a check fails: fix the SVG source, re-run §4, re-render. Track which §4 item would have caught it — if none, the checklist is missing a rule.228229## 8. Output & handoff230231Save both the **SVG** (editable source of truth) and the **PNG** (2× export for slides/docs/social). For the **flat** treatment the renderer uses locally installed fonts (KO/CJK falls back to the platform default — Apple SD Gothic Neo on macOS, Malgun Gothic on Windows, Noto Sans KR on Linux); **sketch** artifacts embed their handwriting subset, so they render identically everywhere. Verify no tofu in the PNG either way.