Changelog

Version history.

Unreleased

  • 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: 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

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