Changelog

Version history.

0

0.6

Read the Gribouille 0.6 announcement

0.6.0 (2026-07-28)

Breaking changes
  • feat!: axis ticks are themed through the single axis-ticks family: the new element-tick(colour:, stroke:, length:) carries the mark length, so tick-length (and its per-axis and per-side variants) is removed, and tick-labels is removed in favour of hiding labels per side with axis-text: element-blank(). axis-ticks: element-blank() is now the one switch that turns marks off, reserving no depth either. (#150GitHub)
  • feat!: the summary-dispatch kernel summarise(name, values) leaves the public API (renamed to the internal _apply-summary), freeing the summarise name for the new wrangle verb; the fun: argument of stat-summary and stat-summary-bin (a name string or callable) is unchanged. (#198GitHub)
Changes
  • feat: automatic continuous breaks come from the extended Wilkinson search (Talbot, Lin, and Hanrahan 2010), which scores candidate tick sequences on simplicity, coverage of the data, and tick count, replacing the fixed 1/2/5 ladder on axes and continuous guides; most plots gain rounder ticks that sit closer to the data. breaks-extended(n:) exposes it for a different target count. (#217GitHub)
  • feat: breaks: and minor-breaks: on a continuous scale accept a closure called with the vector of values the scale trained on (once per panel, so free-scale facets each get their own) and returning the tick positions; unlike an explicit array, closure breaks are clipped to the trained domain rather than widening it. (#216GitHub)
  • feat: breaks-width(width, offset:), breaks-pretty(n:), and breaks-quantile(probs:) build that closure for the common placements: a fixed step, round positions at a target count, and sample quantiles of the data. (#216GitHub)
  • feat: summarise(data, ..aggregations, by:) and count(data, ..cols, sort:) bring grouped aggregation to the row-store data model: one output row per group, named rows => value aggregation closures, first-appearance group order, column-store input accepted, and output that feeds #plot directly. (#198GitHub)
  • feat: as-numeric(data, col, na:) takes a list of sentinel values (a bare value is accepted); cells equal to a sentinel (string cells compared trimmed) become none before parsing, so missing-value placeholders such as "NA" or "-99" do not survive as spurious numbers. (#198GitHub)
  • feat: column/row selection verbs select(data, ..names), rename(data, ..renames), relocate(data, ..cols, before:, after:), drop-na(data, ..cols), distinct(data, ..cols), and slice-max/slice-min(data, col, n: 1, by:) reshape, dedup, and rank a row-store (non-numeric or missing values sort last when ranking), accept column-store input, and feed #plot directly. (#198GitHub)
  • feat: reshaping verbs pivot-longer(data, cols, names-to:, values-to:) and pivot-wider(data, names-from:, values-from:) melt columns into a name/value pair and spread them back (round-tripping modulo column order), filling absent combinations with none, failing on an ambiguous duplicate spread, and carrying the as-factor tag on surviving identifier columns. (#198GitHub)
  • feat: join and bind verbs left-join/inner-join/full-join/semi-join/anti-join(x, y, by:) combine two row-stores on shared key columns (a natural join on the common columns when by: none, a non-key column present in both is an error), and bind-rows(..datasets)/bind-cols(..datasets) stack or glue datasets, unioning columns with none-fill or zipping them by row position. (#198GitHub)
  • feat: the bundled mpg dataset carries a drv column ("4", "f", "r"), so drive train can be faceted, coloured, or grouped on without substituting cyl. (#226GitHub)
  • feat: minor tick marks are themable through axis-ticks-minor and its per-axis variants, replacing the hardcoded half-length, major-stroke sub-decade ticks drawn by guide-axis-logticks(); they inherit the major tick record, so the default rendering is unchanged. (#150GitHub)
  • fix: a numeric nudge-x/nudge-y on geom-text, geom-label, and geom-typst no longer fails the compile when a positional scale is discrete; the offset shifts in level units there, matching position-nudge, and a nudge on the continuous axis of such a plot now applies instead of erroring. (#225GitHub)
  • fix: dup-axis(breaks:, labels:) and sec-axis(breaks:, labels:) are honoured; the secondary axis drew the primary grid with the primary formatting whatever these were set to. (#216GitHub)
  • fix: a continuous scale trained on whole numbers only (years, counts, epoch days) keeps whole automatic breaks on axes and on continuous legends, so a narrow range such as 2020 to 2023 no longer labels half-steps. (#213GitHub)
  • fix: legend entry labels, vertical colourbar tick labels, and y-axis tick labels centre on the cap-height/baseline band instead of the glyph bounds, so a label carrying a descender (Chinstrap) no longer sits higher than one without (Adelie). (#208GitHub, #209GitHub)
  • docs: footer links are legible in light mode (they were painted the page colour), the footer top rule reads in dark mode instead of being 8% black on dark paper, and the copy-to-clipboard button no longer hangs past the bottom border of a one-line code block. (#222GitHub)
  • docs: the theming guide gains a “Ticks and their labels” section showing element-tick, ratio lengths, minor marks, and per-side label hiding. (#219GitHub)
  • docs: reference pages link every cross-reference instead of printing a bare @geom-line when the reference closes a parenthetical, and links no longer run into the preceding word or comma. (#215GitHub)
  • docs: every scale-* reference page lists the keys its ..args accepts with their type, accepted values, and default (including oob, previously undocumented), instead of naming a few of them in prose. (#214GitHub)
  • docs: in the collapsed navbar, the colour-scheme toggle and the search button group at the right edge, separated by the same gap as the rest of the navbar-right controls, and the brand centres, instead of the toggle sitting beside the brand with the search alone at the far right. (#210GitHub)
  • docs: the navbar GitHub icon becomes a widget showing live star and fork counts and a project menu (repository, issues, pull requests, releases, star, fork, Typst Universe, sponsor), replacing the separate Typst Universe, source-code, releases, and sponsor links; the version switcher, GitHub widget, search button, and colour-scheme toggle share one pill button style. (#205GitHub)
  • docs: add a “Wrangling data” guide walking a dataset from coercion through grouped aggregation, reshaping, and joining into #plot, with guidance on when native Typst suffices. (#198GitHub)
  • docs: the News page and the changelog link the Gribouille 0.6 release announcement. (#227GitHub)
  • docs: the stable site is rendered wholly from the release tag instead of taking its pages from main against a pinned src, so each site documents the API it ships with, and a documentation fix now reaches the stable site at the next release rather than immediately. (336b698GitHub)
  • docs: the navbar is dark in both light and dark schemes (matching the other project sites); add scheme-aware scrollbars, an accessible skip link and focus outline, and fix the tabset panel border that was indistinguishable from the background in dark mode. (d21e005GitHub)
  • docs: in the collapsed navbar, opening a menu (e.g., Guides) overlays on top instead of growing the navbar and pushing the other items down, matching the GitHub widget menu. (930fb21GitHub)

0.5

Read the Gribouille 0.5 announcement

0.5.0 (2026-07-13)

Breaking changes
  • feat!: linetype on geom-segment, geom-curve, geom-spoke, geom-errorbar, geom-errorbarh, geom-linerange, and geom-pointrange defaults to auto and honours a mapped linetype scale instead of pinning "solid". (#169GitHub)
  • feat!: every constructor dict stores its concrete type under a single name: key; the per-family fields geom: (layers), coord:, facet:, family: (scale stubs), labeller:, and key: (draw keys) are renamed, guide-custom() returns kind: "guide", name: "custom", and position-fill()/position-identity()/position-stack() carry an explicit params: (:). Only code that pattern-matches the returned dicts is affected. (#168GitHub)
  • feat!: the per-aesthetic scale-* constructors collapse into an aesthetic-agnostic set keyed through scales(); the aesthetic now comes from the key, so scale-colour-viridis-d() becomes scales(colour: scale-viridis-d()) and scale-x-log10() becomes scales(x: scale-log10()). plot(scales:) takes only the scales() dictionary (no positional array), and expand-limits() returns an aesthetic-keyed dictionary. (#158GitHub)
Changes
  • feat: geom-beeswarm, position-jitter, and position-jitterdodge swarm or jitter a discrete (categorical) x directly, like geom-violin; as-factor is no longer required and is only needed to force a numeric column onto a discrete axis. Level positions now follow the sorted axis domain, so the swarm aligns regardless of row order. (#196GitHub)
  • feat: facet-wrap and facet-grid gain a gutter argument and honour a new theme(panel-spacing:) default (a length for both axes or a (x:, y:) dictionary), replacing the fixed 0.4/0.3cm panel gaps with a shared 0.5cm default. (#191GitHub)
  • feat: compose gutter accepts a (x:, y:) dictionary for independent column and row spacing alongside the existing scalar length. (#191GitHub)
  • feat: stat-waffle turns per-group counts (one per row, or the rounded weight sum) into unit cells on an integer grid, column-major from the bottom-left with groups tiling consecutive runs; render waffle charts with geom-tile. (#189GitHub)
  • feat: stat-connect gains connection: "sigmoid", a logistic S-curve connector rescaled through both observations with smooth and n control; paired with geom-line and a reversed y scale it draws bump charts. (#188GitHub)
  • feat: position-stack gains streamgraph baseline offsets: offset: "silhouette" centres each stack on zero and offset: "wiggle" uses the Byron-Wattenberg wiggle-minimising baseline, turning stacked geom-area into a streamgraph. (#187GitHub)
  • feat: stat-difference shades the band between two series by which one is on top: exact linear crossovers are inserted as shared vertices so ribbon segments meet cleanly, and each run carries a _sign level (names via levels:) for fill: after-stat("_sign"). (#186GitHub)
  • feat: fills accept native Typst tiling paints, both as fixed fill: values and through scale-manual(values:), with legend swatches rendering the patterns; per-row alpha and seam sealing skip non-colour paints. (#182GitHub)
  • feat: position-beeswarm and geom-beeswarm arrange overplotted points into a deterministic density-shaped swarm (van der Corput walk scaled by each point’s kernel density), a reproducible alternative to position-jitter. (#181GitHub)
  • feat: geom-density-ridges and stat-density-ridges draw overlapping ridgeline plots from raw observations: one Gaussian-KDE ridge per y bucket with heights normalised across buckets, a scale parameter in y-level units, and a new height aesthetic channel. (#180GitHub)
  • feat: geom-density-2d/geom-density-2d-filled and their stats trace iso-lines or shade iso-bands of a native 2D Gaussian kernel density estimate of the raw (x, y) sample, with per-axis bandwidth and grid control. (#178GitHub)
  • feat: geom-smooth/stat-smooth gain method: "loess" with span and degree control: native tricube-weighted local polynomial fits with a pointwise confidence band on n − trace(L) degrees of freedom. (#177GitHub)
  • feat: geom-violin and stat-ydensity draw mirrored per-bucket density silhouettes with scale: "area"/"count"/"width" normalisation, trim control, and dodge support, sharing the native Gaussian KDE core with stat-density. (#176GitHub)
  • feat: geom-density and stat-density bring native 1D kernel density estimation: Gaussian kernels evaluated directly on an n-point grid, Silverman (bw.nrd0) bandwidth with adjust and trim control, weight support, and _density/_count/_scaled/_n after-stat columns. (#175GitHub)
  • feat: every geom exposes key: (legend glyph override) and, where meaningful, stat: and position:; stat-backed geoms take stat: auto to build their default stat from the geom parameters. (#169GitHub)
  • feat: a keyed scales() constructor, mirroring guides(), binds scale overrides to aesthetics as named arguments (e.g., scales(x: scale-continuous(), colour: scale-viridis-d())) instead of a positional array. (#157GitHub)
  • feat: an installable Agent Skill teaches coding agents to author Gribouille plots, confirming every argument against the site’s .llms.md reference; the repository doubles as a Claude Code plugin marketplace, so the skill installs via /plugin install or npx skills add. (#154GitHub)
  • feat: geom and theme-element stroke: accept the native Typst 1.3pt + accent form, writing thickness and paint together; an explicit colour: still wins over the embedded paint. (#153GitHub)
  • fix: support explicit as-factor() on x for geom-violin, geom-boxplot, and other per-group stats when a grouping aesthetic reuses the same column. (#195GitHub)
  • fix: geom-density with a mapped fill aesthetic shades the curves instead of rendering a blank panel; the default is unfilled through a fill role rather than a hard fill: none. (#192GitHub)
  • fix: non-identity positions (jitter, beeswarm) over an as-factor positional column that is also mapped to colour/fill now draw the mapped scale colours instead of the default; the 1-indexed positions go to a synthetic column so the source column keeps its levels. (#192GitHub)
  • fix: pre-stat grouping skips late-binding markers without a source column (e.g., fill: after-stat("_sign"), colour: after-stat("_level")) instead of crashing on the marker dict; the stat-output column groups the rows once the stat has run. (#186GitHub)
  • fix: annotate forwards a none or auto value on an aesthetic keyword (e.g., annotate("rect", fill: none)) to the geom as a layer parameter instead of storing it as a data cell, so the disable / scale-resolve sentinel applies and the shape is drawn unfilled. (#183GitHub)
  • fix: fill opacity defaults to 1 on geom-area, geom-ribbon, geom-polygon, geom-ellipse, and geom-mark instead of an implicit 0.3-0.6 translucency; explicit alpha: values and mapped alpha aesthetics are unaffected. (#179GitHub)
  • fix: abutting opaque fills (tiles, hexes, stacked bars and areas, iso-band cells) are stroked with their own fill colour, removing the hairline background seams that antialiasing bled through between adjacent shapes. (#178GitHub)
  • fix: stat-quantile replaces the O(n³) pair enumeration with an exact O(n² log n) breakpoint search, scaling geom-quantile to thousands of rows with identical fits on unique optima. (#174GitHub)
  • fix: an unknown scale-* constructor argument now fails at scales() binding with the valid keys for that scale and aesthetic, and stray positional arguments are rejected instead of ignored. (#172GitHub)
  • fix: an unknown stat, scale transform, or oob name now fails with a clear error instead of silently rendering as identity or dropping rows. (#162GitHub)
  • fix: theme() rejects unknown or misspelled element keys and positional arguments instead of storing a key the renderer never reads. (#162GitHub)
  • fix: aes() rejects unknown aesthetic names, such as the US spelling color, with a message listing the valid channels. (#162GitHub)
  • fix: plot() validates the mapping, layers, and coord arguments up front with a named error instead of a cryptic renderer failure. (#162GitHub)
  • fix: data-ingestion and from-theme path errors now show the offending value. (#162GitHub)
  • fix: mapping an aesthetic or a facet variable to a column absent from the data now fails with a clear error naming the aesthetic, the unknown column, and the available columns, instead of rendering a misleading all-none plot. (#156GitHub)
  • fix: numeric values on a discrete scale (a polygon vertex set between level centres, a jittered point) are kept as fractional level positions instead of being dropped when the scale carries explicit limits. (#151GitHub)
  • docs: the published Typst Universe README drops the repo-only “Ask DeepWiki” badge and now keeps the “AI assistants” section. (#194GitHub)
  • docs: add streamgraph, bump chart, difference band, and waffle chart gallery examples. (#193GitHub)
  • docs: a Recipes guide shows chart types and effects built by composing layers and native Typst, with no dedicated geom: lollipop and dumbbell charts, group highlighting, zoom insets, marginal panels, irregular grid layouts, and trend-line casing. (#185GitHub)
  • docs: the agent skill covers the current API: the aesthetic-agnostic scales() constructors, the density and pattern-fill idioms, and a symbol inventory regenerated from lib.typ and checked in CI. (#184GitHub)
  • docs: each scale constructor reference page carries a runnable example in the keyed scales() form. (#160GitHub)
  • docs: an AI Assistants guide explains how to use Gribouille with large language models through the llms.txt machine-readable documentation and the installable skill. (#154GitHub)
  • docs: a Benchmarks guide measures how compile time and output size grow with element count, drawn from a committed dataset with Gribouille itself, and shows where per-row geoms become impractical for large data. (#149GitHub)

0.4

Read the Gribouille 0.4 announcement

0.4.1 (2026-06-23)

  • fix: position: "dodge" now shifts point-based geoms (text, label, point, linerange, etc.) on continuous x-axes by inferring the slot width from the minimum gap between unique positions. (#145GitHub)
  • fix: string values such as "0" are no longer inferred as numeric for scale type; only Typst int/float values produce a continuous scale. CSV users relying on string-numeric x-columns being treated as continuous must call as-numeric(data, "col") first. (#145GitHub)
  • docs: the News page links the Gribouille 0.4 release announcement and shows a featured image for each release. (#141GitHub)

0.4.0 (2026-06-22)

Breaking changes
  • feat!: coord-cartesian() and coord-radial() take clip as a boolean (true/false) instead of the strings "on"/"off". (#134GitHub)
  • feat!: labs() is renamed to labels(), and the plot() / compose() labs: argument to labels:; there is no labs alias. (#131GitHub)
  • feat!: coord-cartesian(), stat-function(), and geom-function() rename the xlim/ylim arguments to x-limits/y-limits. (#131GitHub)
  • feat!: the col-mix() colour helper is renamed to colour-mix(), with arguments colour1/colour2. (#131GitHub)
  • feat!: geom-text, geom-label, and geom-typst drop the dx/dy parameters; offsets now use the nudge-x/nudge-y aesthetics, where a number shifts in data units and a Typst length shifts in canvas units. (#131GitHub)
Changes
  • feat: continuous scale limits and geom-abline intercepts accept ISO-8601 date/datetime/time strings under a temporal scale, completing the temporal-string contract. (#137GitHub)
  • feat: geom-vline/geom-hline intercepts and continuous scale breaks accept ISO-8601 date/datetime/time strings under a temporal scale, matching how geom data is already parsed. (#136GitHub)
  • feat: geom-area() accepts a direction parameter ("hv" or "vh") that steps the filled top edge like geom-step(); none (default) keeps the smooth area. (#135GitHub)
  • feat: element-text() / element-typst() angle is now honoured on axis titles, strip text, and legend title and entry labels (previously only tick labels and the plot title, subtitle, and caption); axis titles default to 0deg (x) / 90deg (y) and reserve the correct space at any angle. (#134GitHub)
  • feat: geom-text(), geom-label(), and geom-typst() accept an angle parameter (a Typst angle, default 0deg) rotating each label. (#134GitHub)
  • feat: every aesthetic key can be pinned as a constant directly on any geom, e.g., geom-label(nudge-x: 0.5), not only through aes(); declared parameters still bind first and unknown or positional arguments are rejected. (#133GitHub)
  • feat: scale expand accepts auto on one side, e.g., expand: (0%, auto), keeping the per-scale default for that side. (#132GitHub)
  • feat: geom-vline, geom-hline, and geom-abline read their intercept channels from aes(); binding xintercept/yintercept/slope/intercept together with data draws one line per row with per-row colour/alpha/linewidth/linetype, and a mapped xintercept/yintercept extends the panel to keep the lines in view; the scalar/array parameter still draws shared lines when nothing is mapped. (#129GitHub)
  • feat: a font set in theme(text: ...) now reaches literal-glyph point markers (e.g., shape: sym.star) for both plot markers and legend keys, instead of falling back to the document font. (#126GitHub)
  • feat: geom-text, geom-label, and geom-typst accept a font: parameter; auto uses the theme text font, then the document font. (#126GitHub)
  • feat: panel gridlines split into a ggplot2-style cascade, panel-grid -> panel-grid-major / panel-grid-minor -> -x / -y, so X/Y and major/minor lines are styled independently; minor defaults to half the major weight in the same colour and a blank panel-grid blanks both weights. (#125GitHub)
  • feat: continuous axes draw minor gridlines (visible by default): midpoints between majors for linear/sqrt/reverse and sub-decade lines for log10; scale-*-continuous (and log10/sqrt/reverse) accept minor-breaks and n-minor to position them. (#125GitHub)
  • feat: the legend-position theme entry sets a global side for every legend (theme(legend-position: "bottom"), also via theme-sub-legend(position:)), accepting the same values as guide-legend(position:); any explicit guides() placement still wins. (#123GitHub)
  • feat: discrete (swatch) legend key glyphs are resizable via guide-legend(key-size: <length>) per legend or the legend-key theme entry for every swatch legend; the row spacing and label offset grow with the glyph so enlarged keys do not overlap. (#122GitHub)
  • feat: the shape aesthetic accepts any character as the marker symbol; a value outside the eight built-in keywords renders as a literal glyph, so letters or emoji work as markers across scale-shape, scale-shape-manual, scale-shape-identity, scale-shape-binned, the geom-point(shape: ...) pin, and legend keys. (#120GitHub)
  • feat: scale limits accept auto on one side, e.g., limits: (auto, 60), keeping the trained bound for the other side. (#116GitHub)
  • feat: binned position scales (scale-x-binned / scale-y-binned) accept breaks as bin edges; ticks sit at interval midpoints and n-breaks is ignored when breaks is set. (#116GitHub)
  • feat: binned colour, fill, shape, linetype, size, linewidth, stroke, and alpha scales accept breaks as bin edges; colour, fill, shape, and linetype bin per row by the edges, the others bin their legend. (#116GitHub)
  • feat: bundled datasets (penguins, economics, mpg) ship as row-store arrays, so penguins.filter(...) and .map(...) work directly; plot() still accepts them. (#116GitHub)
  • fix: scalar and Typst-length nudge-x/nudge-y values now apply to geom-text/geom-label/geom-typst (previously a silent no-op). (#131GitHub)
  • fix: setting the base line/rect stroke in theme() now cascades to every line and rect surface (panel grid, axis line, ticks, legend ticks, legend bar); element-line/element-rect stroke accepts a ratio (e.g., 80%) to scale the parent surface thickness, while an absolute length still pins a surface outright. (#124GitHub)
  • fix: setting the base text size in theme() now cascades to every text surface (axis titles, tick labels, legend, strip, plot title); element-text/element-typst size accepts a ratio (e.g., 80%) to scale the parent surface size, while an absolute length still pins a surface outright. (#121GitHub)
  • fix: position: "dodge" now shifts geom-text/geom-label/geom-typst, geom-point, geom-line/geom-path/geom-step, geom-pointrange, and geom-linerange marks side by side, matching the dodged bars instead of staying on the category centre. (#119GitHub)
  • fix: theme-grey() matches ggplot2 theme_grey() with white grid lines and no axis lines. (#116GitHub)
  • fix: explicit continuous breaks expand the axis range so requested ticks stay visible, unless a side is pinned by an explicit limit. (#116GitHub)
  • docs: a News page lists the release announcement posts, and each changelog version links to its announcement. (#139GitHub)

0.3

Read the Gribouille 0.3 announcement

0.3.0 (2026-06-14)

Breaking changes
  • feat!: compose() panels are now built with the new defer() helper (defer(plot, ...), or defer(compose, ...) to nest), replacing plot(..., defer: true); panels omit their own width/height and the composition sizes each cell. (#81GitHub)
Changes
  • feat: the published package now ships Tinymist-friendly docstrings; hovering a gribouille function in an editor shows formatted parameters, returns, and examples instead of the raw @-tag comments. Variadic sinks list their accepted keys: guides() its aesthetics, and theme() and the theme presets their element surfaces. (#108GitHub)
  • feat: under coord-radial, guides(theta: none) hides the angular axis (arc, minor ticks, and tick labels) and guides(r: none) hides the radial tick labels; the spokes and circles stay. Also fixes guides(theta: none) previously adding a theta arc instead of hiding the axis. (#106GitHub)
  • feat: guides(x: none) / guides(y: none) hide that axis’s tick marks and tick labels (and any log minor ticks) and reclaim the freed space; the axis line, grid, and title stay (remove the title with labs(x: none)). (#105GitHub)
  • feat: guides() accepts none to hide a guide and auto for the default; guides(default: none) hides every legend without its own override, and any other non-guide value fails with a clear message. Replaces the removed guide-none() (pass none instead). (#104GitHub)
  • feat: annotate() gains a clip argument (default true); set clip: false to let an annotation overflow the panel, e.g., a corner inset or a mark placed past the axis limits. (#93GitHub)
  • feat: compose() accepts theme:; an explicit theme styles the composition chrome (title, hoisted legend, panel tags) and propagates into panels with no theme of their own, otherwise a theme shared by every panel is used. (#82GitHub)
  • feat: geom-area() defaults to stat: "align" and position: "stack"; groups with mismatched x values are automatically resampled onto a shared grid and stacked without requiring explicit stat: stat-align(). (#77GitHub)
  • fix: stat-bin-2d and stat-bin-hex _density is now the cell’s fraction of the total count (count / sum(count)), matching ggplot2, instead of an undocumented count-per-cell-area value. (#109GitHub)
  • fix: data outside a sqrt or log10 transform’s domain (negative, or zero/negative for log10) now panics with a clear scale message instead of a raw Typst error, and the sqrt inverse clamps padded view bounds at zero so sqrt axes stay monotone and show the 0 break. (#109GitHub)
  • fix: a horizontal legend (position: "top" / "bottom") with guide-legend(align: center) / align: right now centres or right-justifies its key graphic (colourbar bar, key row, or size band) under the title, instead of leaving the graphic pinned at the left edge while the title moved; a centred colourbar also keeps its end labels inside the legend block. (#103GitHub)
  • fix: the theme legend-background now inherits the base rect element like every other *-background surface, so theme(rect: ...) cascades to it instead of being ignored. (#101GitHub)
  • fix: guide-legend(nrow:) / guide-legend(ncolumn:) now lay a continuous size legend (scale-size-*) out in a grid, like the discrete swatch legend, instead of being ignored; a continuous colourbar stays a single bar and ignores them. (#99GitHub)
  • fix: guide-legend(align:) now also aligns the legend title (not just the entry labels), and a string such as align: "left" fails with a clear message instead of a vague panic or being silently ignored; align is a Typst alignment (left, center, right). (#98GitHub)
  • fix: annotate(clip: false) placed outside the scale limits now draws instead of being dropped by the out-of-range pre-pass, so a corner inset or a mark parked past the axis range is kept. (#97GitHub)
  • fix: a left/right legend that would overrun the caption, or a hoisted compose() legend that leaves no room for the panels, now fails with a clear layout hint instead of silently overprinting. (#96GitHub)
  • fix: continuous scale-size-* legends reserve row height and stride for the resolved key glyph, so a wide size range no longer overlaps the keys and labels. (#95GitHub)
  • fix: the out-of-range filter respects scale expand, keeping points and annotations that sit inside the expansion headroom instead of dropping them; reversed limits (a flipped axis) no longer drop every row. (#92GitHub)
  • fix: geom-linerange() honours its alpha parameter; the line was previously always drawn fully opaque. (#85GitHub)
  • fix: geom-area(position: "stack") now stacks bands correctly; each polygon’s lower edge sits at the cumulated top of the group below rather than always closing at y = 0. (#77GitHub)
  • fix: stat-align() expands its shared grid by a small offset on either side of every breakpoint, so a group that starts or ends mid-range steps cleanly down to the baseline instead of leaving a wedge below the neighbouring group. (#77GitHub)
  • docs: the theming guide notes relative (%) versus absolute text sizing, and element-text gains an example contrasting the two. (#121GitHub)
  • docs: the guides() reference lists each accepted key (colour, fill, ) as a sub-list under the ..args parameter, sharing one description template. (#108GitHub)
  • docs: reference pages for forwarding functions such as the scale-* colour wrappers now list their real parameters instead of an opaque ..args row, and a mixed signature like annotate(geom, ..fields) also lists clip. (#102GitHub)
  • docs: the theme() reference table now lists every theme key, adding plot-tag, legend-ticks, legend-background, legend-bar, and geom. (#100GitHub)

0.2

Read the Gribouille 0.2 announcement

0.2.1 (2026-06-03)

  • feat: compose() gains align-panels (default false); when true, panels share margins grid-wise (left/right per column, top/bottom per row) so their plot areas line up across rows and columns, like patchwork/cowplot panel alignment. (#71GitHub)
  • fix: compose() panel tags (tag-levels) reserve their own band so they no longer overlap the panel content. (#70GitHub)
  • fix: geom-segment/geom-curve/geom-ribbon/geom-area render on discrete scales instead of silently drawing nothing. (#69GitHub)

0.2.0 (2026-06-01)

  • feat: a layer’s data accepts a function applied to the plot data, returning that layer’s frame (e.g., geom-point(data: rows => rows.filter(...))), for subsetting or per-layer transforms without a separate dataset. (#62GitHub)
  • feat: facet-grid(scales:) supports free scales ("free_x" frees x per column, "free_y" frees y per row, "free" both); non-positional scales stay shared and panels keep equal size. (#60GitHub)
  • feat: legend entry labels honour the legend-text text align, and guide-legend() gains an align argument; horizontal legends default to centred labels, vertical to left. (#34GitHub)
  • feat: compose() accepts defer: true to return a spec usable as a panel of another compose, enabling nested compositions; tag-levels accepts a per-depth array (with tag-sep) so nested panels continue the numbering (e.g., B.1, B.2). (#24GitHub)
  • feat: compose() can number panels with a tag pattern (tag-levels "A"/"a"/"1"/"I"/"i", plus tag-prefix/tag-suffix/tag-corner), styled by the new plot-tag theme element. (#23GitHub)
  • feat: compose() accepts composition-level labs (title/subtitle/caption) and alt, and now controls the collected legend’s side through guides (e.g., guides(default: guide-legend(position: "bottom"))); the guides-placement parameter is removed. (#22GitHub)
  • feat: guides() accepts a default entry that sets fallback guide options (such as the legend side) inherited by every aesthetic without its own override, in both plot() and compose(); guide-legend’s position now defaults to auto and inherits from it. (#21GitHub)
  • feat: compose() gains width/height (filling a bounded container by default) and relative widths/heights to set panel proportions relative to one another. (#20GitHub)
  • feat: element-text()/element-typst() gain an align parameter setting per-surface text alignment, independent of the container; title and subtitle default left, caption right, axis titles and strip text centred. (#13GitHub)
  • feat: labs() fields default to auto; pass none to suppress an axis or legend title and reclaim the space it reserved. (#12GitHub)
  • feat: element-blank() on a text surface (axis, plot, or legend title) collapses the space the text would reserve. (#12GitHub)
  • feat: width/height accept auto to fill the available space of a bounded container. (#10GitHub)
  • fix: a standalone plot no longer reserves a fixed empty margin on its top and right edges, letting the panel fill that space. (c9d53acGitHub)
  • fix: a plot with no x-axis title reclaims the empty space the title would have reserved below the panel, matching the y-axis side. (#61GitHub)
  • fix: element-text(angle:)/element-typst(angle:) rotate axis tick labels (seeding the guide-axis(angle:) default) and the plot title, subtitle, and caption, instead of being ignored. (#59GitHub)
  • fix: labs(tag:) draws the figure tag above the title on a standalone plot, styled by the plot-tag theme element, instead of being ignored. (#58GitHub)
  • fix: labs(alt:) fills in the figure’s accessibility alt text when plot(alt:) is unset, instead of being stored and ignored. (#57GitHub)
  • fix: geom-qq()/geom-qq-line() honour the distribution argument (uniform/exponential) instead of always plotting against the normal reference. (#56GitHub)
  • fix: the font set on element-text/element-typst/element-geom is applied to every text surface and to the text-drawing geoms, inheriting the base text font when unset. (#54GitHub)
  • fix: a stage/after-scale channel now trains its scale on the marker’s source column, so the closure receives the channel’s scale-resolved value as documented. (#52GitHub)
  • fix: grouped geoms (e.g., geom-smooth) no longer panic when a grouping aesthetic is mapped via after-scale or stage. (#51GitHub)
  • fix: facet-grid legends reserve space for a secondary x-axis so a top legend no longer overlaps its ticks. (#46GitHub)
  • fix: geom-errorbar()/geom-errorbarh()/geom-rug() resolve an after-scale/stage colour mapping instead of panicking. (#45GitHub)
  • fix: continuous legends fall back to computed breaks when every user-supplied break falls outside the domain. (#44GitHub)
  • fix: coord-cartesian(xlim:/ylim:) zooms continuous axes instead of being ignored. (#43GitHub)
  • fix: coord-flip(reverse: true) preserves a log10/sqrt axis transform instead of overwriting it. (#42GitHub)
  • fix: geom-col() on a continuous x with a single distinct value no longer panics when inferring the bar width. (#41GitHub)
  • fix: log10/sqrt scales validate user limits and report a clear error for non-positive bounds. (#40GitHub)
  • fix: geom-smooth() honours the level argument when sizing the confidence band instead of always using 95%. (#39GitHub)
  • fix: binning geoms reject a non-positive bins with a clear error instead of dividing by zero. (#38GitHub)
  • fix: contour stats skip incomplete cells when the (x, y, z) grid is sparse instead of panicking. (#37GitHub)
  • fix: scale-*-manual() with an empty values array reports a clear error instead of an opaque divide-by-zero. (#36GitHub)
  • fix: number formatters carry a fraction that rounds up to a whole unit (e.g., 0.9999995 no longer renders as 0.1), and format-scientific keeps its mantissa in [1, 10). (#35GitHub)
  • fix: legend guides reserve space for multi-line labels, measuring the resolved custom labels: for both width and line count across swatch, size-ladder, and colourbar guides, so two-line content no longer clips or overlaps. (#33GitHub)
  • fix: a column mapped to both a positional aesthetic (x/y) and a grouping aesthetic (fill/colour/group/…), e.g., aes(x: "class", fill: "class"), now resolves the grouping aesthetic across aggregating stats (boxplot, summary, count/sum, histograms) instead of drawing every mark in the ink colour with an empty guide. (#31GitHub)
  • fix: continuous scales honour an explicit breaks argument for axis ticks and continuous legend guides instead of ignoring it; breaks outside the domain are dropped. (#30GitHub)
  • fix: geom-ribbon() draws one band per discrete fill/colour/group instead of merging every group into a single ribbon. (#29GitHub)
  • fix: plot(width: auto, height: auto) is allowed on an unbounded page, falling back to the default 10cm by 7cm instead of panicking. (#27GitHub)
  • fix: compose() panels fill their cells instead of being letterboxed; their own width/height are discarded once the composition has a size, and the composition size falls back to 16cm by 12cm when the container is unbounded. (#26GitHub)
  • fix: legend labels wider than 2 cm no longer overlap the next swatch; each legend column reserves the label’s full width, most visible in horizontal legends. (#25GitHub)
  • fix: the compose collected legend placed on top or bottom no longer clips its first swatch and is centred under the panels. (#19GitHub)
  • fix: width/height now bound the whole image, including title, subtitle, caption, and plot-background padding; the data panel shrinks to fit and long titles wrap. (b53fab2GitHub)
  • docs: callout headers now sit on the type tint as a distinct band while the body uses the plain surface, and caution gets its own deeper mustard so it no longer matches warning. (#16GitHub)
  • docs: tabset (panel-tabset) labels now follow the light/dark theme; the active tab label uses the brand primary colour in both schemes. (#15GitHub)
  • docs: the development version is now downloadable from the dev documentation site and installable as a local package; release, Typst Universe, and development archives share an identical payload. (#14GitHub)

0.1

Read the Grammar of Graphics for Typst announcement

0.1.1 (2026-05-22)

  • fix: centre collected compose side legends against the panel grid. (994c9e8GitHub)
  • fix: apply plot background inset/outset even without a fill. (2f24982GitHub)
  • docs: enable llms.txt output for the documentation site. (6b05d6aGitHub)
  • docs: add a navbar version switcher between the stable and development docs. (95c3d9bGitHub)

0.1.0 (2026-05-20)

  • feat: initial version of Gribouille.

Looking for a specific change? Browse the full commit history or the list of releases on GitHub.

Back to top