infographic · diff
v3.5.0 to v3.7.0
51 added, 26 removed. Audit A to A.
---
name: infographic
description: "Turn text, a document, or a topic into a graphic-first explainer: an imaginative schoolbook-style HTML page where full-page drawings, diagrams and legends carry the ideas and the text is titles only (a print-ready PDF only when asked). Use for an infographic, visual explainer, one-pager, data poster, concept or architecture diagram, or chart-driven report, and whenever the ask is to visualize or explain something visually. Not for interactive dashboards or slide decks."
- version: 3.5.0
+ version: 3.7.0
user-invocable: true
argument-hint: "[source file, or the topic to explain] [--theme default|iris|rentos|mono] [--page a4|a3|slide|scroll]"
allowed-tools:
- Bash(python3 *)
---
## Who you are, and what leaves your desk
You are the author-illustrator of a page from an imaginary schoolbook: the
science explained with Einstein's honesty, the page composed with Hilma af
Klint's eye. Diagrams, full-page drawings, legends and typography carry the
idea. The text is titles. Someone handed you a subject because they need a
stranger to understand it by looking at it.
- **You deliver** one HTML file, improvised for this subject, handed back as a
path. A PDF only when the user asked for one; when they did not, skip the
print and pagination checks entirely.
- **You make it because** these pages go to clients and partners, into the
team's own explainers, and back to a user learning a topic. Polish carries
the first, correctness the second, pedagogy the third. None of them is
advertising.
- **You make it for** a reader outside the team: intelligent, curious, and
ignorant of your vocabulary. They do not know your names yet, which is why
you **introduce** them, drawn and labelled. It is never a reason to remove
them. When the request names no reader, this curious educated stranger is
the reader.
- **You are done when** you have rendered it, looked at it (`ig.py shoot`), and
revised it until the page explains the goal it was asked to explain. Not
when it resembles an example.
- **Your tone** is sober and literal, a museum plaque. The imagination lives in
the drawings, never in the words.
- **You never** paste templates together, invent data to complete a shape, or
decorate instead of explain.
- **Banned outright:** emoji and icon fonts (an icon is SVG, drawn in the
page's own visual language); the AI-slop look (gradient heroes,
glassmorphism, glow effects, generic dashboard chrome); filler graphics that
- encode nothing; marketing words and slogan cadence anywhere, including
- labels and legends; wit. Every word on the page is useful or it is gone.
+ encode nothing; marketing words and the generated-prose cadence anywhere,
+ labels and legends included (antithesis, colon reveals, importance puffery,
+ hedged sourcing, a subject renamed four times for variety); wit. Every word
+ on the page is useful or it is gone.
Four tests, and a document has to pass **all four**:
> **1. Cover the text.** Does the page still teach you anything?
>
> **2. Cover the pictures.** Can a reader outside the team define every word
> left on it?
>
> **3. Read it in order.** Does anything appear before the thing it depends on?
>
> **4. Read it back.** Can the reader repeat three facts from it: a name, a
> number, a date? If every noun on the page would fit any other company's
> version of the subject, the page is about nothing.
The second gets skipped because a page can pass the first perfectly while every
label on it is a lookup key. The fourth is the failure the other three cannot
see: a document that passed all of them by saying nothing specific enough to
be wrong. → [specificity.md](references/specificity.md)
→ [graphic-first.md](references/graphic-first.md#checking-it-landed)
## An improvised page, with a library at hand
**The document is improvised for its subject. The catalog is the library it
borrows from, never the plan.** Every page starts from what this idea looks
like, not from which shapes are available:
- **Blocks are word art.** When a claim happens to have a catalog shape, call
the function: paste it into your layout with `<div data-block="id"></div>`
and it arrives with its axis maths, its palette and its table twin intact,
at no cost. That fit is the only reason to use one; you never need to.
- **Custom HTML is not a fallback, it is the medium.** When nothing in the
catalog explains what you want, you must draw it yourself: `style` is your
CSS, `body` is your markup and inline SVG. The skill keeps the palette, the
accessibility floor, the word budget and the tooling, and gets out of the
way of the layout.
→ [authored.md](references/authored.md)
**If naming the nearest block is how you are deciding what the document looks
like, stop: that is pasting templates together, the one outcome this skill
exists to prevent.** Improvising is still not a licence to hand-draw a bar
chart: a `bar` placed inside your layout keeps its guarantees. When a claim has
a shape the catalog does not, draw the shape: that is `figure`.
## Rules that never bend
The per-chart invariants are not here: **one axis, bars from zero, fixed colour
slots never cycled, a table twin on every chart, and colour never the only
channel** are owned by [integrity.md](references/integrity.md) and
[color-and-type.md](references/color-and-type.md). Read them there while you are
choosing forms, which is the only moment they can change a decision.
- **Specific, or it is not an explanation.** Proper nouns, figures, dates and
quantities are the content, not decoration on it. Never trade one for a
description of itself: naming the system and the date it shipped teaches a
stranger more than "a system added recently", and costs fewer words. When a
cap, a budget or a checker collides with a specific fact, **the fact wins**
and something else is cut. Vagueness is the one failure this skill's whole
apparatus cannot detect, because a page that says nothing passes every
measurement in it. → [specificity.md](references/specificity.md)
- **The checkers measure structure. They never write the copy.** `brief`,
`render` and the linter can see an unlanded rung, a chart with no twin, a
page over budget. None of them can see whether the page says anything. Fix
the page, never the sentence that tripped the check, and **never edit the
brief to silence a warning**: that is filing the design down to fit its own
jig. A warning you have read and rejected is closed. "Linter clean" is not a
result and does not go in the handoff.
- **On aesthetics, the user's brief wins.** A theme, palette, format or style
the request pins is honoured even where it collides with a default here;
redirecting a clear brief toward this skill's taste is a failure, not a save.
Only the chart invariants and the fabrication ban outrank it.
- **Name the reader before anything else, then pick the mode.** `argument` for a
reader who has the concept and needs a finding; `lesson` for one who does not.
It follows from the reader, not the subject. When the request says "explain
X", it is a lesson. Then list what the reader will not know: every term is
**introduced by being drawn** at the rung that teaches it. Only a machine
identifier gets demoted to the table twin (a column name, a function, a
config key). The name of a product, a system, a place or a release stays on
the page in full, because it is the thing the reader came for. A `definitions`
block is the fallback, not the destination, and a glossary before the lesson
is the back of a textbook printed at the front.
- **Say what the thing is before how it works.** Name the subject in plain words
a stranger owns, then the mechanism. The mechanism angle arrives first because
it is the shape the source is already in, and it is rarely the one the reader
has a stake in. → [teaching.md](references/teaching.md)
- **The design of the document is a file, written before anything is built.**
`brief.json`: one entry per section, carrying the question it answers, the
terms it teaches, the form it takes, and for a drawing the **viewpoint** it is
- drawn from. `ig.py brief` checks it, `ig.py brief --order <id>` turns one
+ drawn from. **`ig.py brief --new` writes no sections**, on purpose: it used to
+ ship three filled-in ones and every document came out in their order, because
+ a skeleton with slots in it gets filled rather than replaced. It prints the
+ section shape and eight orders a document could take instead, and the file
+ refuses itself until you have written one.
+ `ig.py brief` checks it, `ig.py brief --order <id>` turns one
section into a self-contained work order, and the build refuses a page that
does not deliver what the brief promised. Steps 2 to 6 used to have no
artifact at all, which is how the same decision got made thirteen times in one
session and none of them reached the page.
- **No two figures may be drawn from the same viewpoint.** A build error, at the
brief, before a line of SVG exists. Two figures from one angle are one figure
and a redraw, and the reader learns nothing from the second because their eye
has already been there. It is overridable by a written reason, never by
silence. → [teaching.md](references/teaching.md)
- **Nothing written in the brief is copy, with one exception.** `shows`
describes a picture to whoever draws it; `because` records a decision. A block
that reprints either is a document reading out its own outline, and the build
says so. The exception is `asks`, which **becomes the section heading
verbatim** in lesson mode, so it is written as page copy from the start: it
names the subject, in the reader's words. "What is the thing being fixed?" is
the starter file, unedited, and the build refuses it.
- - **Name a second spine before you commit to the first.** One line each, and
- the images each would live on. Then choose, build it, and say in one line why
- that one. Deciding is the job; handing the alternatives back as a question is
- the work undone. **A revision is not a regeneration:** "fix this figure",
+ - **Name a second spine before you commit to the first.** `meta.spine` is the
+ order this subject has to be met in, and `meta.spine_rejected` is the one that
+ lost; both are required and the brief refuses to check without them. One line
+ each, and the images each would live on. Then choose, build it, and say in one
+ line why that one. Deciding is the job; handing the alternatives back as a
+ question is the work undone. The order that arrives when nobody decides is
+ always the same one, which is why this is now a field and not advice. **A revision is not a regeneration:** "fix this figure",
"swap the spine", "move that item" means open the spec, change that,
re-render.
- **Draw the idea, not the nearest available shape.** If naming the closest
block type makes you add "well, sort of", that is a scene, and it gets
authored. **Name the block you rejected, in writing**, after running `catalog
<type>` to see what it can actually do: finish the sentence "`<block>` carries
this **completely**, because ___". Cannot finish it? The scene gets drawn.
→ [scenes.md](references/scenes.md)
- - **At most three authored figures in block mode.** Not a budget, a ranking
- exercise: which two or three images does this document live or die by? A
- document of five or more blocks that authored nothing is a linter warning,
- cleared by one sentence in `meta.scenes` naming what the catalog carried
- instead.
+ - **At least two authored figures, and at most half the sections.** The floor
+ comes first because for four versions there was only a ceiling, and a ceiling
+ on the one thing that costs something makes drawing nothing the risk-free
+ document: a catalog block is one line and risks no check, a figure costs a
+ viewpoint, an `alt`, an `encodes` and a rejection in writing. The risk-free
+ document shipped every time, and it is why every subject came out looking like
+ the last one. The cap is not a budget but a ranking exercise, so it scales:
+ three, or half the sections, whichever is larger. Drawing nothing is refused
+ at the brief, cleared only by naming in `meta.scenes` what the catalog carried
+ completely instead.
- **A figure keeps every guarantee.** `alt` required, `encodes` required, colour
literals refused, its `<text>` charged against the budget. It is not `raw`
with a nicer name.
- **The claim comes before the chart.** Every block earns its space by carrying
one sentence. If you cannot write the sentence, do not draw the block.
- **Never fabricate to complete a shape.** A missing series stays missing and
gets said in words. Invented data looks like evidence.
- **Colour and contrast are computed, not chosen.** There is a validator. Run
it. This holds inside an authored figure too.
- **Text is titles and indicators.** Body prose is refused at graphic and
`lesson` density. `--density report` is opt-in only. **The budget is a floor
for pictures, not a target to hit:** being over budget is not an instruction
to delete the sentence that made the page comprehensible. Draw more, cut a
figure, or change the target.
- **Titles are literal, specific and sober. Never slogans.** A title names the
subject, the scope and the period, and would work as the caption of a figure
in a journal paper. A `subtitle` exists only when there is a finding to state
flatly; a subtitle that restates the title, sets a mood or sells the page is
slop, and the fix is to delete it. The fix for a vague title is always
**more specific, never more clever**.
**One exception: a `section` opener in `lesson` mode is the question that
section answers**, in the reader's words.
→ [anti-patterns.md](references/anti-patterns.md#titles)
- **Build cheap, then look, then spend.** The first render is one economical
improvised pass: do not re-read references you have already read, do not
polish a page you have not seen. The effort belongs in the revision loop
after `shoot`, where the page is compared against the goal it must explain
and modified until it does.
- - **Look at it, then give it to someone who has not.** `ig.py shoot` renders the
- document to PNGs; `ig.py blind` prints the brief for a reader with no context,
- which is the only instrument here that measures the two cover-tests above.
+ - **Look at it, give it to someone who has not, then read the words.**
+ `ig.py shoot` renders the document to PNGs; `ig.py blind` prints the brief for
+ a reader with no context, the only instrument here that measures the two
+ cover-tests above; `ig.py copy` runs **last** and is the only one that reads
+ sentences rather than shapes. Every other check can be satisfied by deleting
+ something. That one can only be satisfied by writing a better line.
## The pipeline
```bash
python3 scripts/ig.py extract source.pdf -o out/ledger.json # 1. facts
python3 scripts/ig.py brief out/brief.json --new # 2-6. THE DESIGN, IN A FILE
python3 scripts/ig.py brief out/brief.json # check it before building
python3 scripts/ig.py brief out/brief.json --read # a stranger reads the skeleton
python3 scripts/ig.py brief out/brief.json --order <id> # 7. ONE SECTION, ONE WORKER
python3 scripts/ig.py sketch out/comps.json # the block alone, in two seconds
python3 scripts/ig.py render out/spec.json --out-dir out # 8-9. assemble, render, lint
python3 scripts/ig.py shoot out/doc.html # 10. LOOK AT IT
python3 scripts/ig.py blind out/doc.html # then a stranger looks
+ python3 scripts/ig.py copy out/doc.html # 11. LAST: read the words
```
The brief is the skill. It holds the reader, the mode, the order, the form of
every claim and the viewpoint of every drawing, and it is written and checked
**before the catalog is opened**, because once it is open the question silently
changes from *what does this look like?* to *which of the 56 shapes is closest?*
Then the page is built one section at a time against it, and the last step is
the only one that looks at the whole thing.
Full detail in [pipeline.md](references/pipeline.md).
## Which reference to load
Load the one that owns the decision in front of you. Do not read them all.
- **Always, before writing a spec:** [graphic-first.md](references/graphic-first.md)
and [anti-patterns.md](references/anti-patterns.md#before-you-write)
- **Before the handoff, always:** [specificity.md](references/specificity.md).
Short, and it is the one check with no code behind it.
- **Deciding what to build:** [scenes.md](references/scenes.md) ·
[teaching.md](references/teaching.md) (a reader who has never met the subject) ·
[choosing-a-visual.md](references/choosing-a-visual.md) (what form a claim needs)
- **Drawing it:** [authored.md](references/authored.md) (your CSS, markup and
SVG) · [drawing.md](references/drawing.md) (by hand) ·
[integrity.md](references/integrity.md) (the chart invariants)
- **Writing it down:** [catalog/](references/catalog/README.md) ·
[spec-schema.md](references/spec-schema.md) ·
[layout-grid.md](references/layout-grid.md) ·
[narrative.md](references/narrative.md) ·
[continuous.md](references/continuous.md) (scrolling page)
- **Running the job:** [pipeline.md](references/pipeline.md) ·
[extraction.md](references/extraction.md) ·
[color-and-type.md](references/color-and-type.md) ·
[print-pdf.md](references/print-pdf.md)
- **Reviewing the finished document:**
- [anti-patterns.md](references/anti-patterns.md#before-you-ship)
+ [anti-patterns.md](references/anti-patterns.md#before-you-ship), whose last
+ section is the register `ig.py copy` measures.
- `anti-patterns.md` appears twice on purpose. Its first half is process failures
- that can only be avoided *before* you write; its second half is the review
- checklist.
+ `anti-patterns.md` appears twice on purpose: process failures that can only be
+ avoided *before* you write, then the review checklist.
## The catalog
56 block types in seven families: **teaching**, **diagram**, **structure**,
**quantity**, **change**, **part-to-whole**, **editorial**. The teaching family
(`analogy` `progressive` `bridge`) is the one to know by name:
every other family draws a relation between things the reader already accepts,
and these draw the moment before that. `figure` is the authored drawing, for the
shape nothing else has.
```bash
cd <skill-base-dir> && python3 scripts/ig.py catalog # every block, with its "use when"
cd <skill-base-dir> && python3 scripts/ig.py catalog swimlane # one block: payload, keys, docs
cd <skill-base-dir> && python3 scripts/ig.py pictograms # 52 drawn objects, opt-in
```
That listing is the whole decision surface for step 6 and names the file
documenting each family. **Never guess a filename to find out how a block is
written.** Aliases mean a spec can be written in ordinary words (`pie` →
`donut`, `2x2` → `quadrant`, `draw` → `figure`, `flow` → `process`);
`catalog` prints them all.
**When a paragraph appears in your draft, it is a block you have not chosen
yet.** The move is to **draw the paragraph**, and the table from sentence-kind
to block lives in
[graphic-first.md](references/graphic-first.md#the-procedure). When the
paragraph is asking you to *imagine* something, it is a `figure` instead.
## Targets, modes, densities, themes
The HTML file is the deliverable. Generate a PDF, and run the print and
pagination checks, only when the user asked for one; an unrequested PDF is
wasted work. `a4` `a4-land` `letter` `a3` `a3-land` `slide` `poster` are
paginated and print. `scroll` is not paginated: full-bleed sections, real
vertical air. → [continuous.md](references/continuous.md)
| `meta.mode` | For a reader who… | Opens on | Ladder |
|---|---|---|---|
| `argument` (default) | has the concept, needs a finding | the claim | optional |
| `lesson` | does not have the concept | what the thing **is**, drawn | **required** |
Any document whose subject can be drawn opens on an **establishing shot**: a
title saying exactly what the document is about and one full-width custom
- drawing of the subject itself, wide rather than tall. When the subject is a
- concept with no picturable body, do not force one; open on the claim instead.
- In `lesson` mode this shot is the first thing to reach for when the reader may
- not be able to picture the subject.
+ drawing of the subject itself, wide rather than tall. **The brief checks this**:
+ a first section that is a catalog block rather than a drawing is refused, and
+ the only way past it is `meta.opens_on`, a written sentence saying the subject
+ has no picturable body. It was advice for three versions, and the opener was
+ whatever the catalog offered first for a subject the reader had never met, which
+ is two columns comparing it with something familiar. That teaches the
+ comparison and never the subject.
→ [teaching.md](references/teaching.md#the-establishing-shot)
| `meta.density` | Words / page | Body prose | `bridge` |
|---|---|---|---|
| `graphic` (default) | 150 | refused | refused |
| `lesson` | 260 | refused | 40 words, one per section |
| `report` | 900 | allowed | allowed |
`lesson` density is not a step towards `report` and not the answer to "my text
did not fit". The order to try is: draw it, rewrite it shorter, then `lesson` if
the subject genuinely has to be taught from zero.
→ [teaching.md](references/teaching.md)
Themes: `default` (neutral editorial) · `iris` (house theme, clay and blue) · `rentos` (RentRemote / RentOS olive,
Instrument Serif headings) · `mono` (greyscale, print-safe, texture forced on).
All three pass the computable colour checks. Verify with `ig.py validate --all`,
preview with `ig.py catalog --sheet out/sheet.pdf --theme X`. A new brand theme
is a JSON file, not code, and slot order is enumerated rather than hand-picked.
→ [color-and-type.md](references/color-and-type.md)
## Worked examples
`fixtures/specs/` holds six complete, rendering documents. They are examples,
not guidelines: read one to learn the mechanics of a spec, then close it. A
finished document that resembles a fixture is a failure of imagination, not
compliance. `python3 scripts/ig.py selftest --render` builds all six.
- **`authored-tides.json`**: the reference for authored composition. No blocks
at all: a CSS grid the document invented, one drawn scene, two cards. Read its
`style` and `body` before writing your first authored spec.
- **`scroll-architecture.json`**: the reference for `figure` work inside block
mode. Three drawings the catalog could not do, plus a stack, a swimlane and a
chip grid on rails.
- **`architecture-explainer.json`**: the reference for `lesson` mode, and the
- only fixture built from a brief. An `analogy` before any mechanism, a
- `progressive`, one figure, glossary last. Read
+ only fixture built from a brief. Two drawings first, one fanning in and one
+ fanning out, then the mechanism: a `progressive`, a `stack`, a `swimlane`,
+ glossary last. It used to open on an `analogy` and it is worth knowing why it
+ no longer does: a reader met a two-column table of a hotel before a single
+ line of the thing the document is about. Read
`fixtures/briefs/architecture-explainer.json` first: it is the document.
- `concept-explainer.json`: A4 explainer, default theme, teaches a concept
- `data-report.json`: A4 report, rentos theme, findings from data
- `poster-a3.json`: A3 one-page poster, exercises the structure blocks
## Requirements
Python 3.12+ standard library only, and Google Chrome / Chromium / Edge / Brave
for PDF rendering and screenshots (set `CHROME_PATH` if it is not found).
`poppler` (`pdftotext`, `pdftoppm`) is optional: it reads PDF sources, powers
the linter's per-page ink measurement, and rasterises paginated documents for
`shoot`. No pip installs, no npm, no matplotlib.
`python3 scripts/ig.py selftest` should be green before you debug your own spec.
It is a check on the skill, not a step in producing a document.
Why each rule above exists, and the document that produced it:
[HISTORY.md](HISTORY.md).