import prism
spec = """
type: flow
steps:
- label: Draft
- label: Review
- label: Ship
"""
prism.diagram(spec)Tutorial
From nothing to a themed diagram, one step at a time
This page is meant to be followed along with. Every diagram below is rendered by prism while this page is built — nothing here is a screenshot, and if any step stopped working the site would fail to build.
Step 0 — install
pip install prism-svgThe import name is prism; the distribution is prism-svg because prism was already taken on PyPI. Check it worked:
prism archetypesYou should see ten names. If you have uv, uvx --from prism-svg prism archetypes works without installing anything.
Step 1 — your first diagram
A prism spec is YAML. It needs exactly one thing: a type, naming the archetype. Here is the smallest useful diagram there is.
That is the whole loop: write YAML, get SVG. You never positioned anything — prism chose the box sizes, the spacing, the arrow geometry and the colours.
Save that YAML to first.yaml and you can do the same from a terminal:
prism render first.yaml -o first.svgStep 2 — give the nodes some substance
Every archetype in prism consumes the same node shape. Learn it once and it works in all ten. The useful fields are sublabel, icon and accent.
spec = """
type: flow
steps:
- label: Draft
sublabel: outline + notes
icon: pencil
accent: 0
- label: Review
sublabel: two readers
icon: eye
accent: 1
- label: Ship
sublabel: to production
icon: rocket
accent: 2
"""
prism.diagram(spec)accent is an index into the theme’s colour ramp, not a colour. That indirection is the point: the theme owns the palette, so the same spec restyles cleanly. There are 1756 icons available (prism icons), all from Lucide.
Step 3 — title, subtitle, caption
These live at the top level of the spec, alongside type, because they belong to the diagram rather than to any node.
spec = """
type: flow
title: Editorial process
subtitle: for long-form pieces
caption: "Source: the house style guide"
steps:
- {label: Draft, icon: pencil, accent: 0}
- {label: Review, icon: eye, accent: 1}
- {label: Ship, icon: rocket, accent: 2}
"""
prism.diagram(spec)That inline {label: Draft, icon: pencil} form is ordinary YAML — use whichever style you find more readable.
Step 4 — change the direction
Most archetypes take a small number of structural options. flow takes direction.
spec = """
type: flow
direction: down
steps:
- {label: Draft, icon: pencil, accent: 0}
- {label: Review, icon: eye, accent: 1}
- {label: Ship, icon: rocket, accent: 2}
"""
prism.diagram(spec)Step 5 — pick the archetype by the shape of the meaning
This is the one judgement prism asks of you. Not what is this about, but what shape is the idea. The same three-stage editorial process, told as a loop rather than a line, wants cycle:
spec = """
type: cycle
title: Editorial loop
center: {label: A piece}
steps:
- {label: Draft, icon: pencil, accent: 0}
- {label: Review, icon: eye, accent: 1}
- {label: Revise, icon: wrench, accent: 2}
"""
prism.diagram(spec)And the same content as a judgement about two independent dimensions wants quadrant, where items carry coordinates in 0..1:
spec = """
type: quadrant
title: What to write next
axes:
x: {label: Effort, low: Low, high: High}
y: {label: Reach, low: Narrow, high: Wide}
quadrant_labels: [Quick posts, Flagship, Notes, Slog]
items:
- {label: Weekly note, x: 0.15, y: 0.3}
- {label: Field guide, x: 0.8, y: 0.85}
- {label: Talk writeup, x: 0.45, y: 0.6}
"""
prism.diagram(spec)The catalog shows all ten with the spec that produced each.
Step 6 — hierarchies, the one recursive case
hierarchy is the only archetype whose data nests, through children. That is data recursion, not template composition — you still cannot put one archetype inside another.
spec = """
type: hierarchy
title: A book
root:
label: Manuscript
icon: book
children:
- label: Part I
children:
- {label: Chapter 1}
- {label: Chapter 2}
- label: Part II
children:
- {label: Chapter 3}
"""
prism.diagram(spec)Step 7 — switch the theme
Add one line. Nothing else about the spec changes.
spec = """
type: flow
theme: dark
title: Editorial process
steps:
- {label: Draft, icon: pencil, accent: 0}
- {label: Review, icon: eye, accent: 1}
- {label: Ship, icon: rocket, accent: 2}
"""
prism.diagram(spec)Four themes ship: default, dark, paper and mono. paper is the one to reach for in print — serif typography, warm surface, hairline strokes:
prism.diagram(spec.replace("theme: dark", "theme: paper"))Step 8 — nudge a single token
You rarely need a whole new theme to change one thing. tokens: overrides individual theme values by dotted path.
spec = """
type: flow
theme: paper
tokens:
geometry.radius: 0
geometry.gap: 44
title: Wider gaps, square corners
steps:
- {label: Draft, icon: pencil, accent: 0}
- {label: Review, icon: eye, accent: 1}
- {label: Ship, icon: rocket, accent: 2}
"""
prism.diagram(spec)A mistyped path fails loudly rather than being ignored — try geometry.radiuss and prism will tell you what it expected.
Step 9 — your own theme
For a whole document set you want a theme file of your own. Scaffold one:
prism new-theme houseThat writes house.yaml here, copied from the default theme and already carrying its new name. Start from whichever bundled theme is closest:
prism new-theme house --from paper # start from the print theme
prism new-theme house -o themes/house.yamlThe file opens with a comment explaining the two rules you have to respect, so you do not have to remember them. Then point any spec at it:
type: flow
theme: ./house.yaml
steps:
- {label: Draft}Edit the tokens and every diagram that references the file restyles at once.
The two rules, since they surprise people: typography.family must be grotesque, serif or mono, and typography.weight values must be 400 or 700. Those are exactly the fonts and weights whose metrics prism has vendored. Anything else would mean measuring text against a font your reader does not have, and labels would overflow their boxes. prism new-theme validates the file it writes, so a bad edit fails when you next render rather than producing a quietly broken picture.
Step 10 — when something is wrong
prism’s errors are written to be acted on. Each names the bad value and points at the valid neighbours.
prism.render_str("""
type: flow
steps:
- {label: Draft, icon: pencl}
""")--------------------------------------------------------------------------- UnknownIcon Traceback (most recent call last) Cell In[11], line 1 ----> 1 prism.render_str(""" 2 type: flow 3 steps: 4 - {label: Draft, icon: pencl} File ~/work/prism/prism/prism/__init__.py:89, in render_str(source) 87 saved, _TGroup.stack = _TGroup.stack, [] 88 try: ---> 89 canvas = frame(archetype.build(spec, ctx), envelope, ctx) 90 svg = canvas._repr_svg_() 91 finally: File ~/work/prism/prism/prism/archetypes/flow/build.py:25, in FlowArchetype.build(self, spec, ctx) 23 def build(self, spec: FlowSpec, ctx: RenderContext) -> Group: 24 theme = ctx.theme ---> 25 boxes = [build_node_box(step, ctx) for step in spec.steps] 27 if spec.direction == "right": 28 spine = RowLayout(boxes, align="middle", gap=theme.geometry.gap) File ~/work/prism/prism/prism/nodebox.py:109, in build_node_box(node, ctx, max_width) 106 body: Group = ColumnLayout(stack, align="middle", gap=theme.geometry.gap / 6) 108 if node.icon: --> 109 glyph = build_icon( 110 node.icon, 111 size=theme.size("label") * 1.4, 112 color=ink, 113 stroke=theme.geometry.stroke, 114 ) 115 body = RowLayout([glyph, body], align="middle", gap=theme.geometry.pad / 2) 117 badge = build_badge(node, ctx) File ~/work/prism/prism/prism/icons.py:68, in build_icon(name, size, color, stroke) 66 def build_icon(name: str, size: float, color: Color, stroke: float) -> IconShape: 67 """Return the named icon as a shape, scaled to `size` and centred on origin.""" ---> 68 return IconShape(name, size, color, stroke) File ~/work/prism/prism/prism/icons.py:44, in IconShape.__init__(self, name, size, color, stroke) 42 data = _icons() 43 if name not in data: ---> 44 raise UnknownIcon(name, data) 45 super().__init__(fill=Colors.Transparent, stroke=color, width=stroke) 46 self.name = name UnknownIcon: unknown icon 'pencl' — did you mean: pencil, pen, pencil-off?
The same holds for unknown archetypes, unknown theme tokens, and dangling references such as a matrix cell naming a row that does not exist.
Step 11 — put it in your own Quarto document
Everything above is a real Quarto page. The pattern is:
```{python}
#| label: fig-process
#| fig-cap: "The editorial process"
#| echo: false
import prism
prism.diagram("figures/process.yaml")
```prism.diagram() accepts a path or a spec string and returns an object that knows how to display itself, so Quarto embeds the SVG directly — no intermediate file and no rasterisation. See In Quarto for PDF output and batch rendering.
Where to go next
- The catalog — all ten archetypes and their fields.
- Themes — every archetype in every theme, side by side.
- For agents — if you are wiring prism up to a language model.
And when you want a field prism does not have, prism schema <archetype> is the authoritative answer on what a given archetype accepts.