| name | gribouille |
| description | Use when authoring, editing, or debugging a Gribouille grammar-of-graphics plot in a Typst document: writing a #plot() call with aes() mappings and geom-*, stat-*, or scale-* layers, adding coords, facets, guides, or themes, or turning data into a scatter, line, bar, boxplot, histogram, density, violin, ridgeline, 2D-density, beeswarm, smooth, or contour figure with the @preview/gribouille package. |
Authoring gribouille plots
Gribouille is a grammar-of-graphics plotting library for Typst.
The API mirrors ggplot2 (R) and plotnine (Python).
A plot layers geom-* over shared data and an aesthetic mapping.
Import
#import "@preview/gribouille:0.7.0": *
Pin the version to the installed one.
Check it via llms.txt or Typst Universe.
Mental model
Pipeline, forward only:
data → stat → position → scale → coord → facet → theme → render
data: a dictionary of column arrays, or a built-in dataset (penguins, mpg, economics).
mapping: aes(...): column names to aesthetics (x, y, colour, fill, shape, size, alpha, linetype, label, …).
Gribouille rejects unknown channels, including the US spelling color.
layers: a tuple of geom-*.
Each geom can set its own stat, position, mapping, and key (legend glyph).
Stat-backed geoms take stat: auto to build their default stat from the geom parameters.
scales: how values map to axes and palettes.
See the scales section below.
coord, facet, labels, guides, theme: everything else.
- Late binding:
after-stat(...), after-scale(...), from-theme(...), stage(...).
Call shape
#plot(
data: <dict-of-columns or dataset>,
mapping: aes(x: "...", y: "...", colour: "...", ...),
layers: (
geom-point(...),
geom-smooth(method: "lm", se: true),
// more layers ...
),
scales: scales(
x: scale-continuous(),
colour: scale-discrete(...),
// more scales ...
),
coord: coord-cartesian(), // optional
facet: facet-wrap("group"), // optional
labels: labels(title: "...", x: "...", y: "..."),
guides: guides(...), // optional; guides(default: none) hides all legends
theme: theme-minimal(),
alt: "...", // optional accessibility text
width: 12cm, // default 10cm
height: 9cm, // default 7cm
)
as-spec: true returns the raw spec instead of rendering, for compose() composition.
Scales
Scale constructors are aesthetic-agnostic.
The aesthetic comes from the scales() key:
scales(colour: scale-viridis-d()), not scale-colour-viridis-d().
scales(x: scale-log10()), not scale-x-log10().
plot(scales:) accepts only the scales() dictionary.
There is no positional array.
The same constructor adapts to its key (axis for x/y, palette for colour/fill, output range for size/alpha/…).
Unknown constructor arguments, unknown aesthetics, and positional arguments fail at binding.
The error lists the valid keys.
Symbol inventory
What exists, by family (regenerate with lua tools/skill-inventory.lua).
Arguments, defaults, and types still come from each symbol's .llms.md reference page.
- Core:
plot, compose, defer, aes, as-factor, as-numeric, typst, after-scale, after-stat, from-theme, stage.
- Datasets:
economics, mpg, penguins.
- Labels:
labels.
- Guides:
guide-legend, guide-axis, guide-axis-logticks, guide-axis-stack, guide-axis-theta, guide-custom, guides, draw-key-blank, draw-key-line, draw-key-path, draw-key-point, draw-key-rect.
- Geoms:
annotate, geom-point, geom-line, geom-path, geom-step, geom-area, geom-rect, geom-tile, geom-bin-2d, geom-hex, geom-contour, geom-contour-filled, geom-segment, geom-curve, geom-spoke, geom-polygon, geom-ellipse, geom-mark, geom-col, geom-bar, geom-histogram, geom-freqpoly, geom-density, geom-density-2d, geom-density-2d-filled, geom-smooth, geom-ribbon, geom-boxplot, geom-violin, geom-density-ridges, geom-errorbar, geom-errorbarh, geom-count, geom-linerange, , , , , , , , , , , , , , , , , .
Worked example
#import "@preview/gribouille:0.7.0": *
#let species-colours = (
Adelie: rgb("#ff8c00"),
Chinstrap: rgb("#008B8B"),
Gentoo: rgb("#800080"),
)
#let species-scale = scale-discrete(
limits: species-colours.keys(),
palette: species-colours.values(),
)
#plot(
data: penguins,
mapping: aes(
x: "flipper-len",
y: "body-mass",
colour: "species",
fill: "species",
shape: "species",
),
layers: (
geom-point(size: 2pt, alpha: 0.25, stroke: 0.5pt, colour: rgb("#ffffff")),
geom-smooth(method: "lm", se: true, alpha: 0.2),
geom-mark(method: "hull", expand: 5pt, alpha: 0.25),
),
scales: scales(
x: scale-continuous(),
y: scale-continuous(labels: format-comma()),
colour: species-scale,
fill: species-scale,
),
labels: labels(
title: typst("Penguins *Dataset*"),
x: "Flipper Length (mm)",
y: "Body Mass (g)",
),
guides: guides(default: none),
theme: theme-minimal(),
width: 12cm,
height: 9cm,
)
Bind a shared scale once (species-scale) and reuse it for every aesthetic that carries the same palette.
Density and pattern fills
Kernel density estimation is native.
The same KDE core backs geom-violin, geom-density-ridges, geom-density-2d, and geom-density-2d-filled:
#plot(
data: penguins,
mapping: aes(x: "body-mass", fill: "species"),
layers: (geom-density(fill: auto, alpha: 0.4),),
)
Fills accept native Typst tiling paints, as fixed fill: values or through a palette.
Legend swatches render the pattern:
#let stripes(base, ink) = tiling(size: (5pt, 5pt))[
#place(box(width: 5pt, height: 5pt, fill: base))
#place(line(start: (0%, 100%), end: (100%, 0%), stroke: 1pt + ink))
]
#plot(
data: (g: ("a", "a", "b", "b"), n: (3, 5, 2, 4), k: ("x", "y", "x", "y")),
mapping: aes(x: "g", y: "n", fill: "k"),
layers: (geom-col(position: "dodge"),),
scales: scales(fill: scale-manual(values: (
stripes(rgb("#fde68a"), rgb("#b45309")),
stripes(rgb("#bfdbfe"), rgb("#1d4ed8")),
))),
)
Gotchas
- Aesthetics use British spelling:
colour, not color.
On error, aes() lists the valid channels.
- Fill opacity defaults to 1 on
geom-area, geom-ribbon, geom-polygon, geom-ellipse, and geom-mark.
For translucency, pass alpha:.
stroke: accepts the native Typst 1.3pt + accent form (thickness and paint together).
An explicit colour: wins over the embedded paint.
- Unknown
stat, scale transform, oob, or theme() element names fail fast instead of silently rendering as identity.
- CSV string-numeric columns stay discrete.
For a continuous scale, call
as-numeric(data, "col").
Reference protocol
The API is pre-1.0.
Names and arguments change between releases.
Do not recall a geom, stat, scale, or argument from memory.
Confirm each against the machine-readable reference.
- Index:
https://m.canouil.dev/gribouille/llms.txt lists every page and its .llms.md.
- Fetch the symbol's page, e.g.,
.../reference/geoms/geom-smooth.llms.md, .../reference/scales/scale-viridis-d.llms.md, .../reference/stats/stat-summary.llms.md.
Every <page>.html has a sibling <page>.llms.md.
- Use only the arguments, defaults, and types documented there.
Families under reference/: core, geoms, stats, scales, coord, facets, positions, guides, labels, themes, helpers, datasets.
Tutorials: get-started/, guides/, examples/.
Validate
Compile before claiming success:
typst compile plot.typ
Errors follow the shape <scope>: <problem>; got <value>. <hint>.
Read the hint, then re-check the argument against its .llms.md page.