<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Mickaël CANOUIL</title>
<link>https://mickael.canouil.fr/blog.html</link>
<atom:link href="https://mickael.canouil.fr/blog.xml" rel="self" type="application/rss+xml"/>
<description>A data science blog and portfolio by Mickaël CANOUIL</description>
<language>en-gb</language>
<image>
<url>https://mickael.canouil.fr/assets/images/social-profile.png</url>
<title>Mickaël CANOUIL</title>
<link>https://mickael.canouil.fr/blog.html</link>
</image>
<generator>quarto-1.10.11</generator>
<lastBuildDate>Mon, 15 Jun 2026 00:00:00 GMT</lastBuildDate>
<item>
  <title>Gribouille 0.3.0: Finer Guide Control and a Themed Compose</title>
  <link>https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<p>Another week, another <a href="https://github.com/mcanouil/gribouille">Gribouille</a> release. Gribouille 0.3.0 is narrower in scope than <a href="../../posts/2026-06-03-gribouille-0-2/index.html">0.2</a>, but it brings some wanted controls. The headline is guide control: a single argument now hides axis ticks and legends without touching the theme. Alongside that, <code>compose()</code> gains a <code>theme:</code> parameter, <code>defer()</code> replaces <code>plot(..., defer: true)</code>, <code>geom-area()</code> stacks by default, and <code>annotate()</code> can let marks overflow the panel.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Featured card for the Gribouille 0.3.0 release on a cream paper
background. On the left, the Gribouille wordmark sits above the tagline
&quot;Create elegant graphics with the Grammar of Graphics for Typst.&quot;, a
small orange &quot;v0.3.0 release&quot; pill, and the URL
m.canouil.dev/gribouille. On the right, a slightly rotated bordered card
frames a scatter plot of Palmer Penguin body mass against flipper
length, with the three species shown as coloured point clouds.
" width="600"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>At a glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><a href="https://github.com/mcanouil/gribouille">Gribouille</a> 0.3.0 on Typst Universe: <code>#import "@preview/gribouille:0.3.0": *</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/guides/guides.html"><code>guides(x: none)</code></a> / <code>guides(y: none)</code> hide tick marks and tick labels; the axis line, grid, and title stay. Remove the title separately with <code>labs(x: none)</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/guides/guides.html"><code>guides(default: none)</code></a> hides every legend without its own override.</li>
<li>Under <a href="https://m.canouil.dev/gribouille/reference/coord/coord-radial.html"><code>coord-radial</code></a>, <code>guides(theta: none)</code> hides the full angular axis and <code>guides(r: none)</code> hides radial tick labels.</li>
<li><code>guides(none)</code> replaces the removed <code>guide-none()</code>; <code>guides(auto)</code> restores the default.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> gains <code>theme:</code>, which styles the composition chrome and propagates into panels with no theme of their own.</li>
<li><strong>Breaking change</strong>: <code>plot(..., defer: true)</code> is replaced by <code>defer(plot, ...)</code>. Panels inside a <code>compose()</code> also no longer accept their own <code>width</code>/<code>height</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/geoms/geom-area.html"><code>geom-area()</code></a> defaults to <code>stat: "align"</code> and <code>position: "stack"</code>; groups with mismatched x values are resampled automatically.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/layers/annotate.html"><code>annotate()</code></a> gains <code>clip:</code> (default <code>true</code>); <code>clip: false</code> lets a mark overflow the panel.</li>
</ul>
</div>
</div>
<p>Every figure in this post is a real, freshly compiled plot.</p>
<section id="guide-controls" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="guide-controls"><span class="header-section-number">1</span> Guide controls</h2>
<p>The biggest visible change is a new layer of control over what guides draw.</p>
<p>Before 0.3.0, hiding an axis’s tick marks and labels meant reaching into <code>theme()</code> for the right element surface. Now, <code>guides(x: none)</code> does it directly. The axis line, the vertical grid lines, and the axis title all stay. To drop the title too, add <code>labs(x: none)</code> alongside it.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-1" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-1-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-5" class="code-annotation-target">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-6">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-1-7">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ticks Off, Grid Stays"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-8">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-9">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-10">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-11">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-1-12">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-13">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="5" data-code-annotation="1"><code>guides(x: none)</code> removes tick marks and labels. The line, grid, and title are untouched. <code>guides(y: none)</code> does the same for the y-axis.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-x-none-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Penguin scatter of body mass against flipper length with no x-axis tick marks or labels. The vertical grid lines and the axis title 'Flipper Length (mm)' remain."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-x-none-light.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length with no x-axis tick marks or labels. The vertical grid lines and the axis title 'Flipper Length (mm)' remain."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-x-none-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Penguin scatter of body mass against flipper length with no x-axis tick marks or labels. The vertical grid lines and the axis title 'Flipper Length (mm)' remain."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-x-none-dark.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length with no x-axis tick marks or labels. The vertical grid lines and the axis title 'Flipper Length (mm)' remain."></a></p>
</div>
</div>
</div>
</div>
<p>The same syntax now controls legends too. <code>guides(none)</code> hides a legend; <code>guides(auto)</code> restores the default. Both replace the removed <code>guide-none()</code>.</p>
<p>When you want all legends gone at once, <code>guides(default: none)</code> sets the fallback for every aesthetic without its own override.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-5" class="code-annotation-target">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-6">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-7">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No Legends"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-8">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-9">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-10">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-11">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-2-12">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-13">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="5" data-code-annotation="1">Every aesthetic without an explicit guide inherits <code>none</code>; the colour legend disappears and the plot area fills the freed space.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-default-none-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Penguin scatter of body mass against flipper length, coloured by species, with no legend shown."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-default-none-light.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length, coloured by species, with no legend shown."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-default-none-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Penguin scatter of body mass against flipper length, coloured by species, with no legend shown."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-default-none-dark.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length, coloured by species, with no legend shown."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="radial-guide-controls" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="radial-guide-controls"><span class="header-section-number">2</span> Radial guide controls</h2>
<p>The same guide syntax extends to <code>coord-radial</code>. <code>guides(theta: none)</code> hides the full angular axis, the arc, minor ticks, and tick labels together. <code>guides(r: none)</code> hides only the radial tick labels, leaving the spokes and circles in place.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-3" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-3-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-3-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-bar<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),),</span></span>
<span id="annotated-cell-3-5">  coord<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> coord-radial<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-6" class="code-annotation-target">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>theta<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-3-7">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-3-8">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Angular Axis Hidden"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-9">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-10">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Count"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-11">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-3-12">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-3-13">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">10cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-14">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">10cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-15"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="6" data-code-annotation="1"><code>theta: none</code> removes the angular ring and labels. <code>default: none</code> also drops the fill legend, leaving the radial grid as the only scale reference.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-radial-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="A coxcomb chart of penguin counts by species using coord-radial, with the angular tick labels hidden. Only the radial grid is visible."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-radial-light.svg" class="img-fluid hero-art" alt="A coxcomb chart of penguin counts by species using coord-radial, with the angular tick labels hidden. Only the radial grid is visible."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/guides-radial-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="A coxcomb chart of penguin counts by species using coord-radial, with the angular tick labels hidden. Only the radial grid is visible."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/guides-radial-dark.svg" class="img-fluid hero-art" alt="A coxcomb chart of penguin counts by species using coord-radial, with the angular tick labels hidden. Only the radial grid is visible."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="compose-gets-a-theme" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="compose-gets-a-theme"><span class="header-section-number">3</span> Compose gets a theme</h2>
<p>Two changes landed in <code>compose()</code> together.</p>
<p>The first is a <code>theme:</code> parameter. Pass a theme and it styles the composition chrome: the shared title, the hoisted legend, and the panel tags. It also propagates into any panel that has no theme of its own, so you can set one theme once and let it cascade instead of repeating it across every panel.</p>
<p>The second is the <code>defer()</code> helper.</p>
<div class="callout callout-style-default callout-warning callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Warning</span>Breaking change
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>plot(..., defer: true)</code> is removed. Replace <code>plot(data: ..., ..., defer: true)</code> with <code>defer(plot, data: ..., ...)</code>. Panels inside a <code>compose()</code> also no longer accept their own <code>width</code>/<code>height</code>; the composition sizes each cell.</p>
</div>
</div>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-4" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> defer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="annotated-cell-4-5">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-6"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-4-7"></span>
<span id="annotated-cell-4-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>compose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-4-9">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-10">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bill Length"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-11">  columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-12">  tag-levels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-13">  tag-prefix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"("</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-14">  tag-suffix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">")"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-15">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bottom"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="annotated-cell-4-16">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"One Theme, Two Panels"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-4-17" class="code-annotation-target">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-4-18">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">18cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-19">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-20"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="1" data-code-annotation="1"><code>defer(plot, ...)</code> replaces <code>plot(..., defer: true)</code>. The panel no longer sets <code>width</code>/<code>height</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-4" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="17" data-code-annotation="2"><code>theme: theme-minimal()</code> styles the title, legend, and tags, and propagates into both panels because neither sets its own theme.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/compose-theme-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="Two scatter panels side by side, tagged (1) Body Mass and (2) Bill Length, sharing a species legend at the bottom and a title across the top. The minimal theme is set once on compose()."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/compose-theme-light.svg" class="img-fluid hero-art" alt="Two scatter panels side by side, tagged (1) Body Mass and (2) Bill Length, sharing a species legend at the bottom and a title across the top. The minimal theme is set once on compose()."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/compose-theme-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="Two scatter panels side by side, tagged (1) Body Mass and (2) Bill Length, sharing a species legend at the bottom and a title across the top. The minimal theme is set once on compose()."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/compose-theme-dark.svg" class="img-fluid hero-art" alt="Two scatter panels side by side, tagged (1) Body Mass and (2) Bill Length, sharing a species legend at the bottom and a title across the top. The minimal theme is set once on compose()."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="area-stacks-by-default" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="area-stacks-by-default"><span class="header-section-number">4</span> Area stacks by default</h2>
<p><code>geom-area()</code> now defaults to <code>stat: "align"</code> and <code>position: "stack"</code>.</p>
<p>Before 0.3.0, stacking a multi-group area chart needed both arguments spelled out. Now they are the defaults. <code>stat: "align"</code> also handles groups that have different x values: it resamples each group onto a shared grid before stacking, so the x values do not need to match.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-5" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">series</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-5-2">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">8</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">9</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-5-3">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"B"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-5-4">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> g: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"C"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-5-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-6"></span>
<span id="annotated-cell-5-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-5-8">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> series<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-9">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"g"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-10" class="code-annotation-target">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-area<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),),</span></span>
<span id="annotated-cell-5-11">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-5-12">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Stacked by Default"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-13">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"x"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-14">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"y"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-15">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Group"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-16">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-5-17">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-5-18">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-19">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-20"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="10" data-code-annotation="1">No <code>stat:</code> or <code>position:</code> needed. Groups A, B, and C have different x breakpoints; <code>stat: "align"</code> resamples them onto a shared grid before stacking.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/geom-area-stack-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-10" title="Stacked area chart with three groups A, B, and C over x values 0 to 4. Groups A and B have different x breakpoints; stat-align resamples them onto a shared grid before stacking."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/geom-area-stack-light.svg" class="img-fluid hero-art" alt="Stacked area chart with three groups A, B, and C over x values 0 to 4. Groups A and B have different x breakpoints; stat-align resamples them onto a shared grid before stacking."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-15-gribouille-0-3/./assets/typst-render/geom-area-stack-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-11" title="Stacked area chart with three groups A, B, and C over x values 0 to 4. Groups A and B have different x breakpoints; stat-align resamples them onto a shared grid before stacking."><img src="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/assets/typst-render/geom-area-stack-dark.svg" class="img-fluid hero-art" alt="Stacked area chart with three groups A, B, and C over x values 0 to 4. Groups A and B have different x breakpoints; stat-align resamples them onto a shared grid before stacking."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="annotations-can-overflow" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="annotations-can-overflow"><span class="header-section-number">5</span> Annotations can overflow</h2>
<p><code>annotate()</code> gains a <code>clip</code> argument. The default is <code>true</code>, which preserves the existing behaviour: marks outside the panel are clipped. Set <code>clip: false</code> and the mark is drawn even if it sits past the axis limits.</p>
<p>This is useful for corner insets, labels anchored outside the data range, or decorations that belong in the margin. A paired fix ensures that an <code>annotate(clip: false)</code> placed outside the scale <code>limits</code> actually draws. Before 0.3.0, the out-of-range pre-pass silently dropped it.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-6-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-6-5">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-6">    annotate<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-6-7">      geom-text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>label<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"← past the edge"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-8">      x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">235</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-9">      y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5000</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-10" class="code-annotation-target">      clip<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-11">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-12">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-13">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-6-14">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Annotation Outside the Panel"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-15">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-16">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-17">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-6-18">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-6-19">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-20">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-21"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="10" data-code-annotation="1"><code>clip: false</code> keeps the mark visible even though <code>x = 235</code> sits past the panel boundary.</span>
</dd>
</dl>
</section>
<section id="under-the-hood" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="under-the-hood"><span class="header-section-number">6</span> Under the hood</h2>
<p>As with 0.2, a large share of the release is fixes rather than features. Legend layout was the main focus.</p>
<ul>
<li>Horizontal legends with centred or right <code>align</code> now centre or right-justify the key graphic under the title, instead of leaving it pinned to the left while the title moved.</li>
<li><code>guide-legend(nrow:)</code> and <code>guide-legend(ncolumn:)</code> now work for continuous size legends, laying keys out in a grid as they do for discrete swatch legends.</li>
<li><code>guide-legend(align:)</code> now aligns the legend title as well as the entry labels. Passing a plain string such as <code>"left"</code> also reports a clear error instead of being silently ignored.</li>
<li><code>legend-background</code> now inherits the base <code>rect</code> element, so <code>theme(rect: ...)</code> cascades to it as it does for every other <code>*-background</code> surface.</li>
<li>A hoisted <code>compose()</code> legend that leaves no room for the panels, or a left/right legend that would overrun the caption, now reports a clear layout error instead of silently overprinting.</li>
</ul>
<p>A second group cleans up statistics and scale behaviour.</p>
<ul>
<li><code>stat-bin-2d</code> and <code>stat-bin-hex</code> <code>_density</code> is now the cell’s fraction of the total count, matching ggplot2, instead of an undocumented count-per-cell-area value.</li>
<li><code>sqrt</code> and <code>log10</code> transforms validate their domain and report clear errors for out-of-range data. The <code>sqrt</code> inverse also clamps padded view bounds at zero so the axis stays monotone and shows the <code>0</code> break.</li>
<li>The out-of-range filter now respects scale <code>expand</code>, so points and annotations inside the expansion headroom are kept. Reversed <code>limits</code> (a flipped axis) no longer drop every row.</li>
<li><code>geom-linerange()</code> honours its <code>alpha</code> parameter, which was always documented but never applied.</li>
</ul>
<div class="highlight">
<p><strong>Most of the legend fixes are invisible at a glance:</strong> existing plots look the same, but the cases that used to produce overlapping keys, misaligned titles, or silent layout glitches now behave correctly.</p>
</div>
</section>
<section id="editor-support-tinymist-docstrings" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="editor-support-tinymist-docstrings"><span class="header-section-number">7</span> Editor support: Tinymist docstrings</h2>
<p>The published package now ships Tinymist-friendly docstrings. Hovering over a gribouille function in a compatible editor shows formatted parameters, return values, and examples, instead of the raw <code>@</code>-tag comments. Variadic sinks such as <code>guides()</code> and <code>theme()</code> list their accepted keys explicitly, so you can see which aesthetics or element surfaces each function takes without opening the reference.</p>
</section>
<section id="wrap-up" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="wrap-up"><span class="header-section-number">8</span> Wrap-up</h2>
<div class="highlight">
<p><strong>Guide control moved one level up: hide ticks and legends with one argument, no theme surgery needed.</strong></p>
</div>
<p>Next on the list is more geoms and worked examples. If you run into something unexpected, the issue tracker is the right place for it.</p>
<ul>
<li>Gribouille
<ul>
<li>Repository: <a href="https://github.com/mcanouil/gribouille" class="uri">https://github.com/mcanouil/gribouille</a>.</li>
<li>Documentation: <a href="https://m.canouil.dev/gribouille" class="uri">https://m.canouil.dev/gribouille</a>.</li>
<li>Typst Universe: <a href="https://typst.app/universe/package/gribouille" class="uri">https://typst.app/universe/package/gribouille</a>.</li>
</ul></li>
<li>Typst Render
<ul>
<li>Repository: <a href="https://github.com/mcanouil/quarto-typst-render" class="uri">https://github.com/mcanouil/quarto-typst-render</a>.</li>
<li>Documentation: <a href="https://m.canouil.dev/quarto-typst-render" class="uri">https://m.canouil.dev/quarto-typst-render</a>.</li>
</ul></li>
</ul>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>A note on contributions
</div>
</div>
<div class="callout-body-container callout-body">
<p>Gribouille is an unfunded spare-time project, and the API is still settling. Bug reports and ideas are very welcome on the issue tracker. Pull requests are not being accepted for now, for the reasons set out in the <a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/index.html">launch post</a>. Thanks in advance for your patience.</p>
</div>
</div>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Gribouille 0.3.0: {Finer} {Guide} {Control} and a {Themed}
    {Compose}},
  date = {2026-06-15},
  url = {https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-06-15). Gribouille 0.3.0: Finer Guide Control and a
Themed Compose. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/">https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/</a>
</div></div></section></div> ]]></description>
  <category>typst</category>
  <category>quarto</category>
  <category>grammar-of-graphics</category>
  <category>gribouille</category>
  <guid>https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/</guid>
  <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-06-15-gribouille-0-3/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Why Quarto Stuck: Four Years from a Biostatistician’s Desk</title>
  <link>https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<p>After four years of using Quarto every day, I want to explain why it stuck as my go-to for reports, slides, websites, and books. Before Quarto, that work was spread across R Markdown, <code>bookdown</code>, <code>xaringan</code>, and <code>distill</code>. Quarto folded all of that into one command line tool, and I have not looked back since the early days before v1.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="A dark poster with the title &quot;Why Quarto Stuck&quot; above the line &quot;from R
Markdown to one CLI&quot;, set over a softly glowing horizontal timeline with
nodes for the years 2022 to 2026, the final 2026 node picked out in
coral.
" width="600"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>At a Glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Four years of daily Quarto use.</li>
<li>Close to 14,000 contributions on <code>quarto-dev/quarto-cli</code> across discussions, issues, and pull requests.</li>
<li>One CLI replaced a fragmented R Markdown toolchain for reports, slides, websites, and books.</li>
<li>The extension system turned Quarto into a platform: more than 40 extensions built and maintained.</li>
<li>Typst for PDF: fast compilation, readable programming model, no more LaTeX friction.</li>
<li><a href="https://github.com/mcanouil/gribouille">Gribouille</a>: a grammar of graphics in pure Typst, so plots compile where the document does.</li>
</ul>
</div>
</div>
<section id="it-started-by-helping-out" class="level2">
<h2 class="anchored" data-anchor-id="it-started-by-helping-out">It Started by Helping Out</h2>
<p>My time with Quarto began before v1: back-and-forth between the issue/discussion tracker and real document use in my research on the genetics of type 2 diabetes and obesity, when the CLI was still finding its shape. I still spend time in the <a href="https://github.com/quarto-dev/quarto-cli"><code>quarto-dev/quarto-cli</code></a> repository, answering questions, helping people troubleshoot, surfacing gaps in the documentation, and guiding the same R Markdown and <code>bookdown</code> migration I once made myself.</p>
<p><em>The figure below (Figure&nbsp;1) traces that involvement month by month, split across discussions, issues, and pull requests, and it is itself drawn with <a href="https://github.com/mcanouil/gribouille">Gribouille</a> through the <a href="https://github.com/mcanouil/quarto-typst-render"><code>quarto-typst-render</code></a> extension.</em><sup>1</sup></p>
<div id="fig-activity" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-activity-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-10-why-quarto-stuck/./assets/typst-render/quarto-cli-activity-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figure&nbsp;1: Monthly contributions to quarto-dev/quarto-cli from mid-2022 to mid-2026, split across discussions, issues, and pull requests."><img src="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/assets/typst-render/quarto-cli-activity-light.svg" class="img-fluid figure-img" alt="Stacked area chart of my monthly activity on quarto-dev/quarto-cli from mid-2022 to mid-2026, split by thread type into discussions, issues, and pull requests. Discussions are the largest band throughout, issues second, pull requests a thin top band; activity climbs through 2023, peaks across 2023 and 2024, then eases, with the final months of 2026 partial."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-10-why-quarto-stuck/./assets/typst-render/quarto-cli-activity-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Figure&nbsp;1: Monthly contributions to quarto-dev/quarto-cli from mid-2022 to mid-2026, split across discussions, issues, and pull requests."><img src="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/assets/typst-render/quarto-cli-activity-dark.svg" class="img-fluid figure-img" alt="Stacked area chart of my monthly activity on quarto-dev/quarto-cli from mid-2022 to mid-2026, split by thread type into discussions, issues, and pull requests. Discussions are the largest band throughout, issues second, pull requests a thin top band; activity climbs through 2023, peaks across 2023 and 2024, then eases, with the final months of 2026 partial."></a></p>
</div>
</div>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-activity-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: Monthly contributions to <code>quarto-dev/quarto-cli</code> from mid-2022 to mid-2026, split across discussions, issues, and pull requests.
</figcaption>
</figure>
</div>
<p>The totals still catch me off guard. Close to 14,000 contributions in all: comments, replies, pull requests, issues, and discussions across four years.</p>
<div class="highlight">
<p><strong>Close to 14,000 contributions</strong> across discussions, issues, and pull requests: that is a lot of people who were stuck and then were not.</p>
</div>
<p>Working through those threads deepened my understanding of what Quarto does well and where it falls short, which shaped how I use it for my own work today.</p>
</section>
<section id="from-r-markdown-to-one-cli" class="level2">
<h2 class="anchored" data-anchor-id="from-r-markdown-to-one-cli">From R Markdown to One CLI</h2>
<p>The first thing that won me over was the scope of a single tool. With R Markdown I kept a small mental map of which package produced which output. With Quarto I write <code>quarto render</code>, and the same project gives me an HTML report, a PDF through Typst, a Reveal.js deck, a website, or a book. The fragmented toolchain became one CLI, and that alone changed how I work.</p>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-10-why-quarto-stuck/./assets/typst-render/one-source-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="A diagram: a single source document, a .qmd file, with an arrow pointing to five output labels, HTML, PDF, Reveal.js, Website, and Book."><img src="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/assets/typst-render/one-source-light.svg" class="img-fluid" alt="A diagram: a single source document, a .qmd file, with an arrow pointing to five output labels, HTML, PDF, Reveal.js, Website, and Book."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-10-why-quarto-stuck/./assets/typst-render/one-source-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="A diagram: a single source document, a .qmd file, with an arrow pointing to five output labels, HTML, PDF, Reveal.js, Website, and Book."><img src="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/assets/typst-render/one-source-dark.svg" class="img-fluid" alt="A diagram: a single source document, a .qmd file, with an arrow pointing to five output labels, HTML, PDF, Reveal.js, Website, and Book."></a></p>
</div>
</div>
</div>
<p>The defaults are also less surprising. Format options live under a single <code>format:</code> key, so an HTML and a PDF variant of the same document sit side by side instead of fighting each other. Even the format names are more intuitive: <code>html</code>, <code>pdf</code>, <code>revealjs</code>, <code>website</code>, and <code>book</code> are easier to remember than the R Markdown equivalents (<em>also way less typing</em>), and they are consistent across formats, so I do not have to remember that <code>html_document</code> is for HTML but <code>pdf_document</code> is for PDF. A project/directory-level <code>_metadata.yml</code> sets shared options once, and conditional content with <code>{.content-visible when-format="html"}</code> reads far more clearly than the output checks I used to scatter through R Markdown code cells. None of this is magic, it is just less surprising, and after years of small surprises that matters.</p>
<p>Multi-language support is also native in Quarto. I move between R and Python in the same workflow, even if not yet in the same document (<em>I have strong hopes for <strong>Quarto 2</strong> ongoing developments</em>).</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>R and Python still live in separate documents within a project, not in one shared session. For my work that boundary has never been a real problem. When both languages were needed, I relied on <code>reticulate</code> to call Python from R.</p>
</div>
</div>
<p>The look and feel out of the box is, in my opinion, simply better, especially for HTML. And because R is no longer mandatory, writing notes or plain prose is a first-class use case, not an afterthought. That is also what makes Quarto easy to recommend: I have helped colleagues and clients move from R and <code>bookdown</code>, without asking them to change how they think.</p>
</section>
<section id="building-my-own" class="level2">
<h2 class="anchored" data-anchor-id="building-my-own">Building My Own</h2>
<p>If one feature made Quarto really stick, it is the extension system. An extension adds shortcodes, filters, formats, or whole project types without forking or hacking the core. That single idea turned Quarto from a better R Markdown<sup>2</sup> into a platform I build on.</p>
<p>I know this because I keep building on it with tools that began as solutions to my own needs but that I share in the spirit of open source.</p>
<ol class="qx-timeline">
  <li class="qx-more"><span class="qx-more-inner"><span class="qx-when">2022 Q3</span><a href="https://github.com/mcanouil/quarto-revealjs-storybook">Storybook</a>, <a href="https://github.com/mcanouil/quarto-revealjs-coeos">Coeos</a>, <a href="https://github.com/mcanouil/quarto-iconify">Iconify</a>, <a href="https://github.com/mcanouil/quarto-letter">Letter</a>, <a href="https://github.com/mcanouil/quarto-animate">Animate</a>, and <a href="https://github.com/mcanouil/quarto-elevator">Elevator</a>, my first handful while learning the format.</span></li>
  <li class="qx-more"><span class="qx-more-inner"><span class="qx-when">2022 Q4</span><a href="https://github.com/mcanouil/quarto-masonry">Masonry</a>.</span></li>
  <li class="qx-more"><span class="qx-more-inner"><span class="qx-when">2023 Q1</span><a href="https://github.com/mcanouil/quarto-lua-env">Lua Env</a> and <a href="https://github.com/mcanouil/quarto-revealjs-spotlight">Spotlight</a>.</span></li>
  <li class="qx-card qx-left"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-preview-colour">Preview Colour</a> shows an inline swatch next to a hex, RGB, or HSL code. <span class="qx-extra">Also <a href="https://github.com/mcanouil/quarto-badge">Version Badge</a>.</span></div><div class="qx-tick">2023 Q2</div></li>
  <li class="qx-card qx-right"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-invoice">Invoice</a> is a Typst template I actually send to clients.</div><div class="qx-tick">2023 Q4</div></li>
  <li class="qx-card qx-left"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-highlight-text">Highlight Text</a> highlights text across HTML, Typst, Reveal.js, and Docx.</div><div class="qx-tick">2024 Q2</div></li>
  <li class="qx-more"><span class="qx-more-inner"><span class="qx-when">2024 Q3</span><a href="https://github.com/mcanouil/quarto-github">GitHub</a>, later replaced by Git Link.</span></li>
  <li class="qx-card qx-right"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a> manages extensions from VS Code and Positron.</div><div class="qx-tick">2024 Q4</div></li>
  <li class="qx-more"><span class="qx-more-inner"><span class="qx-when">2025 Q1</span><a href="https://github.com/mcanouil/quarto-div-reuse">Div Reuse</a> and <a href="https://github.com/mcanouil/quarto-language-cell-decorator">Language Cell Decorator</a>.</span></li>
  <li class="qx-card qx-left"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-mcanouil">quarto-mcanouil</a> brings <code>brand.yml</code> theming to HTML, Typst, and Reveal.js, and <a href="https://github.com/mcanouil/quarto-gitlink">Git Link</a> turns issue, pull request, and commit references into links. <span class="qx-extra">Also <a href="https://github.com/mcanouil/quarto-external">External</a>, <a href="https://github.com/mcanouil/quarto-modal">Modal</a>, and <a href="https://github.com/mcanouil/quarto-toc-depth">TOC Depth</a>.</span></div><div class="qx-tick">2025 Q3</div></li>
  <li class="qx-card qx-right"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-extensions-updater">Extensions Updater</a> updates a project's extensions automatically, like Dependabot. <span class="qx-extra">Also <a href="https://github.com/mcanouil/quarto-collapse-output">Collapse Output</a>, <a href="https://github.com/mcanouil/quarto-remember">Remember</a>, <a href="https://github.com/mcanouil/quarto-revealjs-tabset">Tabset</a>, and <a href="https://github.com/mcanouil/quarto-offcanvas">Offcanvas</a>.</span></div><div class="qx-tick">2025 Q4</div></li>
  <li class="qx-card qx-left"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-code-window">Code Window</a> dresses code blocks as macOS or Windows windows, and <a href="https://github.com/mcanouil/quarto-typst-render">Typst Render</a> compiles Typst blocks to images for any format. <span class="qx-extra">Also <a href="https://github.com/mcanouil/quarto-revealjs-a11y">A11y</a>.</span></div><div class="qx-tick">2026 Q1</div></li>
  <li class="qx-card qx-right"><div class="qx-box"><a href="https://github.com/mcanouil/quarto-prism">Prism</a> attaches format-specific values to a single Div, Span, or CodeBlock. <span class="qx-extra">Also <a href="https://github.com/mcanouil/quarto-revealjs-fragmention">Fragmention</a>, <a href="https://github.com/mcanouil/quarto-revealjs-cascade">Cascade</a>, and <a href="https://github.com/mcanouil/quarto-revealjs-codefrag">Code Annotation Fragments</a>.</span></div><div class="qx-tick">2026 Q2</div></li>
</ol>
<div class="highlight">
<p><strong>The extension system is the real unlock:</strong> it let me shape the authoring experience around my work instead of waiting for the core to grow a feature.</p>
</div>
<p>The same mechanism scales up. Embedding a Quarto format extension inside an R package gives a large organisation branded, client-ready HTML, PDF, and Reveal.js with its corporate identity baked in. My needs as a biostatistician and consultant drove most of this, and some of it the community adopted. <a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a> manages extensions from VS Code and Positron, and the <a href="https://github.com/mcanouil/quarto-extensions">Quarto Extensions</a> directory lists more than 300 of them through the GitHub API. I also maintain that directory as a community resource that stands apart from the core team, so anyone can find an extension without knowing it exists first. I have written before about the Wizard (<a href="../../posts/2025-10-20-quarto-wizard-1-0-0/index.html">1.0.0</a> and <a href="../../posts/2026-01-12-quarto-wizard-2-0-0/index.html">2.0.0</a>), about <a href="../../posts/2025-12-12-quarto-extensions-updater/index.html">keeping extensions updated</a>, and about <a href="../../posts/2025-11-06-quarto-extensions-lua/index.html">the Lua behind Quarto extensions</a>.</p>
</section>
<section id="then-came-typst" class="level2">
<h2 class="anchored" data-anchor-id="then-came-typst">Then Came Typst</h2>
<p>Building all of this kept pulling me toward one format in particular, and that format is Typst. It has become my most recent rabbit hole, both standalone with <code>typst compile</code> and through Quarto’s native <code>typst</code> output. For PDF it is a genuine step up from LaTeX: compilation is fast, the programming model is readable, and layout control is far more direct. I no longer dread a page that needs precise placement.</p>
<p>Out of that work came <a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/index.html">Gribouille</a>, a grammar of graphics written in pure Typst, so I can draw a plot where the document is compiled rather than importing a rendered image. The chart above (Figure&nbsp;1), the one tracing my time on <code>quarto-cli</code>, is one of its outputs. The non-obvious lesson, for me, was how little I missed LaTeX once the small frictions were gone. The other was that Typst’s documentation is remarkably easy to search: the model is consistent enough that the right page turns up before I have finished typing the query. My tutorials on building Typst templates for Quarto (<a href="../../posts/2026-02-27-typst-template-tutorial-part1/index.html">Part 1</a> and <a href="../../posts/2026-03-05-typst-template-tutorial-part2/index.html">Part 2</a>) document those experiments, from <a href="../../posts/2026-01-19-typst-document-dispatcher/index.html">document dispatching</a> to <a href="../../posts/2026-06-03-gribouille-0-2/index.html">the Gribouille releases</a>, and most recently <a href="../../posts/2026-05-28-typst-linkedin-carousels/index.html">building LinkedIn carousels with Typst</a>, which showed that the same source-to-output loop works for social content as well as technical documents.</p>
</section>
<section id="four-years-on" class="level2">
<h2 class="anchored" data-anchor-id="four-years-on">Four Years On</h2>
<p>Quarto stuck for a plain reason: it stopped getting in my way. What began as answering other people’s questions turned into building my own tools, and then into a new way to make PDFs altogether. Four years on, that is still why I open it every day.</p>
<p>There is also something coming I’m excited about, namely Quarto 2, a ground-up rewrite in Rust: faster compilation, a tree-sitter parser with real error messages, and a collaborative editor built on Automerge. I cannot wait, even though backwards compatibility is one of the main goals, some breaking changes are likely to occur in favour of a better experience, and I am sure it will be worth it.</p>
<p>Happy publishing!</p>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>The counts come from my own <a href="https://github.com/mcanouil/quarto-cli-activity"><code>quarto-cli-activity</code></a> data for <code>quarto-dev/quarto-cli</code>.↩︎</p></li>
<li id="fn2"><p>To be fair, R Markdown supports templates and filters, but to me they always felt like afterthoughts, not first-class features.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Why {Quarto} {Stuck:} {Four} {Years} from a
    {Biostatistician’s} {Desk}},
  date = {2026-06-10},
  url = {https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-06-10). Why Quarto Stuck: Four Years from a
Biostatistician’s Desk. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/">https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>typst</category>
  <category>extensions</category>
  <category>rmarkdown</category>
  <guid>https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/</guid>
  <pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-06-10-why-quarto-stuck/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Gribouille 0.2.0 and 0.2.1: Composing Plots and a Sturdier Core</title>
  <link>https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Featured card for the Gribouille 0.2.1 release on a cream paper
background. On the left, the Gribouille wordmark sits above the tagline
&quot;Create elegant graphics with the Grammar of Graphics for Typst.&quot;, a
small orange &quot;v0.2.1 release&quot; pill, and the URL
m.canouil.dev/gribouille. On the right, a slightly rotated bordered card
frames a scatter plot titled &quot;Penguins Dataset&quot; of Palmer Penguin body
mass against flipper length, the three species shown as shaded point
clouds with short callout labels.
" width="600"></a></p>
</figure>
</div>
<section id="introduction" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introduction"><span class="header-section-number">1</span> Introduction</h2>
<p><a href="https://github.com/mcanouil/gribouille">Gribouille</a> shipped its <a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/index.html">first version</a> a fortnight ago. This is the first feature update: 0.2.0 landed first, and 0.2.1 followed shortly after with <code>align-panels</code> and two more fixes, so this post covers the 0.2 update as a whole. It has two faces. On the surface, <a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> learned to number, nest, title, share a legend across panels, and line those panels up across the grid, <code>facet-grid()</code> got free scales, and labels and guides became easier to control. Underneath, most of the time went into the internals, turning a long list of panics into clear errors and correct output.</p>
<p>Every figure in this post is a real, freshly compiled plot.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>At a glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><a href="https://github.com/mcanouil/gribouille">Gribouille</a> 0.2.1 on Typst Universe: <code>#import "@preview/gribouille:0.2.1": *</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> can tag panels (<code>A</code>, <code>1</code>, <code>i</code>, …), nest into another <code>compose()</code>, carry its own <code>labs</code>, control the shared legend through <code>guides</code>, and size itself with <code>width</code>/<code>height</code> and relative <code>widths</code>/<code>heights</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose(align-panels: true)</code></a> shares panel margins grid-wise, so the plot areas line up across rows and columns even when their axis labels differ in width (0.2.1).</li>
<li><a href="https://m.canouil.dev/gribouille/reference/facets/facet-grid.html"><code>facet-grid(scales: ...)</code></a> supports <code>"free_x"</code>, <code>"free_y"</code>, and <code>"free"</code>, matching <code>ggplot2</code>.</li>
<li>A layer’s <code>data</code> accepts a function applied to the plot data, for per-layer subsets or transforms without a second dataset.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/guides/guides.html"><code>guides()</code></a> gains a <code>default</code> entry, a fallback inherited by every aesthetic without its own override.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/labs/labs.html"><code>labs()</code></a> fields default to <code>auto</code>; pass <code>none</code> to drop an axis or legend title and reclaim the space it reserved.</li>
<li>A long internals pass: many operations that used to panic now report a clear error or handle the edge, <code>after-scale</code>/<code>stage</code> channels resolve correctly, and <code>geom-segment()</code>/<code>geom-curve()</code>/<code>geom-ribbon()</code>/<code>geom-area()</code> draw on discrete scales instead of nothing (0.2.1).</li>
</ul>
</div>
</div>
<p>Every <code>{typst}</code> block below pulls the library in through a one-line preamble, <code>#import "@preview/gribouille:0.2.1": *</code>, exactly as the <a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/index.html">launch post</a> explained.</p>
</section>
<section id="compose-grew-up" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="compose-grew-up"><span class="header-section-number">2</span> Compose grew up</h2>
<p>The launch already had <a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a>: pass a few deferred plots, get them laid out on a grid with their shared legend hoisted to the edge. This release turns it into a small layout language of its own, close in spirit to <code>patchwork</code>.</p>
<p>Panels can be numbered with <code>tag-levels</code>, the composition can carry its own title through <code>labs</code>, and the shared legend’s side is set once with a <code>default</code> guide.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-1" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-1-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-5" class="code-annotation-target">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-6">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-1-7" class="code-annotation-target">  defer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-9"></span>
<span id="annotated-cell-1-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>compose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-1-11">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-12">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bill Length"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-13">  columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-1-14" class="code-annotation-target">  tag-levels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-15">  tag-prefix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"("</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-16">  tag-suffix<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">")"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-17">  tag-corner<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"top-right"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-1-18" class="code-annotation-target">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bottom"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-1-19" class="code-annotation-target">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Two Views, One Shared Legend"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-1-20">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">18cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-21">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-22"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="5" data-code-annotation="1"><code>x: none</code> and <code>y: none</code> drop the per-panel axis titles and give the space back to the data.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="7" data-code-annotation="2"><code>defer: true</code> returns a spec instead of drawing, so <code>compose()</code> can place it.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="14" data-code-annotation="3"><code>tag-levels: "1"</code> numbers the panels in order; <code>"A"</code>, <code>"a"</code>, <code>"I"</code>, and <code>"i"</code> give the other styles.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="18" data-code-annotation="4">The <code>default</code> guide sets the legend side once, and every aesthetic without its own guide inherits it.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="19" data-code-annotation="5">A composition-level <code>labs()</code> writes one title above the whole figure.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/compose-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Two side-by-side scatter plots tagged (1) Body Mass and (2) Bill Length against flipper length, points coloured by species, with a single shared species legend below both panels and a title across the top reading 'Two Views, One Shared Legend'."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/compose-light.svg" class="img-fluid hero-art" alt="Two side-by-side scatter plots tagged (1) Body Mass and (2) Bill Length against flipper length, points coloured by species, with a single shared species legend below both panels and a title across the top reading 'Two Views, One Shared Legend'."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/compose-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Two side-by-side scatter plots tagged (1) Body Mass and (2) Bill Length against flipper length, points coloured by species, with a single shared species legend below both panels and a title across the top reading 'Two Views, One Shared Legend'."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/compose-dark.svg" class="img-fluid hero-art" alt="Two side-by-side scatter plots tagged (1) Body Mass and (2) Bill Length against flipper length, points coloured by species, with a single shared species legend below both panels and a title across the top reading 'Two Views, One Shared Legend'."></a></p>
</div>
</div>
</div>
</div>
<p>Three things here are new. <code>tag-levels</code>, with <code>tag-prefix</code>, <code>tag-suffix</code>, and <code>tag-corner</code>, labels each panel and styles the tag through a new <code>plot-tag</code> theme element. A composition now accepts its own <code>labs</code> (title, subtitle, caption) and <code>alt</code> text, so the figure reads as one unit. The shared legend is steered through <code>guides</code> instead of the old <code>guides-placement</code> argument, which is removed.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Nesting
</div>
</div>
<div class="callout-body-container callout-body">
<p>A <code>compose()</code> call also accepts <code>defer: true</code>, so it can become a panel inside another <code>compose()</code>. A per-depth <code>tag-levels</code> array then continues the numbering down the tree, giving tags such as <code>B.1</code> and <code>B.2</code>.</p>
</div>
</div>
<p>The 0.2.1 release adds one more control and tidies the tags. By default each panel keeps its own margins, so when one panel carries wide axis labels and another narrow ones, their plot areas start at different offsets and never quite line up. <code>align-panels: true</code> shares the margins grid-wise, left and right per column, top and bottom per row, so the data areas align across the grid the way <code>patchwork</code> and <code>cowplot</code> do. The panel tags also reserve their own band now, sitting above each panel instead of overlapping the data.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="annotated-cell-2-5">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-6">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-2-7">  defer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-9"></span>
<span id="annotated-cell-2-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>compose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-11">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-12">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bill Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-13">  columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-14" class="code-annotation-target">  align-panels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-15" class="code-annotation-target">  tag-levels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-16">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bottom"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="annotated-cell-2-17">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-18">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">14cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-19"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="14" data-code-annotation="1"><code>align-panels: true</code> makes the two plot areas share a left edge, even though the four-digit body-mass labels are wider than the two-digit bill-length labels.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="15" data-code-annotation="2">The tags now reserve their own band above each panel instead of overlapping the data.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/align-panels-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Two stacked scatter panels tagged (a) and (b): body mass with four-digit y labels above bill length with two-digit y labels, both against flipper length and coloured by species. With align-panels on, the two plot areas share a left edge despite the different label widths, and the tags sit in a band above each panel rather than over the data. A shared species legend runs along the bottom."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/align-panels-light.svg" class="img-fluid hero-art" alt="Two stacked scatter panels tagged (a) and (b): body mass with four-digit y labels above bill length with two-digit y labels, both against flipper length and coloured by species. With align-panels on, the two plot areas share a left edge despite the different label widths, and the tags sit in a band above each panel rather than over the data. A shared species legend runs along the bottom."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/align-panels-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Two stacked scatter panels tagged (a) and (b): body mass with four-digit y labels above bill length with two-digit y labels, both against flipper length and coloured by species. With align-panels on, the two plot areas share a left edge despite the different label widths, and the tags sit in a band above each panel rather than over the data. A shared species legend runs along the bottom."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/align-panels-dark.svg" class="img-fluid hero-art" alt="Two stacked scatter panels tagged (a) and (b): body mass with four-digit y labels above bill length with two-digit y labels, both against flipper length and coloured by species. With align-panels on, the two plot areas share a left edge despite the different label widths, and the tags sit in a band above each panel rather than over the data. A shared species legend runs along the bottom."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="free-the-facet-scales" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="free-the-facet-scales"><span class="header-section-number">3</span> Free the facet scales</h2>
<p><a href="https://m.canouil.dev/gribouille/reference/facets/facet-grid.html"><code>facet-grid()</code></a> used to share one pair of axes across every panel. It now takes a <code>scales</code> argument, matching <code>ggplot2</code>: <code>"free_x"</code> frees the x-axis per column, <code>"free_y"</code> frees the y-axis per row, and <code>"free"</code> frees both. Non-positional scales such as colour stay shared, and the panels keep an equal size.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb1-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb1-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb1-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb1-5">  facet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> facet-grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>rows<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> scales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"free_y"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb1-6">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb1-7">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb1-8">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">14cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb1-9"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/facet-free-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Three stacked panels, one per species (Adelie, Chinstrap, Gentoo), each a scatter of body mass against flipper length with its own y-axis range, sharing the colour scale."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/facet-free-light.svg" class="img-fluid hero-art" alt="Three stacked panels, one per species (Adelie, Chinstrap, Gentoo), each a scatter of body mass against flipper length with its own y-axis range, sharing the colour scale."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/facet-free-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Three stacked panels, one per species (Adelie, Chinstrap, Gentoo), each a scatter of body mass against flipper length with its own y-axis range, sharing the colour scale."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/facet-free-dark.svg" class="img-fluid hero-art" alt="Three stacked panels, one per species (Adelie, Chinstrap, Gentoo), each a scatter of body mass against flipper length with its own y-axis range, sharing the colour scale."></a></p>
</div>
</div>
</div>
<p>Each row now picks its own y-range, so a group sitting in a narrow band no longer wastes most of its panel on empty space.</p>
</section>
<section id="highlight-a-subset-without-a-second-dataset" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="highlight-a-subset-without-a-second-dataset"><span class="header-section-number">4</span> Highlight a subset without a second dataset</h2>
<p>A layer’s <code>data</code> now takes a function as well as a frame. The function receives the plot data and returns the rows that layer should draw. So one dataset can feed a faint base layer and a sharp highlight on top, with no second table to keep in step.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-4" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-4-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-4-5">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.35</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-6">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-7" class="code-annotation-target">      data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> rows <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=&gt;</span> rows<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>filter<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>r <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=&gt;</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-4-8">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">mass</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> r<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-4-9">        mass <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> and mass <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5500</span></span>
<span id="annotated-cell-4-10">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}),</span></span>
<span id="annotated-cell-4-11">      size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-12">      colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D55E00"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-13">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-14">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-15">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-4-16">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"One Dataset, One Layer Highlighted"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-17">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-18">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-19">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-4-20">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="annotated-cell-4-21">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-22">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-4-23"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="7" data-code-annotation="1">The function gets the plot data as an array of rows and returns the subset to draw, here the penguins at 5,500 grams and above.</span>
</dd>
</dl>
</div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/layer-data-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="Penguin scatter of body mass against flipper length, every point faint, with the heaviest individuals at 5,500 grams and above re-drawn as larger orange markers."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/layer-data-light.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length, every point faint, with the heaviest individuals at 5,500 grams and above re-drawn as larger orange markers."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/layer-data-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="Penguin scatter of body mass against flipper length, every point faint, with the heaviest individuals at 5,500 grams and above re-drawn as larger orange markers."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/layer-data-dark.svg" class="img-fluid hero-art" alt="Penguin scatter of body mass against flipper length, every point faint, with the heaviest individuals at 5,500 grams and above re-drawn as larger orange markers."></a></p>
</div>
</div>
</div>
</div>
<p>The same trick covers per-layer transforms, not just filtering. Return a reshaped frame and that layer draws it, while the rest of the plot keeps the original data.</p>
</section>
<section id="tidier-labels-and-guides" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="tidier-labels-and-guides"><span class="header-section-number">5</span> Tidier labels and guides</h2>
<p>Two smaller changes remove a recurring annoyance.</p>
<p>First, every <a href="https://m.canouil.dev/gribouille/reference/labs/labs.html"><code>labs()</code></a> field defaults to <code>auto</code>. Passing <code>none</code> now drops an axis or legend title and reclaims the margin it reserved, instead of leaving an empty gap. <a href="https://m.canouil.dev/gribouille/reference/themes/element-blank.html"><code>element-blank()</code></a> on a text surface does the same from the theme side.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb2-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb2-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb2-5">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb2-6">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Axis Titles Off, Space Reclaimed"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-7">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-8">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-9">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb2-10">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb2-11">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-12">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-13"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
<div>
<div class="light-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/labs-none-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-10" title="A scatter of body mass against flipper length with no axis titles, the plot area reaching closer to the edges, under a single bold title."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/labs-none-light.svg" class="img-fluid hero-art" alt="A scatter of body mass against flipper length with no axis titles, the plot area reaching closer to the edges, under a single bold title."></a></p>
</div>
</div>
<div class="dark-content">
<div style="text-align: center;">
<p><a href="../../posts/2026-06-03-gribouille-0-2/./assets/typst-render/labs-none-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-11" title="A scatter of body mass against flipper length with no axis titles, the plot area reaching closer to the edges, under a single bold title."><img src="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/assets/typst-render/labs-none-dark.svg" class="img-fluid hero-art" alt="A scatter of body mass against flipper length with no axis titles, the plot area reaching closer to the edges, under a single bold title."></a></p>
</div>
</div>
</div>
<p>Second, <a href="https://m.canouil.dev/gribouille/reference/guides/guides.html"><code>guides()</code></a> takes a <code>default</code> entry. It holds the fallback guide options, the legend side most often, and any aesthetic without its own guide inherits from it. You saw it above feeding the shared legend in <code>compose()</code>; it works the same on a single <code>plot()</code>. Legend labels also honour the <code>legend-text</code> alignment now, and <a href="https://m.canouil.dev/gribouille/reference/themes/element-text.html"><code>element-text()</code></a>/<a href="https://m.canouil.dev/gribouille/reference/themes/element-typst.html"><code>element-typst()</code></a> gained an <code>align</code> argument and a working <code>angle</code>, so tick labels and titles rotate as asked.</p>
</section>
<section id="the-heavy-lifting-was-underneath" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="the-heavy-lifting-was-underneath"><span class="header-section-number">6</span> The heavy lifting was underneath</h2>
<p>The list above is the visible part. The larger share of this release is a pass over the internals, and it is the kind of work that does not show up in a screenshot.</p>
<p>A whole class of inputs that used to crash the compile now behaves. A <code>geom-col()</code> on a continuous axis with a single distinct value, an empty <code>values</code> array in a <code>scale-*-manual()</code>, a non-positive <code>bins</code>, a sparse contour grid, or <code>width: auto</code> on an unbounded page: each of these used to panic, and each now either draws or reports a clear, actionable error.</p>
<div class="highlight">
<p><strong>Most of the 0.2 work is invisible:</strong> the figures look the same, but far fewer inputs make the compiler give up.</p>
</div>
<p>A second group is about getting the right answer rather than not crashing.</p>
<ul>
<li><code>after-scale</code> and <code>stage</code> channels now train on the marker’s source column, so grouped geoms such as <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-smooth.html"><code>geom-smooth()</code></a>, and <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-errorbar.html"><code>geom-errorbar()</code></a> or <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-rug.html"><code>geom-rug()</code></a>, resolve a mapped colour instead of panicking.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/coord/coord-cartesian.html"><code>coord-cartesian(xlim:, ylim:)</code></a> zooms the axes instead of being ignored, and <a href="https://m.canouil.dev/gribouille/reference/coord/coord-flip.html"><code>coord-flip(reverse: true)</code></a> keeps a <code>log10</code> or <code>sqrt</code> transform.</li>
<li>A column mapped to both a positional and a grouping aesthetic, for example <code>aes(x: "species", fill: "species")</code>, now resolves the grouping across aggregating stats instead of drawing every mark in the ink colour with an empty guide.</li>
<li>Number formatters round correctly, so <code>0.9999995</code> no longer renders as <code>0.1</code>, and continuous scales honour an explicit <code>breaks</code>.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/geoms/geom-segment.html"><code>geom-segment()</code></a>, <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-curve.html"><code>geom-curve()</code></a>, <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-ribbon.html"><code>geom-ribbon()</code></a>, and <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-area.html"><code>geom-area()</code></a> draw on a discrete scale instead of silently rendering nothing (0.2.1).</li>
</ul>
<p>A third group, and a big one, is about layout space: what a plot reserves, and what it reclaims when it is not needed. The <code>labs(none)</code> and <code>element-blank()</code> controls shown above are the visible handles, but most of the work was below them, deciding how much room each part of a figure should take.</p>
<ul>
<li>A standalone plot no longer keeps a fixed empty margin on its top and right edges; the panel grows into that space.</li>
<li>A missing axis title reclaims the band it would have reserved, on the x-axis side as well as the y-axis side.</li>
<li><code>width</code> and <code>height</code> now bound the whole image, title, subtitle, caption, and background padding included, so the data panel shrinks to fit and long titles wrap instead of overflowing.</li>
<li>Legends reserve the room a label actually needs, measuring multi-line and wider-than-usual labels across swatches, size ladders, and colourbars, so nothing clips or overlaps.</li>
</ul>
<p>None of this changes the API. Documents that compiled before still compile, and the plots that were already correct look the same. What changed is the long tail of inputs that did not.</p>
</section>
<section id="typst-render-moved-too" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="typst-render-moved-too"><span class="header-section-number">7</span> Typst Render moved too</h2>
<p>The figures here are compiled by the <a href="https://github.com/mcanouil/quarto-typst-render"><code>quarto-typst-render</code></a> extension, which had its own run of releases since the launch post. Three are worth knowing about for a documentation-heavy project.</p>
<ul>
<li><code>code-fold</code> and <code>code-summary</code> collapse the echoed Typst source into a <code>&lt;details&gt;</code> block, so a long example does not push the figure off the screen.</li>
<li>Quarto code annotations now work on echoed Typst source: the <code>// &lt;1&gt;</code> markers and the numbered list under the <code>compose()</code> block above are exactly that feature.</li>
<li><code>output-source</code> writes the compiled Typst source, preamble and colour bindings included, next to each saved image, which is handy when you want to reproduce a figure outside Quarto.</li>
</ul>
</section>
<section id="wrap-up" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="wrap-up"><span class="header-section-number">8</span> Wrap-up</h2>
<div class="highlight">
<p><strong>A composition language on top, a sturdier core underneath.</strong></p>
</div>
<p>Next on the list is more geoms, a wider set of worked examples, and feedback from the people writing Typst documents day to day.</p>
<ul>
<li>Gribouille
<ul>
<li>Repository: <a href="https://github.com/mcanouil/gribouille" class="uri">https://github.com/mcanouil/gribouille</a>.</li>
<li>Documentation: <a href="https://m.canouil.dev/gribouille" class="uri">https://m.canouil.dev/gribouille</a>.</li>
<li>Typst Universe: <a href="https://typst.app/universe/package/gribouille" class="uri">https://typst.app/universe/package/gribouille</a>.</li>
</ul></li>
<li>Typst Render
<ul>
<li>Repository: <a href="https://github.com/mcanouil/quarto-typst-render" class="uri">https://github.com/mcanouil/quarto-typst-render</a>.</li>
<li>Documentation: <a href="https://m.canouil.dev/quarto-typst-render" class="uri">https://m.canouil.dev/quarto-typst-render</a>.</li>
</ul></li>
</ul>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>A note on contributions
</div>
</div>
<div class="callout-body-container callout-body">
<p>Gribouille is an unfunded spare-time project, and the API is still settling. Bug reports and ideas are very welcome on the issue tracker. Pull requests are not being accepted for now, for the reasons set out in the launch post. Thanks in advance for your patience.</p>
</div>
</div>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Gribouille 0.2.0 and 0.2.1: {Composing} {Plots} and a
    {Sturdier} {Core}},
  date = {2026-06-03},
  url = {https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-06-03). Gribouille 0.2.0 and 0.2.1: Composing Plots
and a Sturdier Core. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/">https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/</a>
</div></div></section></div> ]]></description>
  <category>typst</category>
  <category>quarto</category>
  <category>grammar-of-graphics</category>
  <category>gribouille</category>
  <guid>https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/</guid>
  <pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-06-03-gribouille-0-2/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Build LinkedIn Carousels with Typst: the slide Layout</title>
  <link>https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Wide social card on a pale grey-green background with a slim emerald
rail down the left edge. A ruled monospace kicker reads &quot;TYPST ·
TUTORIAL&quot; above large display type that spells &quot;Build LinkedIn Carousels
with Typst&quot;, the word &quot;Typst&quot; in emerald, then a smaller line &quot;The Slide
Layout&quot;. Stacked rounded cards form a motif on the right.
" width="600"></a></p>
</figure>
</div>
<section id="introduction" class="level2">
<h2 class="anchored" data-anchor-id="introduction">Introduction</h2>
<p>Since January 2026 I have made the <a href="https://www.linkedin.com/in/mickaelcanouil/recent-activity/all/">carousels for my LinkedIn posts</a> with Typst. It has worked well, so here is how I do it.</p>
<p>A LinkedIn carousel is not a special thing. It is a multi-page document that the feed turns into swipeable cards, and you upload it as a single PDF. So if you can make a multi-page PDF, you can make a carousel.</p>
<p><a href="https://typst.app">Typst</a> is a fine tool for exactly that. You describe each page in plain text, compile to PDF in a fraction of a second, and keep the source under version control like any other code. No design tool, no manual export, no moving boxes around by hand.</p>
<p>This post shows the one helper I use to make this easy: a small <code>slide</code> helper that gives every page a full-bleed background with consistent padding. Every slide shown below is a real Typst compilation, rendered to SVG by the <a href="https://github.com/mcanouil/quarto-typst-render"><code>Typst Render</code> Quarto extension</a>, so you see the code and its result side by side.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>At a glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>This deck uses a square 1:1 page, the format LinkedIn shows carousels in.</li>
<li>Set the page margin to <code>0cm</code> so backgrounds reach every edge.</li>
<li>A <code>slide</code> layout adds a rail, a page number, and the padding to every card.</li>
<li>Compile with <code>typst compile</code>, then upload the PDF or export PNG frames.</li>
</ul>
</div>
</div>
</section>
<section id="setting-up-the-page" class="level2">
<h2 class="anchored" data-anchor-id="setting-up-the-page">Setting up the page</h2>
<p>Start by setting the page geometry once, at the top of the file.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#set</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Carousel"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> author<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Your Name"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#set</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">text</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>lang<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"en"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb1-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#set</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">page</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">21cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">21cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> margin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
<p>The 21cm by 21cm size is a square 1:1. The <a href="https://www.linkedin.com/pulse/linkedin-image-size-guide-2026-jan-van-musscher-wslwe/">LinkedIn image size guide</a> recommends carousels as a square 1080 by 1080, or a 4:5 portrait. The exact dimensions do not matter, only the ratio does, so any square pair works.</p>
<p>The important line is <code>margin: 0cm</code>.</p>
<div class="highlight">
<p><strong>Zero page margin is the trick:</strong> the background reaches every edge, and the <code>slide</code> helper adds the padding back inside.</p>
</div>
<p>Carousel slides want colour and imagery that reach every edge, and a page margin would fence the content into a smaller box with white gutters. Removing the margin gives you the full page, and you add the padding back yourself. That is what the <code>slide</code> helper does.</p>
</section>
<section id="the-slide-layout" class="level2">
<h2 class="anchored" data-anchor-id="the-slide-layout">The <code>slide</code> layout</h2>
<p>A <code>slide</code> helper can do more than add padding. It can also draw the parts that repeat on every card. The layout below draws a full-height accent rail down the left edge, adds a page number in the top-right corner, then lays out your content in the space that is left. Here it is, defined and then called with an empty body so you can see the rail and the padded area where <code>body</code> goes.</p>
<div class="cell">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="annotated-cell-2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">slide</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> paper<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> body<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> page<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-2" class="code-annotation-target">  place<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>left <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> top<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> rect<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.55cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> accent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-2-3" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-4">    place<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-5">      top <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.4cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.4cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-6">      text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mono<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">14pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink-soft<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-7">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-8">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-2-9" class="code-annotation-target">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-10">    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>left: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.6cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> right: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.7cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.7cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-11">    body</span>
<span id="annotated-cell-2-12">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-2-13" class="code-annotation-target"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span>
<span id="annotated-cell-2-14"></span>
<span id="annotated-cell-2-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">({</span></span>
<span id="annotated-cell-2-16">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-2-17">    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-18">    stroke<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>paint: ink-soft<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> thickness: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> dash: <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dashed"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-2-19">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-20">    align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>center <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> horizon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mono<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink-soft<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body goes here"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="annotated-cell-2-21">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-22"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<div class="cell-annotation">
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="1" data-code-annotation="1">Start a fresh page and paint its background with <code>fill</code>, so each call is one card in the carousel.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="2" data-code-annotation="2">Draw the rail: a thin rectangle pinned to the left edge, the full height of the page. With <code>margin: 0cm</code> it reaches the top, bottom, and side.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="3" data-code-annotation="3">Add a small page number like <code>2 / 3</code> in the top-right, but only when an <code>index</code> is passed.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="9,10,11,12" data-code-annotation="4">Add back the padding that <code>margin: 0cm</code> removed, with a wider left inset so the content clears the rail.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="13" data-code-annotation="5"><code>body</code> is a trailing content argument, so the helper is called as <code>#slide({ ... })</code> with the card’s content in the braces.</span>
</dd>
</dl>
</div>
<p><a href="../../posts/2026-05-28-typst-linkedin-carousels/./assets/typst-render/layout.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="An otherwise empty slide showing what the layout draws on its own: the emerald rail down the left edge and a dashed outline marking the padded content area where the body is placed."><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/assets/typst-render/layout.svg" class="img-fluid hero-art w-50 mx-auto d-block" alt="An otherwise empty slide showing what the layout draws on its own: the emerald rail down the left edge and a dashed outline marking the padded content area where the body is placed."></a></p>
</div>
<p>The helper is optional: a bare <code>#page(...)</code> would work too. Defining <code>slide</code> once is what keeps the rail, the page number, and the padding identical on every card, and every call site short.</p>
<div class="highlight">
<p><strong>Define the layout once, and every card matches</strong> without repeating the setup.</p>
</div>
<p>The page geometry, a small palette, the fonts, this <code>slide</code> layout, and a ruled <code>kicker</code> label are the file’s setup. In this post they live in a shared preamble, so each live example below shows only the card it draws.</p>
</section>
<section id="slides-live" class="level2">
<h2 class="anchored" data-anchor-id="slides-live">Slides, live</h2>
<p>The cover card passes an <code>index</code>, adds a <code>kicker</code> at the top, and centres the headline. The rail and the page number come from the layout.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1 / 3"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-2">  kicker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Typst · Carousel"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-3">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-4">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">16cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-5">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> display<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">54pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Build decks"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-6">    linebreak<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="cb2-7">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> display<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">54pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> accent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"from text"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-8">    v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.4cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-9">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">20pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink-soft<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"One layout, used on every card."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-10">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span>
<span id="cb2-11">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb2-12"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span></code></pre></div></div>
<p><a href="../../posts/2026-05-28-typst-linkedin-carousels/./assets/typst-render/cover.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Cover card with an emerald rail down the left edge, a '1 / 3' index top-right, a ruled 'TYPST · CAROUSEL' kicker, and a large two-line headline 'Build decks from text'."><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/assets/typst-render/cover.svg" class="img-fluid hero-art w-50 mx-auto d-block" alt="Cover card with an emerald rail down the left edge, a '1 / 3' index top-right, a ruled 'TYPST · CAROUSEL' kicker, and a large two-line headline 'Build decks from text'."></a></p>
<p>A content card stacks a few numbered rows with a <code>grid</code> to keep the numbers and text aligned.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"2 / 3"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-2">  kicker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Why Typst"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-3">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-4">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb3-5">    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">16cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-6">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb3-7">      font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> display<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">32pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-8">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Three reasons it fits carousels."</span></span>
<span id="cb3-9">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-10">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-11">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-12"></span>
<span id="cb3-13">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">row</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> t<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb3-14">    columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.3cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-15">    align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>left <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> horizon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> left <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> horizon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-16">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mono<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">22pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> accent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> str<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>n<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="cb3-17">    block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">15cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">18pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> t<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="cb3-18">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-19"></span>
<span id="cb3-20">  stack<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb3-21">    dir<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ttb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-22">    spacing<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.7cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-23">    row<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Plain text in, PDF out. Diff it, version it, regenerate it."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-24">    row<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"One layout sets the look of every page at once."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-25">    row<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No design tool, no manual export, no pixel pushing."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-26">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-27"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span></code></pre></div></div>
<p><a href="../../posts/2026-05-28-typst-linkedin-carousels/./assets/typst-render/content.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Content card with the rail, a '2 / 3' index, the heading 'Three reasons it fits carousels.', and a numbered list of three short benefits."><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/assets/typst-render/content.svg" class="img-fluid hero-art w-50 mx-auto d-block" alt="Content card with the rail, a '2 / 3' index, the heading 'Three reasons it fits carousels.', and a numbered list of three short benefits."></a></p>
<p>The closing card changes the background with the <code>fill</code> parameter. The rail and the page number stay the same.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>index<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"3 / 3"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> ink<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb4-2">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-3">  kicker<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Ship it"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-4">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-5">  text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb4-6">    font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> display<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">46pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> paper<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-7">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Compile and post."</span></span>
<span id="cb4-8">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-9">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-10">  text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb4-11">    font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mono<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">17pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#8fa49d"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb4-12">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typst compile carousel.typ carousel.pdf"</span></span>
<span id="cb4-13">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-14">  v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-15"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">})</span></span></code></pre></div></div>
<p><a href="../../posts/2026-05-28-typst-linkedin-carousels/./assets/typst-render/cta.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Closing card on a dark green-black background with the rail and a '3 / 3' index, reading 'Compile and post.' above the command to compile the carousel to PDF."><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/assets/typst-render/cta.svg" class="img-fluid hero-art w-50 mx-auto d-block" alt="Closing card on a dark green-black background with the rail and a '3 / 3' index, reading 'Compile and post.' above the command to compile the carousel to PDF."></a></p>
<p>Each card renders as a true square, so what you see is what the feed will show.</p>
</section>
<section id="compile-the-whole-deck" class="level2">
<h2 class="anchored" data-anchor-id="compile-the-whole-deck">Compile the whole deck</h2>
<p>Stack your slides in one <code>carousel.typ</code> file, then compile it. Each <code>#slide(...)</code> becomes one page, and the PDF is the carousel.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Each #slide is one page; the PDF is the carousel</span></span>
<span id="cb5-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">typst</span> compile carousel.typ carousel.pdf</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Typst can also export one PNG per page, no extra tool needed</span></span>
<span id="cb5-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">typst</span> compile carousel.typ <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"carousel-{p}.png"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--ppi</span> 144</span></code></pre></div></div>
<p>Upload <code>carousel.pdf</code> to LinkedIn as a document post, and keep the PNG frames for a preview.</p>
</section>
<section id="want-a-gif-for-another-platform" class="level2">
<h2 class="anchored" data-anchor-id="want-a-gif-for-another-platform">Want a GIF for another platform?</h2>
<p>LinkedIn takes the PDF, but some places want a GIF. Build one from the PNG frames with <a href="https://imagemagick.org">ImageMagick</a>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Assemble the exported PNG frames into a looping GIF</span></span>
<span id="cb6-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">magick</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-delay</span> 250 <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-loop</span> 0 carousel-<span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">*</span>.png carousel.gif</span></code></pre></div></div>
<p><code>-delay 250</code> holds each frame for 2.5 seconds, and <code>-loop 0</code> loops forever.</p>
<p>Here is this post’s deck as a looping GIF.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/carousel/carousel.gif" class="lightbox" data-gallery="quarto-lightbox-gallery-6"><img src="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/assets/carousel/carousel.gif" class="hero-art img-fluid w-50 mx-auto d-block quarto-figure quarto-figure-center figure-img" alt="A looping GIF cycling through three square carousel cards: the cover 'Build decks from text', a 'Three reasons it fits carousels' list, and a dark closing 'Compile and post.' card, each with the emerald rail and a page number."></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>A few design tips
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Keep body text at 14pt or larger so it stays legible at thumbnail size.</li>
<li>Aim for five to seven slides: a cover, the content, and a closing call to action.</li>
<li>Use one visual idea per deck: a palette, a font pairing, a single motif.</li>
<li>Avoid copy-pasting the previous carousel, so each one looks different.</li>
</ul>
</div>
</div>
</section>
<section id="bonus-an-llm-prompt" class="level2">
<h2 class="anchored" data-anchor-id="bonus-an-llm-prompt">Bonus: an LLM prompt</h2>
<p>If you would rather start from a draft, paste the prompt below into your assistant of choice. It encodes the geometry, the <code>slide</code> helper, and the design constraints, so the output compiles with the <code>typst</code> CLI.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="text" style="background: #f1f3f5;"><pre class="sourceCode default code-with-copy"><code class="sourceCode default"><span id="cb7-1">You are designing a LinkedIn carousel as a Typst document.</span>
<span id="cb7-2">Produce a single self-contained carousel.typ that compiles with `typst compile`.</span>
<span id="cb7-3"></span>
<span id="cb7-4">Hard requirements:</span>
<span id="cb7-5"></span>
<span id="cb7-6">- Page geometry: #set page(width: 21cm, height: 21cm, margin: 0cm).</span>
<span id="cb7-7">  This is a square 1:1 (LinkedIn shows carousels at 1080 by 1080); keep the ratio.</span>
<span id="cb7-8">- Define one `slide` layout and use it for every card.</span>
<span id="cb7-9">  Build it from a full-page block with zero page margin and an inset, so the background reaches every edge and the content keeps consistent padding:</span>
<span id="cb7-10">    #let slide(fill: &lt;default-bg&gt;, body) = page(fill: fill, {</span>
<span id="cb7-11">      block(width: 100%, height: 100%, inset: 1.7cm, body)</span>
<span id="cb7-12">    })</span>
<span id="cb7-13">  Add any repeated parts you want on top of that (a header, a page number, ...).</span>
<span id="cb7-14">- Invent a small palette (background, ink, soft ink, one accent) and a font trio (display, body, mono).</span>
<span id="cb7-15">  Prefer legible fonts.</span>
<span id="cb7-16">- Five to seven cards following the arc: cover, then content, then a closing call to action.</span>
<span id="cb7-17">  Use one card with `fill: &lt;accent-or-dark&gt;` to invert the look.</span>
<span id="cb7-18">- Body text at least 14pt; headlines 30 to 56pt, so everything reads at thumbnail size.</span>
<span id="cb7-19">  Use place, grid, stack, and v()/h() for layout.</span>
<span id="cb7-20"></span>
<span id="cb7-21">Then tell me the two `typst compile` commands to export it to PDF and to one PNG per page.</span>
<span id="cb7-22"></span>
<span id="cb7-23">The topic of the carousel is: &lt;describe your announcement here&gt;.</span>
<span id="cb7-24">Layout and style (optional): &lt;describe any layout or look you want, for example a side rail, page numbers, a colour band, a serif or a sans display; leave blank to let you pick a clean design&gt;.</span></code></pre></div></div>
<div class="highlight">
<p><strong>It comes down to one idea:</strong> a single <code>slide</code> helper turns a blank page into a carousel page, and the rest is the content you put on it.</p>
</div>
<p>Now build a deck for your next post. If you make one, tag me on <a href="https://www.linkedin.com/in/mickaelcanouil/">LinkedIn</a> so I can see it.</p>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Build {LinkedIn} {Carousels} with {Typst:} The `Slide`
    {Layout}},
  date = {2026-05-28},
  url = {https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-05-28). Build LinkedIn Carousels with Typst: the
`slide` Layout. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/">https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/</a>
</div></div></section></div> ]]></description>
  <category>typst</category>
  <category>design</category>
  <category>linkedin</category>
  <category>carousel</category>
  <category>productivity</category>
  <guid>https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/</guid>
  <pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-05-28-typst-linkedin-carousels/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Gribouille: a Grammar of Graphics for Typst</title>
  <link>https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Featured card for the Gribouille release on a cream paper background. On
the left, the Gribouille wordmark sits above the tagline &quot;Create elegant
graphics with the Grammar of Graphics for Typst.&quot;, a small orange
&quot;v0.1.0 release&quot; pill, and the URL m.canouil.dev/gribouille. On the
right, a slightly rotated bordered card frames a scatter plot of Palmer
Penguin body mass against flipper length, with three coloured point
clouds for the species and per-species summary labels.
" width="600"></a></p>
</figure>
</div>
<section id="introduction" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introduction"><span class="header-section-number">1</span> Introduction</h2>
<p>Typst has grown from a curious newcomer to a credible alternative for scientific writing, reports, and slide decks. What it still lacked, until today, was a real grammar of graphics: a way to declare a figure with the same vocabulary you would reach for in <a href="https://ggplot2.tidyverse.org"><code>ggplot2</code></a>, then have it draw, lay out, and align with the rest of the document.</p>
<p><a href="https://github.com/mcanouil/gribouille">Gribouille</a> (French for <em>scribble</em>) is the first cut of that library. Version 0.1.0 lands on <a href="https://typst.app/universe/package/gribouille">Typst Universe</a> with this announcement, modelled closely on <code>ggplot2</code>, drawing on top of <a href="https://cetz-package.github.io/"><code>cetz</code></a>, and shipped with a documentation site that is itself a Quarto project rendered by the <a href="https://github.com/mcanouil/quarto-typst-render"><code>quarto-typst-render</code></a> extension. Every figure in this post is a real, freshly compiled plot.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>At a glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><a href="https://github.com/mcanouil/gribouille">Gribouille</a> v0.1.0 on Typst Universe: <code>#import "@preview/gribouille:0.1.0": *</code>.</li>
<li>A broad library of geoms and stats, scales spanning every aesthetic, a range of built-in themes, and full facet support.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> orchestrates several plots into a single figure with a shared, hoisted legend, fed by a new <code>defer: true</code> flag on <a href="https://m.canouil.dev/gribouille/reference/core/plot.html"><code>plot()</code></a>.</li>
<li><a href="https://github.com/mcanouil/quarto-typst-render"><code>quarto-typst-render</code></a> v0.13.1 ships <code>typst_define()</code>, the Python and R helper that streams arbitrary values, scalars, lists, dictionaries, and full data frames alike, straight into Typst code blocks.</li>
</ul>
</div>
</div>
</section>
<section id="why-a-grammar-of-graphics-for-typst" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="why-a-grammar-of-graphics-for-typst"><span class="header-section-number">2</span> Why a grammar of graphics for Typst</h2>
<p>Until now, producing a figure inside a Typst document meant taking a detour through another language. You would leave Typst to call out to <code>ggplot2</code>, <a href="https://matplotlib.org"><code>matplotlib</code></a>, or <a href="https://plotnine.org"><code>plotnine</code></a>, save a PNG or SVG, and import it back. That works, but it splits the toolchain in two: one font system on the page, another inside the figure; one colour palette in the report, another in the chart; one render pass that knows about the layout, another that does not.</p>
<p><code>cetz</code> already gives Typst a robust low-level drawing API, but the declarative layer that turns a dataset, a mapping, and a few layers into a publication-quality figure was missing. That is exactly the gap Gribouille fills.</p>
<div class="highlight">
<p><strong>One toolchain.</strong> Same fonts, same palette, same render pass, from data to PDF.</p>
</div>
<p>The pay-off is a single toolchain. Give a plot an <code>alt:</code> description and Gribouille wraps the result in a Typst <code>figure</code> element with <code>kind: "gribouille-plot"</code>, so you can attach show rules, captions, or cross-references in one place; with or without it, every plot shares the document’s fonts, palette, and geometry. The YAML front-matter of this very post wires its <code>background:</code> and <code>foreground:</code> to the site’s light and dark colours, and every figure below reacts automatically when you toggle the theme. Compilation is deterministic, offline-friendly, and produces a PDF straight out of Typst with no Python or R runtime required.</p>
</section>
<section id="a-respectful-nod-to-ggplot2" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="a-respectful-nod-to-ggplot2"><span class="header-section-number">3</span> A respectful nod to ggplot2</h2>
<p>A short personal aside. I have been using <code>ggplot2</code> since almost its first release, and a large part of how I think about data visualisation comes from that work. This release would not exist without Hadley Wickham’s original vision, nor without the people carrying it forward today, in particular Thomas Lin Pedersen and Teun van den Brand, and the wider community of contributors who keep shaping the package through to the recent v4 release.</p>
<p>Gribouille is, in many ways, a port of <code>ggplot2</code>’s vocabulary into Typst. The names line up: <a href="https://m.canouil.dev/gribouille/reference/core/plot.html"><code>plot()</code></a> instead of <code>ggplot()</code>, <code>geom-point()</code> for <code>geom_point()</code>, <code>facet-wrap()</code>, <code>scale-colour-*()</code>, <a href="https://m.canouil.dev/gribouille/reference/themes/theme-minimal.html"><code>theme-minimal()</code></a>, and so on. Where the two part ways, it is because Typst is a different language, with no lazy evaluation, no plot object you can mutate after the fact, and no <code>ggsave()</code> output pipeline. Column names are plain strings. Per-aesthetic type coercion is done inline with <a href="https://m.canouil.dev/gribouille/reference/helpers/as-factor.html"><code>as-factor()</code></a> and <a href="https://m.canouil.dev/gribouille/reference/helpers/as-numeric.html"><code>as-numeric()</code></a>. These are opinionated departures, but the mental model is the same one you already have.</p>
</section>
<section id="build-a-penguins-plot-one-layer-at-a-time" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="build-a-penguins-plot-one-layer-at-a-time"><span class="header-section-number">4</span> Build a penguins plot, one layer at a time</h2>
<p>The library ships the <a href="https://allisonhorst.github.io/palmerpenguins/">Palmer Penguins</a> dataset under the <a href="https://m.canouil.dev/gribouille/reference/datasets/penguins.html"><code>penguins</code></a> symbol. The walk-through below builds a single figure end to end, one grammar concept at a time, with brief notes on alternatives at each step. It is a tighter version of the <a href="https://m.canouil.dev/gribouille/get-started/">Get Started</a> page on the documentation site.</p>
<p>Every plot you build composes the same pieces. <a href="https://m.canouil.dev/gribouille/reference/core/plot.html"><code>plot()</code></a> wraps a stack of layers, each sheet sitting on the one below, from data and mapping at the foundation up through theme and labels.</p>
<div>
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/grammar-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="A plot() container surrounding six stacked parallax sheets representing the grammar layers, from bottom to top: data plus aes(), layer (geom plus stat plus position), scale-*, coord-*, facet-*, and theme plus labs."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/grammar-light.svg" class="img-fluid hero-art" style="display: block; margin-inline: auto;" alt="A plot() container surrounding six stacked parallax sheets representing the grammar layers, from bottom to top: data plus aes(), layer (geom plus stat plus position), scale-*, coord-*, facet-*, and theme plus labs."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/grammar-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="A plot() container surrounding six stacked parallax sheets representing the grammar layers, from bottom to top: data plus aes(), layer (geom plus stat plus position), scale-*, coord-*, facet-*, and theme plus labs."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/grammar-dark.svg" class="img-fluid hero-art" style="display: block; margin-inline: auto;" alt="A plot() container surrounding six stacked parallax sheets representing the grammar layers, from bottom to top: data plus aes(), layer (geom plus stat plus position), scale-*, coord-*, facet-*, and theme plus labs."></a></p>
</div>
</div>
<p>The post’s YAML points the <code>typst-render</code> extension at a one-line preamble file so every <code>{typst}</code> block has the library in scope:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">extensions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst-render</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">preamble</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> _preamble.typ</span></span></code></pre></div></div>
<p>And <code>_preamble.typ</code> contains:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"@preview/gribouille:0.1.0"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span></span></code></pre></div></div>
<p>You would write the same <code>#import</code> once at the top of your own Typst document.</p>
<section id="data-and-aesthetics" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="data-and-aesthetics"><span class="header-section-number">4.1</span> Data and aesthetics</h3>
<p>Every plot starts with a dataset and an <a href="https://m.canouil.dev/gribouille/reference/core/aes.html"><code>aes()</code></a> mapping that binds column names to visual channels. At a minimum, <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-point.html"><code>geom-point()</code></a> needs <code>x</code> and <code>y</code>.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb3-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb3-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb3-5">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-6">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-7"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-1-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title="Scatter of penguin body mass against flipper length, every point in the default theme ink colour, no legend."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-1-light.svg" class="img-fluid hero-art" alt="Scatter of penguin body mass against flipper length, every point in the default theme ink colour, no legend."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-1-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-5" title="Scatter of penguin body mass against flipper length, every point in the default theme ink colour, no legend."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-1-dark.svg" class="img-fluid hero-art" alt="Scatter of penguin body mass against flipper length, every point in the default theme ink colour, no legend."></a></p>
</div>
</div>
</div>
</div>
<p>The plot-level <code>mapping</code> is inherited by every layer. If only one layer needs a given mapping, you can also pass it inside that layer’s <code>mapping:</code> argument and keep the plot-level mapping minimal.</p>
</section>
<section id="encode-a-third-variable-with-colour" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="encode-a-third-variable-with-colour"><span class="header-section-number">4.2</span> Encode a third variable with colour</h3>
<p>Add <code>colour: "species"</code> and the points split visually by group.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb4-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb4-4">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-5">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-6">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb4-8">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb4-9">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-10">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-11"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-2a-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-6" title="Scatter of body mass against flipper length, points coloured by species (Adelie, Chinstrap, Gentoo) with a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-2a-light.svg" class="img-fluid hero-art" alt="Scatter of body mass against flipper length, points coloured by species (Adelie, Chinstrap, Gentoo) with a species legend on the right."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-2a-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-7" title="Scatter of body mass against flipper length, points coloured by species (Adelie, Chinstrap, Gentoo) with a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-2a-dark.svg" class="img-fluid hero-art" alt="Scatter of body mass against flipper length, points coloured by species (Adelie, Chinstrap, Gentoo) with a species legend on the right."></a></p>
</div>
</div>
</div>
</div>
<p>If your figure must remain legible in greyscale, swap <code>colour: "species"</code> for <code>shape: "species"</code> and the same grouping rides the point shape instead. Both aesthetics can be combined when accessibility matters.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb5-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb5-4">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-5">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-6">    shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb5-8">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb5-9">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-10">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-11"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-2b-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-8" title="Scatter of body mass against flipper length, monochrome with point glyphs distinguishing species: open circles for Adelie, triangles for Chinstrap, squares for Gentoo, with a species shape legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-2b-light.svg" class="img-fluid hero-art" alt="Scatter of body mass against flipper length, monochrome with point glyphs distinguishing species: open circles for Adelie, triangles for Chinstrap, squares for Gentoo, with a species shape legend on the right."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-2b-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-9" title="Scatter of body mass against flipper length, monochrome with point glyphs distinguishing species: open circles for Adelie, triangles for Chinstrap, squares for Gentoo, with a species shape legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-2b-dark.svg" class="img-fluid hero-art" alt="Scatter of body mass against flipper length, monochrome with point glyphs distinguishing species: open circles for Adelie, triangles for Chinstrap, squares for Gentoo, with a species shape legend on the right."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="stack-a-second-layer" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="stack-a-second-layer"><span class="header-section-number">4.3</span> Stack a second layer</h3>
<p>Layers stack in the order you list them. Adding <a href="https://m.canouil.dev/gribouille/reference/geoms/geom-smooth.html"><code>geom-smooth()</code></a> draws one fitted line per colour group, because the <code>colour</code> aesthetic is inherited from the plot-level mapping.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Only <code>method: "lm"</code> is supported in this release; LOESS is deferred for the reason listed in the limitations section.</p>
</div>
</div>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb6-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb6-4">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-5">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-6">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-7">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-8">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-9">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb6-10">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-11">    geom-smooth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>method<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lm"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-12">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-13">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-14">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-15"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-3-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-10" title="Per-species scatter coloured by species with one linear fit and confidence ribbon per group, and a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-3-light.svg" class="img-fluid hero-art" alt="Per-species scatter coloured by species with one linear fit and confidence ribbon per group, and a species legend on the right."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-3-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-11" title="Per-species scatter coloured by species with one linear fit and confidence ribbon per group, and a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-3-dark.svg" class="img-fluid hero-art" alt="Per-species scatter coloured by species with one linear fit and confidence ribbon per group, and a species legend on the right."></a></p>
</div>
</div>
</div>
</div>
</section>
<section id="pick-a-palette" class="level3" data-number="4.4">
<h3 data-number="4.4" class="anchored" data-anchor-id="pick-a-palette"><span class="header-section-number">4.4</span> Pick a palette</h3>
<p><a href="https://m.canouil.dev/gribouille/reference/scales/scale-colour-discrete.html"><code>scale-colour-discrete()</code></a> replaces the default palette. A matching <a href="https://m.canouil.dev/gribouille/reference/scales/scale-fill-discrete.html"><code>scale-fill-discrete()</code></a> keeps the confidence ribbons in step with the points.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb7-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cb</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb7-2">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#0072B2"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-3">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D55E00"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-4">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#009E73"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb7-6"></span>
<span id="cb7-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb7-8">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-9">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb7-10">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-11">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-12">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-13">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-14">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-15">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb7-16">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-17">    geom-smooth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>method<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lm"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-18">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-19">  scales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb7-20">    scale-colour-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> cb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-21">    scale-fill-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> cb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-22">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb7-23">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-24">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb7-25"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-4-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-12" title="Same per-species scatter and linear fits as step 3, now using a colour-blind-friendly Okabe-Ito palette (blue, vermillion, bluish green)."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-4-light.svg" class="img-fluid hero-art" alt="Same per-species scatter and linear fits as step 3, now using a colour-blind-friendly Okabe-Ito palette (blue, vermillion, bluish green)."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-4-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-13" title="Same per-species scatter and linear fits as step 3, now using a colour-blind-friendly Okabe-Ito palette (blue, vermillion, bluish green)."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-4-dark.svg" class="img-fluid hero-art" alt="Same per-species scatter and linear fits as step 3, now using a colour-blind-friendly Okabe-Ito palette (blue, vermillion, bluish green)."></a></p>
</div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p><a href="https://m.canouil.dev/gribouille/reference/scales/scale-colour-okabe-ito.html"><code>scale-colour-okabe-ito()</code></a> exposes the same Okabe-Ito palette used in the example but as a one-liner if you do not want to spell out the colour codes.</p>
</div>
</div>
</section>
<section id="split-into-small-multiples" class="level3" data-number="4.5">
<h3 data-number="4.5" class="anchored" data-anchor-id="split-into-small-multiples"><span class="header-section-number">4.5</span> Split into small multiples</h3>
<p><a href="https://m.canouil.dev/gribouille/reference/facets/facet-wrap.html"><code>facet-wrap()</code></a> lays out one panel per level of a discrete column, sharing scales across panels.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cb</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb8-2">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#0072B2"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-3">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D55E00"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-4">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#009E73"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb8-6"></span>
<span id="cb8-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb8-8">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-9">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb8-10">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-11">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-12">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-13">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-14">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-15">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb8-16">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-17">    geom-smooth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>method<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lm"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-18">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-19">  scales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb8-20">    scale-colour-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> cb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-21">    scale-fill-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> cb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-22">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-23">  facet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> facet-wrap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"island"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb8-24">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-25">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb8-26"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-5-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-14" title="Three panels (Torgersen, Biscoe, Dream) arranged on a two-column grid, one per island, sharing colour and axis scales."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-5-light.svg" class="img-fluid hero-art" alt="Three panels (Torgersen, Biscoe, Dream) arranged on a two-column grid, one per island, sharing colour and axis scales."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-5-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-15" title="Three panels (Torgersen, Biscoe, Dream) arranged on a two-column grid, one per island, sharing colour and axis scales."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-5-dark.svg" class="img-fluid hero-art" alt="Three panels (Torgersen, Biscoe, Dream) arranged on a two-column grid, one per island, sharing colour and axis scales."></a></p>
</div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>For two crossed factors, <a href="https://m.canouil.dev/gribouille/reference/facets/facet-grid.html"><code>facet-grid()</code></a> produces a matrix of panels instead.</p>
</div>
</div>
</section>
<section id="polish" class="level3" data-number="4.6">
<h3 data-number="4.6" class="anchored" data-anchor-id="polish"><span class="header-section-number">4.6</span> Polish</h3>
<p>A theme, axis labels, and the figure is ready for the page.</p>
<p><a href="https://m.canouil.dev/gribouille/reference/helpers/format-comma.html"><code>format-comma()</code></a>, passed to <code>scale-y-continuous(labels: ...)</code>, turns <code>5000</code> into <code>5,000</code> on the y-axis. Sibling helpers cover number, percent, currency, scientific notation, and case or wrap formatting.</p>
<p><a href="https://m.canouil.dev/gribouille/reference/themes/element-typst.html"><code>element-typst()</code></a> wires the subtitle to render as Typst markup, so the linear-fit formula <code>$hat(y) = beta_0 + beta_1 dot.c x$</code> and the per-species names appear with maths and inline colour without leaving the string.</p>
<p>A single <code>species-colours</code> dictionary feeds both <a href="https://m.canouil.dev/gribouille/reference/scales/scale-colour-discrete.html"><code>scale-colour-discrete()</code></a> (<code>limits:</code> plus <code>palette:</code>) and the second subtitle line. The entries are mapped to coloured <code>#text(...)</code> snippets, joined into one Typst-markup string.</p>
<p>Legend placement is controlled with <a href="https://m.canouil.dev/gribouille/reference/guides/guides.html"><code>guides()</code></a> bound to <a href="https://m.canouil.dev/gribouille/reference/guides/guide-legend.html"><code>guide-legend(position: ...)</code></a>. <code>position:</code> accepts the side strings <code>"top"</code>, <code>"right"</code>, <code>"bottom"</code>, <code>"left"</code>, a Typst alignment like <code>bottom + right</code> for an inside-panel corner, or a <code>(dx:, dy:)</code> offset for arbitrary placement.</p>
<p>The same constructor exposes <code>direction</code>, <code>byrow</code>, <code>nrow</code>, and <code>ncolumn</code> for swatch layout, and a <code>"top"</code> or <code>"bottom"</code> placement automatically lays a colourbar or size ladder out horizontally. The <code>ncol</code> argument was renamed to <code>ncolumn</code> in the run-up to this release, so older snippets need updating before they compile.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb9-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">species-colours</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-2">  Adelie:    rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#0072B2"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-3">  Chinstrap: rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D55E00"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-4">  Gentoo:    rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#009E73"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb9-6"></span>
<span id="cb9-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-8">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-9">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-10">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-11">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-12">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-13">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-14">    shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-15">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-16">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-17">    geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-18">    geom-smooth<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>method<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"lm"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-19">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-20">  scales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-21">    scale-colour-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-22">      limits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> species-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>keys<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb9-23">      palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> species-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>values<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb9-24">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-25">    scale-fill-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-26">      limits<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> species-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>keys<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb9-27">      palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> species-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>values<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb9-28">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-29">    scale-y-continuous<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>labels<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> format-comma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()),</span></span>
<span id="cb9-30">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-31">  facet<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> facet-wrap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"island"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-32">  guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-33">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> bottom <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-34">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> bottom <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-35">    shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> guide-legend<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>position<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> bottom <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-36">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-37">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-38">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Penguin Body Mass Scales with Flipper Length"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-39">    subtitle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb9-40">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>Linear fit <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$hat(y) = beta_0 + beta_1 dot.c x$</span> on every island for three species: <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb9-41">      species-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>pairs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="cb9-42">        .map<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>p <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=&gt;</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> p<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> p<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)))</span></span>
<span id="cb9-43">        .join<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">", "</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb9-44">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="cb9-45">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-46">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-47">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-48">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-49">    shape<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-50">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-51">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb9-52">    plot-title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> element-text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-53">    plot-subtitle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> element-typst<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-54">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-55">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-56">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">9cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-57"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-6-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-16" title="Polished faceted scatter on a minimal theme with bold title, a subtitle pairing the linear-fit formula with the three species names rendered in their respective colours, thousands-separated y ticks, title-cased axis labels, and the species legend tucked inside the empty bottom-right facet panel."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-6-light.svg" class="img-fluid hero-art" alt="Polished faceted scatter on a minimal theme with bold title, a subtitle pairing the linear-fit formula with the three species names rendered in their respective colours, thousands-separated y ticks, title-cased axis labels, and the species legend tucked inside the empty bottom-right facet panel."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/step-6-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-17" title="Polished faceted scatter on a minimal theme with bold title, a subtitle pairing the linear-fit formula with the three species names rendered in their respective colours, thousands-separated y ticks, title-cased axis labels, and the species legend tucked inside the empty bottom-right facet panel."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/step-6-dark.svg" class="img-fluid hero-art" alt="Polished faceted scatter on a minimal theme with bold title, a subtitle pairing the linear-fit formula with the three species names rendered in their respective colours, thousands-separated y ticks, title-cased axis labels, and the species legend tucked inside the empty bottom-right facet panel."></a></p>
</div>
</div>
</div>
</div>
</section>
</section>
<section id="compose-multiple-plots" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="compose-multiple-plots"><span class="header-section-number">5</span> Compose multiple plots</h2>
<p>Sometimes a story needs more than one panel. <a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> takes several deferred plots, lays them out on a grid or stack, and hoists their shared legend into a single guide block at the edge of the figure. The deferred plots are produced by passing <code>defer: true</code> to <a href="https://m.canouil.dev/gribouille/reference/core/plot.html"><code>plot()</code></a>, which returns a spec dictionary instead of rendering immediately.</p>
<div class="highlight">
<p><strong>Many plots, one legend.</strong> <code>compose()</code> hoists the shared aesthetics for you.</p>
</div>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb10-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb10-2">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> penguins<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-3">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-4">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-point<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> alpha<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb10-5">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">6cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-6">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.5cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-7">  defer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-9"></span>
<span id="cb10-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>compose<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb10-11">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="cb10-12">  panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill-len"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body-mass"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)),</span></span>
<span id="cb10-13">  layout<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"grid"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-14">  columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-15"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/compose-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-18" title="Two side-by-side scatter plots, the left of penguin body mass against flipper length and the right against bill length, points coloured by species (Adelie, Chinstrap, Gentoo), with a single shared species legend on the right of the pair."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/compose-light.svg" class="img-fluid hero-art" alt="Two side-by-side scatter plots, the left of penguin body mass against flipper length and the right against bill length, points coloured by species (Adelie, Chinstrap, Gentoo), with a single shared species legend on the right of the pair."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/compose-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-19" title="Two side-by-side scatter plots, the left of penguin body mass against flipper length and the right against bill length, points coloured by species (Adelie, Chinstrap, Gentoo), with a single shared species legend on the right of the pair."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/compose-dark.svg" class="img-fluid hero-art" alt="Two side-by-side scatter plots, the left of penguin body mass against flipper length and the right against bill length, points coloured by species (Adelie, Chinstrap, Gentoo), with a single shared species legend on the right of the pair."></a></p>
</div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Hoisting knobs
</div>
</div>
<div class="callout-body-container callout-body">
<p>The hoisting is configurable. <code>collect:</code> controls which aesthetics are merged: <code>auto</code> hoists every legend identical across panels, <code>none</code> keeps each panel’s own legend in place, and an array such as <code>("colour",)</code> whitelists specific aesthetics. <code>guides-placement:</code> sits the shared legend on <code>"right"</code>, <code>"left"</code>, <code>"top"</code>, or <code>"bottom"</code>. <code>layout:</code> switches between <code>"grid"</code> (with <code>columns:</code> and <code>gutter:</code>) and <code>"stack"</code> (with <code>direction:</code> such as <code>ttb</code> or <code>ltr</code>).</p>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>You only need <a href="https://m.canouil.dev/gribouille/reference/core/compose.html"><code>compose()</code></a> when you want the shared legend hoisted. A rendered plot is ordinary Typst content, so the base <a href="https://typst.app/docs/reference/layout/grid/"><code>grid</code></a> and <a href="https://typst.app/docs/reference/layout/stack/"><code>stack</code></a> functions arrange several plots just as well when each keeps its own legend.</p>
</div>
</div>
</section>
<section id="drive-gribouille-from-any-computation" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="drive-gribouille-from-any-computation"><span class="header-section-number">6</span> Drive Gribouille from any computation</h2>
<p><code>Typst Render</code> 0.13.1 adds a small but powerful helper called <code>typst_define()</code>, available from both R and Python. It lets a code cell hand any value to every <code>{typst}</code> block in the document under a single Typst dictionary named <code>typst_define</code>. A scalar, a list, a dictionary, a NumPy array, a Polars DataFrame, anything JSON-serialisable, lands in Typst as the corresponding native value.</p>
<div class="highlight">
<p><strong>Compute anywhere, render in Typst.</strong> <code>typst_define()</code> is the bridge.</p>
</div>
<p>The mechanism is deliberately plain. The helper serialises the values you give it to JSON, hex-encodes them, and emits a tiny Pandoc metadata block of the form <code>---\ntypst-define: &lt;hex&gt;\n---\n</code>. The extension’s Lua filter, which runs at the <code>pre-quarto</code> stage, decodes that block and injects a <code>#let typst_define = (...)</code> preamble in front of every Typst compilation. You compute on one side, read <code>typst_define.something</code> on the other, and the two stay in step regardless of which language did the work.</p>
<p>That bridge is what makes the rest of this section possible. The example below uses Python with <a href="https://pola.rs"><code>polars</code></a>, but the same approach works with R (<code>typst_define()</code> ships in both languages), with plain Python collections, with database queries, with REST calls, or with anything else that can produce a value. Python and Polars are one concrete illustration, not a requirement.</p>
<section id="a-python-example" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="a-python-example"><span class="header-section-number">6.1</span> A Python example</h3>
<p>The Python helper has explicit converters for <code>polars.DataFrame</code> and <code>numpy.ndarray</code>, which is what keeps the example short.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Source the helper yourself
</div>
</div>
<div class="callout-body-container callout-body">
<p><code>typst_define()</code> is not auto-injected. Quarto runs the computational engine <em>before</em> any filter or shortcode, so the extension cannot stage the helper into the kernel for you. You must source it explicitly, typically inside a setup code cell marked <code>#| include: false</code>, by extending <code>sys.path</code> (Python) or <code>source()</code>-ing the file (R) from <code>_extensions/mcanouil/typst-render/_resources/</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| include: false</span></span>
<span id="cb11-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os, sys</span>
<span id="cb11-3">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, os.path.join(</span>
<span id="cb11-4">  os.environ.get(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"QUARTO_PROJECT_DIR"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span>),</span>
<span id="cb11-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_extensions"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mcanouil"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typst-render"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_resources"</span>,</span>
<span id="cb11-6">))</span>
<span id="cb11-7"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typst_define <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> typst_define</span></code></pre></div></div>
<p>The code cell below inlines the same bootstrap with <code>#| echo: true</code> so you can read it in context.</p>
</div>
</div>
<div id="3cf60eb7" class="cell" data-execution_count="1">
<div class="code-copy-outer-scaffold"><div class="sourceCode cell-code" id="cb12" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb12-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os</span>
<span id="cb12-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> sys</span>
<span id="cb12-3"></span>
<span id="cb12-4"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> polars <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> pl</span>
<span id="cb12-5"></span>
<span id="cb12-6">project_dir <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.environ.get(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"QUARTO_PROJECT_DIR"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"../.."</span>)</span>
<span id="cb12-7">sys.pycache_prefix <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.path.join(project_dir, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".cache"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pycache"</span>)</span>
<span id="cb12-8">extension_resources <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.path.join(</span>
<span id="cb12-9">  project_dir,</span>
<span id="cb12-10">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_extensions"</span>,</span>
<span id="cb12-11">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mcanouil"</span>,</span>
<span id="cb12-12">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typst-render"</span>,</span>
<span id="cb12-13">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_resources"</span>,</span>
<span id="cb12-14">)</span>
<span id="cb12-15">sys.path.insert(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>, extension_resources)</span>
<span id="cb12-16"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> typst_define <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> typst_define</span>
<span id="cb12-17"></span>
<span id="cb12-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Network read at render time; swap for a local CSV or the `palmerpenguins`</span></span>
<span id="cb12-19"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Python package when working offline.</span></span>
<span id="cb12-20">penguins <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pl.read_csv(</span>
<span id="cb12-21">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"https://raw.githubusercontent.com/allisonhorst/palmerpenguins/main/inst/extdata/penguins.csv"</span>,</span>
<span id="cb12-22">  null_values<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NA"</span>,</span>
<span id="cb12-23">).with_columns(</span>
<span id="cb12-24">  pl.col(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper_length_mm"</span>).cast(pl.Float64),</span>
<span id="cb12-25">  pl.col(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body_mass_g"</span>).cast(pl.Float64),</span>
<span id="cb12-26">)</span>
<span id="cb12-27"></span>
<span id="cb12-28">summary <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> (</span>
<span id="cb12-29">  penguins</span>
<span id="cb12-30">  .drop_nulls([<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper_length_mm"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body_mass_g"</span>])</span>
<span id="cb12-31">  .group_by(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span>)</span>
<span id="cb12-32">  .agg(</span>
<span id="cb12-33">    pl.col(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper_length_mm"</span>).mean().<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>).alias(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"flipper_mean"</span>),</span>
<span id="cb12-34">    pl.col(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body_mass_g"</span>).mean().<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">round</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>).cast(pl.Int64).alias(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body_mass_mean"</span>),</span>
<span id="cb12-35">    pl.<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">len</span>().alias(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"n"</span>),</span>
<span id="cb12-36">  )</span>
<span id="cb12-37">  .sort(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span>)</span>
<span id="cb12-38">)</span>
<span id="cb12-39"></span>
<span id="cb12-40">typst_define(summary<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>summary)</span>
<span id="cb12-41">summary</span></code></pre></div></div>
<div class="cell-output cell-output-display cell-output-markdown">

</div>
<div class="cell-output cell-output-display" data-execution_count="1">
<div><style>
.dataframe > thead > tr,
.dataframe > tbody > tr {
  text-align: right;
  white-space: pre-wrap;
}
</style>
<small>shape: (3, 4)</small>
<table class="dataframe caption-top table table-sm table-striped small" data-border="1">
<thead>
<tr class="header">
<th data-quarto-table-cell-role="th">species</th>
<th data-quarto-table-cell-role="th">flipper_mean</th>
<th data-quarto-table-cell-role="th">body_mass_mean</th>
<th data-quarto-table-cell-role="th">n</th>
</tr>
<tr class="even">
<td>str</td>
<td>f64</td>
<td>i64</td>
<td>u32</td>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>"Adelie"</td>
<td>190.0</td>
<td>3701</td>
<td>151</td>
</tr>
<tr class="even">
<td>"Chinstrap"</td>
<td>195.8</td>
<td>3733</td>
<td>68</td>
</tr>
<tr class="odd">
<td>"Gentoo"</td>
<td>217.2</td>
<td>5076</td>
<td>123</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<p>The trailing <code>summary</code> is there on purpose, so Jupyter prints the DataFrame in the rendered output and the reader can see exactly what crossed the language boundary. The same <code>plot()</code> call you wrote in the previous section now reads its data from <code>typst_define.summary</code> instead of the bundled <code>penguins</code> symbol.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb13-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cb</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb13-2">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#0072B2"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb13-3">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#D55E00"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb13-4">  rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#009E73"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb13-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb13-6"></span>
<span id="cb13-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>plot<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb13-8">  data<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> typst_define<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>summary<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-9">  mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> aes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb13-10">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-11">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"body_mass_mean"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-12">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-13">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb13-14">  layers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>geom-col<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.6</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb13-15">  scales<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>scale-fill-discrete<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>palette<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> cb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),),</span></span>
<span id="cb13-16">  labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> labs<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb13-17">    title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Mean Body Mass per Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-18">    subtitle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Computed in Polars, drawn in Gribouille"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-19">    x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-20">    y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (g)"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-21">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Species"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-22">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb13-23">  theme<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> theme-minimal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(),</span></span>
<span id="cb13-24">  width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">12cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-25">  height<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">7cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb13-26"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="light-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/polars-summary-light.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-20" title="Bar chart of mean body mass per species (Adelie, Chinstrap, Gentoo), three coloured bars on a minimal theme with bold title, sentence-case subtitle 'Computed in Polars, drawn in Gribouille', and a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/polars-summary-light.svg" class="img-fluid hero-art" alt="Bar chart of mean body mass per species (Adelie, Chinstrap, Gentoo), three coloured bars on a minimal theme with bold title, sentence-case subtitle 'Computed in Polars, drawn in Gribouille', and a species legend on the right."></a></p>
</div>
<div class="dark-content">
<p><a href="../../posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/./assets/typst-render/polars-summary-dark.svg" class="lightbox" data-gallery="quarto-lightbox-gallery-21" title="Bar chart of mean body mass per species (Adelie, Chinstrap, Gentoo), three coloured bars on a minimal theme with bold title, sentence-case subtitle 'Computed in Polars, drawn in Gribouille', and a species legend on the right."><img src="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/assets/typst-render/polars-summary-dark.svg" class="img-fluid hero-art" alt="Bar chart of mean body mass per species (Adelie, Chinstrap, Gentoo), three coloured bars on a minimal theme with bold title, sentence-case subtitle 'Computed in Polars, drawn in Gribouille', and a species legend on the right."></a></p>
</div>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Rendering notes
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Set <code>engine: jupyter</code> explicitly in the post YAML so the Python code cells run via Jupyter.</li>
<li>Pin the kernel with <code>jupyter: &lt;name&gt;</code> (this post uses <code>python3</code>, but a dedicated kernel such as <code>gribouille-post</code> makes the env reproducible).</li>
<li>The Python environment needs <code>polars</code> and <a href="https://ipython.org"><code>IPython</code></a> available.</li>
<li>The Lua filter runs before any Quarto rendering, so <code>typst_define</code> is in scope for every <code>{typst}</code> block from that point in the document onward.</li>
</ul>
</div>
</div>
</section>
</section>
<section id="sec-limitations" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="sec-limitations"><span class="header-section-number">7</span> Current limitations</h2>
<p>This first release is deliberately focused. Some <code>ggplot2</code>/<code>plotnine</code> features are out of scope by design; others are pending while the project matures.</p>
<ul>
<li>No <code>geom-violin()</code>, <code>geom-density()</code>, or <code>geom-density-2d()</code>. Kernel density estimation needs numerical machinery that pure Typst cannot run efficiently, and the realistic escape hatch is a WebAssembly helper. That route is on hold for now because I do not yet have enough hands-on WASM experience to build and ship one with confidence, and the help I have had from LLMs has not closed that gap to a level I am comfortable releasing.</li>
<li><code>geom-smooth()</code> and <a href="https://m.canouil.dev/gribouille/reference/stats/stat-smooth.html"><code>stat-smooth()</code></a> support <code>method: "lm"</code> only. LOESS is deferred for the same reason.</li>
<li><a href="https://m.canouil.dev/gribouille/reference/geoms/geom-dotplot.html"><code>geom-dotplot()</code></a> uses histogram-style binning only; data-driven dot-density binning is deferred.</li>
<li>Maps and spatial geometries (<code>ggplot2</code>’s <code>sf</code> integration) are currently out of scope.</li>
</ul>
</section>
<section id="wrap-up" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="wrap-up"><span class="header-section-number">8</span> Wrap-up</h2>
<div class="highlight">
<p><strong>Small, focused, and modelled on the library that has shaped a generation of data visualisation.</strong></p>
</div>
<p>Next on the list is filling in more geoms, more themes, and more worked examples, and chasing feedback from the people writing Typst documents day to day.</p>
<ul>
<li>Gribouille
<ul>
<li>Repository: <a href="https://github.com/mcanouil/gribouille" class="uri">https://github.com/mcanouil/gribouille</a>.</li>
<li>Documentation: <a href="https://m.canouil.dev/gribouille" class="uri">https://m.canouil.dev/gribouille</a>.</li>
<li>Typst Universe: <a href="https://typst.app/universe/package/gribouille" class="uri">https://typst.app/universe/package/gribouille</a>.</li>
</ul></li>
<li>Typst Render
<ul>
<li>Repository: &lt;https:// github.com/mcanouil/quarto-typst-render&gt;.</li>
<li>Documentation: <a href="https://m.canouil.dev/quarto-typst-render" class="uri">https://m.canouil.dev/quarto-typst-render</a>.</li>
</ul></li>
</ul>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>A note on contributions
</div>
</div>
<div class="callout-body-container callout-body">
<p>Gribouille is an unfunded spare-time project, and the API is still settling. Bug reports and ideas are very welcome on the issue tracker. Pull requests are not being accepted for now: the internals shift between releases, every review costs time I have to take from the work that moves the library forward, and I am being especially careful in the current climate of unreviewed LLM-authored patches. Once the surface is stable I will revisit and open the door. Thanks in advance for your patience and your understanding.</p>
</div>
</div>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Gribouille: A {Grammar} of {Graphics} for {Typst}},
  date = {2026-05-20},
  url = {https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-05-20). Gribouille: a Grammar of Graphics for Typst.
<em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/">https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/</a>
</div></div></section></div> ]]></description>
  <category>typst</category>
  <category>quarto</category>
  <category>computation</category>
  <category>grammar-of-graphics</category>
  <category>gribouille</category>
  <guid>https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/</guid>
  <pubDate>Wed, 20 May 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-05-20-gribouille-grammar-of-graphics-for-typst/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Quarto Reveal.js Extensions to Sharpen Your Slides</title>
  <link>https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Dark-background card grid titled &quot;Quarto Reveal.js Extensions&quot; with four
coloured tiles: fragmention in blue, codefrag in teal, cascade in amber,
and tabset in rose, each with a numbered badge and a short description.
" width="600"></a></p>
</figure>
</div>
<section id="introduction" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introduction"><span class="header-section-number">1</span> Introduction</h2>
<p>Reveal.js is a flexible slide engine, but a few rough edges show up again and again when writing slides in Quarto. List fragments need a marker on every item, code annotations stay static, long talks repeat the same heading on continuation slides, and tabsets need a mouse click to advance. Each of the four small extensions below closes one of those gaps, and they compose cleanly on a single slidedeck.</p>
<p>Every section installs one extension, shows the minimal front-matter and body needed to activate it, and embeds a focused slidedeck you can step through with the arrow keys.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Four extensions at a glance
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li><a href="https://github.com/mcanouil/quarto-revealjs-fragmention"><code>quarto-revealjs-fragmention</code></a> hoists <code>.fragment</code> markers onto list items.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-codefrag"><code>quarto-revealjs-codefrag</code></a> turns code annotations into navigable fragments.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-cascade"><code>quarto-revealjs-cascade</code></a> repeats heading chains after slide breaks.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-tabset"><code>quarto-revealjs-tabset</code></a> makes <code>.panel-tabset</code> blocks keyboard and PDF friendly.</li>
</ul>
</div>
</div>
</section>
<section id="fragmention" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="fragmention"><span class="header-section-number">2</span> Fragmention</h2>
<p><code>quarto-revealjs-fragmention</code> lets you animate a whole list item as a single fragment. You place an empty span with the <code>.fragment</code> class (and optional <code>fragment-index</code>) at the start of the item, and the filter hoists those attributes onto the parent <code>&lt;li&gt;</code>, so there is no stray marker left in the rendered slide.</p>
<section id="install" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="install"><span class="header-section-number">2.1</span> Install</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> add mcanouil/quarto-revealjs-fragmention</span></code></pre></div></div>
</section>
<section id="activate-in-the-front-matter" class="level3" data-number="2.2">
<h3 data-number="2.2" class="anchored" data-anchor-id="activate-in-the-front-matter"><span class="header-section-number">2.2</span> Activate in the front-matter</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-startfrom="4" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 3;"><span id="cb2-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb2-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fragmention</span></span></code></pre></div></div>
</section>
<section id="mark-up-your-list" class="level3" data-number="2.3">
<h3 data-number="2.3" class="anchored" data-anchor-id="mark-up-your-list"><span class="header-section-number">2.3</span> Mark up your list</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-shortcodes="false" data-startfrom="8" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown" style="counter-reset: source-line 7;"><span id="cb3-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## A staged list</span></span>
<span id="cb3-9"></span>
<span id="cb3-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment fragment-index="1"} Load the data from disk.</span>
<span id="cb3-11"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment fragment-index="2"} Clean and validate the inputs.</span>
<span id="cb3-12"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment fragment-index="3"} Fit the model.</span>
<span id="cb3-13"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment fragment-index="4"} Report the results.</span>
<span id="cb3-14"></span>
<span id="cb3-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Mixed fragments</span></span>
<span id="cb3-16"></span>
<span id="cb3-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment .fade-in-then-semi-out fragment-index="1"} Step one fades in, then dims.</span>
<span id="cb3-18"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment .highlight-red fragment-index="2"} Step two highlights in red.</span>
<span id="cb3-19"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>A static bullet always visible.</span>
<span id="cb3-20"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>[]{.fragment .grow fragment-index="3"} Step three grows for emphasis.</span></code></pre></div></div>
</section>
<section id="live-demo" class="level3" data-number="2.4">
<h3 data-number="2.4" class="anchored" data-anchor-id="live-demo"><span class="header-section-number">2.4</span> Live demo</h3>
<div style="text-align: center;">
<p><a href="assets/slides/demo-fragmention.html" class="lightbox" title="" data-gallery="quarto-lightbox-gallery-2"><embed src="assets/slides/demo-fragmention.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
</section>
<section id="codefrag" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="codefrag"><span class="header-section-number">3</span> Codefrag</h2>
<p><code>quarto-revealjs-codefrag</code> pairs naturally with Quarto’s <code>code-annotations: select</code> mode. Each <code># &lt;n&gt;</code> marker becomes a Reveal.js fragment, so the matching annotation appears step by step as you press the arrow keys, and the tooltip stays in sync with the current annotation.</p>
<section id="install-1" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="install-1"><span class="header-section-number">3.1</span> Install</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb4-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> add mcanouil/quarto-revealjs-codefrag</span></code></pre></div></div>
</section>
<section id="activate-in-the-front-matter-1" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="activate-in-the-front-matter-1"><span class="header-section-number">3.2</span> Activate in the front-matter</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-startfrom="5" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 4;"><span id="cb5-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">revealjs-plugins</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb5-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> codefrag</span></span></code></pre></div></div>
</section>
<section id="annotate-your-code" class="level3" data-number="3.3">
<h3 data-number="3.3" class="anchored" data-anchor-id="annotate-your-code"><span class="header-section-number">3.3</span> Annotate your code</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-shortcodes="false" data-startfrom="9" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown" style="counter-reset: source-line 8;"><span id="cb6-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Step through an R pipeline</span></span>
<span id="cb6-10"></span>
<span id="cb6-11"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```{.r code-line-numbers="|1|2-3|4-5|6"}</span></span>
<span id="cb6-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(dplyr)                          <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># &lt;1&gt;</span></span>
<span id="cb6-13">mtcars <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb6-14">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filter</span>(cyl <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">6</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>                   <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># &lt;2&gt;</span></span>
<span id="cb6-15">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group_by</span>(gear) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="cb6-16">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">summarise</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">mean_mpg =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">mean</span>(mpg)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span>    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># &lt;3&gt;</span></span>
<span id="cb6-17">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arrange</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">desc</span>(mean_mpg))               <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># &lt;4&gt;</span></span>
<span id="cb6-18"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```</span></span>
<span id="cb6-19"></span>
<span id="cb6-20"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">1. </span>Load the <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`dplyr`</span> package.</span>
<span id="cb6-21"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">2. </span>Keep only six-cylinder cars.</span>
<span id="cb6-22"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">3. </span>Compute the mean mileage per gear.</span>
<span id="cb6-23"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">4. </span>Sort the resulting table.</span></code></pre></div></div>
</section>
<section id="live-demo-1" class="level3" data-number="3.4">
<h3 data-number="3.4" class="anchored" data-anchor-id="live-demo-1"><span class="header-section-number">3.4</span> Live demo</h3>
<div style="text-align: center;">
<p><a href="assets/slides/demo-codefrag.html" class="lightbox" title="" data-gallery="quarto-lightbox-gallery-3"><embed src="assets/slides/demo-codefrag.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
</section>
<section id="cascade" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="cascade"><span class="header-section-number">4</span> Cascade</h2>
<p><code>quarto-revealjs-cascade</code> repeats the current heading chain whenever a <code>---</code> slide break creates a continuation slide. You keep one section heading (and optional subsection) at the top of a topic, then use <code>---</code> to start fresh slides without retyping the title.</p>
<section id="install-2" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="install-2"><span class="header-section-number">4.1</span> Install</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> add mcanouil/quarto-revealjs-cascade</span></code></pre></div></div>
</section>
<section id="activate-in-the-front-matter-2" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="activate-in-the-front-matter-2"><span class="header-section-number">4.2</span> Activate in the front-matter</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-startfrom="4" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 3;"><span id="cb8-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cascade</span></span></code></pre></div></div>
</section>
<section id="chain-your-headings" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="chain-your-headings"><span class="header-section-number">4.3</span> Chain your headings</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-shortcodes="false" data-startfrom="8" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown" style="counter-reset: source-line 7;"><span id="cb9-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Cascade demo</span></span>
<span id="cb9-9"></span>
<span id="cb9-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Results</span></span>
<span id="cb9-11"></span>
<span id="cb9-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Experiment A</span></span>
<span id="cb9-13"></span>
<span id="cb9-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Baseline accuracy: 0.82.</span>
<span id="cb9-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Calibration error: 0.04.</span>
<span id="cb9-16"></span>
<span id="cb9-17">---</span>
<span id="cb9-18"></span>
<span id="cb9-19"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Recall improves with larger batches.</span>
<span id="cb9-20"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Precision plateaus after 10 epochs.</span>
<span id="cb9-21"></span>
<span id="cb9-22">---</span>
<span id="cb9-23"></span>
<span id="cb9-24"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Experiment B</span></span>
<span id="cb9-25"></span>
<span id="cb9-26"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Baseline accuracy: 0.87.</span>
<span id="cb9-27"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Calibration error: 0.03.</span>
<span id="cb9-28"></span>
<span id="cb9-29">---</span>
<span id="cb9-30"></span>
<span id="cb9-31"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Results replicate across three seeds.</span>
<span id="cb9-32"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>No regression on the held-out split.</span></code></pre></div></div>
</section>
<section id="live-demo-2" class="level3" data-number="4.4">
<h3 data-number="4.4" class="anchored" data-anchor-id="live-demo-2"><span class="header-section-number">4.4</span> Live demo</h3>
<div style="text-align: center;">
<p><a href="assets/slides/demo-cascade.html" class="lightbox" title="" data-gallery="quarto-lightbox-gallery-4"><embed src="assets/slides/demo-cascade.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
</section>
<section id="tabset" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="tabset"><span class="header-section-number">5</span> Tabset</h2>
<p><code>quarto-revealjs-tabset</code> turns a <code>.panel-tabset</code> block into a keyboard-driven tabset. Each tab becomes a Reveal.js fragment, so the arrow keys cycle through them, and PDF export lays every tab out on its own page instead of collapsing them to the active one.</p>
<section id="install-3" class="level3" data-number="5.1">
<h3 data-number="5.1" class="anchored" data-anchor-id="install-3"><span class="header-section-number">5.1</span> Install</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb10-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> add mcanouil/quarto-revealjs-tabset</span></code></pre></div></div>
</section>
<section id="activate-in-the-front-matter-3" class="level3" data-number="5.2">
<h3 data-number="5.2" class="anchored" data-anchor-id="activate-in-the-front-matter-3"><span class="header-section-number">5.2</span> Activate in the front-matter</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" data-startfrom="4" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 3;"><span id="cb11-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">revealjs-plugins</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb11-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tabset</span></span></code></pre></div></div>
</section>
<section id="wrap-your-tabs" class="level3" data-number="5.3">
<h3 data-number="5.3" class="anchored" data-anchor-id="wrap-your-tabs"><span class="header-section-number">5.3</span> Wrap your tabs</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" data-shortcodes="false" data-startfrom="8" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown" style="counter-reset: source-line 7;"><span id="cb12-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Dataset overview {.smaller}</span></span>
<span id="cb12-9"></span>
<span id="cb12-10">::: {.panel-tabset}</span>
<span id="cb12-11"></span>
<span id="cb12-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Description</span></span>
<span id="cb12-13"></span>
<span id="cb12-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>1,000 records collected in 2025.</span>
<span id="cb12-15"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Three outcome labels, balanced across classes.</span>
<span id="cb12-16"></span>
<span id="cb12-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Summary</span></span>
<span id="cb12-18"></span>
<span id="cb12-19"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Feature <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Min <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Median <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Max <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb12-20"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|---------|----:|-------:|----:|</span></span>
<span id="cb12-21"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Age     <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span>  18 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span>     42 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span>  91 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb12-22"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> Score   <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span>   0 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span>     57 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span> 100 <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb12-23"></span>
<span id="cb12-24"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Notes</span></span>
<span id="cb12-25"></span>
<span id="cb12-26"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Missing values imputed with the column median.</span>
<span id="cb12-27"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Duplicates removed before splitting.</span>
<span id="cb12-28"></span>
<span id="cb12-29">:::</span></code></pre></div></div>
</section>
<section id="live-demo-3" class="level3" data-number="5.4">
<h3 data-number="5.4" class="anchored" data-anchor-id="live-demo-3"><span class="header-section-number">5.4</span> Live demo</h3>
<div style="text-align: center;">
<p><a href="assets/slides/demo-tabset.html" class="lightbox" title="" data-gallery="quarto-lightbox-gallery-5"><embed src="assets/slides/demo-tabset.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
</section>
<section id="all-four-together" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="all-four-together"><span class="header-section-number">6</span> All four together</h2>
<p>The four extensions compose without fuss on a single slidedeck. You declare the two filters, the two plugins, and <code>code-annotations: select</code>, then use each feature where it fits the story.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" data-startfrom="5" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 4;"><span id="cb13-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb13-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> fragmention</span></span>
<span id="cb13-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> cascade</span></span>
<span id="cb13-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">revealjs-plugins</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb13-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> codefrag</span></span>
<span id="cb13-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> tabset</span></span></code></pre></div></div>
<div style="text-align: center;">
<p><a href="assets/slides/demo-combined.html" class="lightbox" title="" data-gallery="quarto-lightbox-gallery-6"><embed src="assets/slides/demo-combined.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
<section id="wrap-up" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="wrap-up"><span class="header-section-number">7</span> Wrap-up</h2>
<p>Each extension is small, focused, and targets a friction point rather than a whole workflow. Install the ones you need, keep the rest out of your front-matter, and your next slidedeck can lean a little more on the keyboard and a little less on the mouse.</p>
<p>Repositories for the curious:</p>
<ul>
<li><a href="https://github.com/mcanouil/quarto-revealjs-fragmention"><code>quarto-revealjs-fragmention</code></a>.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-codefrag"><code>quarto-revealjs-codefrag</code></a>.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-cascade"><code>quarto-revealjs-cascade</code></a>.</li>
<li><a href="https://github.com/mcanouil/quarto-revealjs-tabset"><code>quarto-revealjs-tabset</code></a>.</li>
</ul>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Reveal.js} {Extensions} to {Sharpen} {Your} {Slides}},
  date = {2026-04-21},
  url = {https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-04-21). Quarto Reveal.js Extensions to Sharpen Your
Slides. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/">https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>reveal.js</category>
  <category>extensions</category>
  <category>presentations</category>
  <guid>https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/</guid>
  <pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-04-21-quarto-revealjs-extensions/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Branded Figures and Tables in R and Python with Quarto</title>
  <link>https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Workflow diagram showing how Quarto's brand feature feeds code-generated
outputs: quarto render sets QUARTO_EXECUTE_INFO, parsed by
get_brand_info(), which feeds configure_brand_fonts(), theme_brand(),
and gt_brand() to produce light and dark figures and tables. The Quarto
logo appears in the top right.
" width="600"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Quarto Version
</div>
</div>
<div class="callout-body-container callout-body">
<p>This post was written and tested with <strong>Quarto CLI 1.9.37</strong>. Some APIs or behaviours may differ in earlier stable releases.</p>
</div>
</div>
<p>Quarto’s <code>brand</code> feature is a convenient way to keep a document’s look consistent across formats: define your colours, fonts, and logos once in a <code>_brand.yml</code> file, and everything from the navbar to the code blocks picks them up automatically. Code-generated outputs, however, are a different story. A <code>ggplot2</code> scatter plot or a <code>gt</code> summary table will cheerfully ignore your brand and render with their own defaults. This post walks through one approach to close that gap: reading the resolved brand configuration at render time and applying it to every figure and table your code produces.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Key Principles
</div>
</div>
<div class="callout-body-container callout-body">
<ul>
<li>Quarto sets <code>QUARTO_EXECUTE_INFO</code> for every code cell; parse it for brand colours, fonts, and palettes.</li>
<li>Register web fonts before any plotting; graphics devices do not see brand-defined fonts by default.</li>
<li>Wrap the styling logic in <code>theme_brand()</code> (figures) and <code>gt_brand()</code> (tables) so every output inherits brand values.</li>
<li>Use <code>renderings: [light, dark]</code> and <code>.light-content</code>/<code>.dark-content</code> to match the reader’s selected theme automatically.</li>
<li>The companion repository <a href="https://github.com/mcanouil/quarto-brand-renderings">mcanouil/quarto-brand-renderings</a> has production-ready implementations with full error handling.</li>
</ul>
</div>
</div>
<section id="the-problem" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="the-problem"><span class="header-section-number">1</span> The Problem</h2>
<p>The goal is to make every visual output match the document’s brand without hardcoding hex values or font names. Here is what branded outputs look like across six different brand configurations, each with light and dark variants:</p>
<section class="featured-carousel-section" aria-label="Branded outputs preview">
  <div class="featured-carousel featured-carousel--media featured-carousel--n6" aria-roledescription="carousel" aria-label="Branded outputs across six brand configurations">
    <div class="featured-track" style="--total: 7;">
      <a class="lightbox" href="assets/_featured-brand-1.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-1.png" alt="Brand 1 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      <a class="lightbox" href="assets/_featured-brand-2.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-2.png" alt="Brand 2 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      <a class="lightbox" href="assets/_featured-brand-3.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-3.png" alt="Brand 3 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      <a class="lightbox" href="assets/_featured-brand-4.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-4.png" alt="Brand 4 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      <a class="lightbox" href="assets/_featured-brand-5.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-5.png" alt="Brand 5 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      <a class="lightbox" href="assets/_featured-brand-6.png" data-gallery="brand-carousel"><img class="lightbox" src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/assets/_featured-brand-6.png" alt="Brand 6 featured image showing light and dark mode branded figures and tables." loading="lazy"></a>
      
    </div>
  </div>
  
</section>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Alternative Approaches
</div>
</div>
<div class="callout-body-container callout-body">
<p>There is no single right way to achieve branded outputs in R and Python. The <a href="https://posit-dev.github.io/brand-yml/"><code>brand.yml</code></a> project provides dedicated <a href="https://posit-dev.github.io/brand-yml/pkg/r/">R</a> and <a href="https://posit-dev.github.io/brand-yml/pkg/py/">Python</a> packages that read a <code>_brand.yml</code> file directly and are the recommended starting point for most projects. At the time of writing, however, the <code>brand.yml</code> packages do not support font registration for graphics devices. You could also write your own wrappers, hard-code a shared palette, or use any other strategy that suits your workflow. This post demonstrates one particular approach: reading the resolved brand data from <code>QUARTO_EXECUTE_INFO</code> at render time, making it independent of external packages and portable across languages, with full font registration support.</p>
</div>
</div>
</section>
<section id="the-quarto_execute_info-approach" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="the-quarto_execute_info-approach"><span class="header-section-number">2</span> The <code>QUARTO_EXECUTE_INFO</code> Approach</h2>
<p>When Quarto renders a document, it sets the <code>QUARTO_EXECUTE_INFO</code> environment variable for every code cell. This variable points to a JSON file containing document metadata, format settings, and, crucially, the resolved brand configuration.</p>
<p>The brand data lives at <code>format.render.brand</code> in the JSON, with separate entries for each colour mode (<code>light</code>, <code>dark</code>). Reading it requires only a JSON parser.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-1-1" class="active tab-pane" aria-labelledby="tabset-1-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-4" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode r"><span id="annotated-cell-4-1">get_brand_info <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-4-2" class="code-annotation-target">  jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fromJSON</span>(</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-3" class="code-annotation-target">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">Sys.getenv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"QUARTO_EXECUTE_INFO"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">unset =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="annotated-cell-4-4">  )</span>
<span id="annotated-cell-4-5">}</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="3" data-code-annotation="1">Read the environment variable set by Quarto.</span>
</dd>
<dt data-target-cell="annotated-cell-4" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="2" data-code-annotation="2">Parse the JSON string into an R list.</span>
</dd>
</dl>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode python"><span id="annotated-cell-5-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> json</span>
<span id="annotated-cell-5-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> os</span>
<span id="annotated-cell-5-3"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pathlib</span>
<span id="annotated-cell-5-4"></span>
<span id="annotated-cell-5-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_brand_info() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">dict</span>:</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-6" class="code-annotation-target">    raw <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> os.environ.get(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"QUARTO_EXECUTE_INFO"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span>)</span>
<span id="annotated-cell-5-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> raw:</span>
<span id="annotated-cell-5-8">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> {}</span>
<span id="annotated-cell-5-9">    path <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pathlib.Path(raw)</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-5-10" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> path.is_file():</span>
<span id="annotated-cell-5-11">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> json.loads(path.read_text())</span>
<span id="annotated-cell-5-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> json.loads(raw)</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="6" data-code-annotation="1">Read the environment variable set by Quarto.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="10" data-code-annotation="2">The value may be a file path or a raw JSON string; check which.</span>
</dd>
</dl>
</div>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Exploring the Structure
</div>
</div>
<div class="callout-body-container callout-body">
<p>During development, print the full JSON to see what is available:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-2-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-1" aria-controls="tabset-2-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-2-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-2-2" aria-controls="tabset-2-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-2-1" class="active tab-pane" aria-labelledby="tabset-2-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r"><span id="cb1-1">info <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_brand_info</span>()</span>
<span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cat</span>(jsonlite<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">toJSON</span>(info[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">pretty =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>))</span></code></pre></div></div>
</div>
<div id="tabset-2-2" class="tab-pane" aria-labelledby="tabset-2-2-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb2-1">info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_info()</span>
<span id="cb2-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">print</span>(json.dumps(info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>], indent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>))</span></code></pre></div></div>
</div>
</div>
</div>
</div>
</div>
</section>
<section id="brand-data-structure" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="brand-data-structure"><span class="header-section-number">3</span> Brand Data Structure</h2>
<p>The brand object under <code>format.render.brand</code> contains one entry per colour mode. Each mode holds a <code>data</code> object with colour and typography definitions.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="json" style="background: #f1f3f5;"><pre class="sourceCode json cw-auto code-with-copy"><code class="sourceCode json"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"light"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-3">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"data"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-4">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"color"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-5">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"palette"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-6">          <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"anchor-blue"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2f5d8a"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-7">          <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"harbour-teal"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#2d8c8f"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-8">          <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"strait-violet"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#5f6fb3"</span></span>
<span id="cb3-9">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="cb3-10">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"foreground"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#4f789e"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-11">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"background"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#f8fafc"</span></span>
<span id="cb3-12">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="cb3-13">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"typography"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-14">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"base"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Chakra Petch"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-15">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"headings"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Great Vibes"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-16">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"monospace"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"JetBrains Mono"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb3-17">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"fonts"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<span id="cb3-18">          <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"family"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Chakra Petch"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"source"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bunny"</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-19">        <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb3-20">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-21">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-22">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="cb3-23">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"dark"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb3-24">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"data"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"..."</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb3-26"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>The key fields are:</p>
<ul>
<li><code>color.palette</code>: named colour values used for data series and accents.</li>
<li><code>color.foreground</code> / <code>color.background</code>: ink and paper colours for the given mode.</li>
<li><code>typography.base</code> / <code>typography.headings</code>: font family names.</li>
<li><code>typography.fonts</code>: font specifications including source (<code>bunny</code>, <code>google</code>, <code>file</code>, or <code>system</code>).</li>
</ul>
<p>The diagrams below summarise how each helper function fits into the rendering pipeline.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-3-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-1" aria-controls="tabset-3-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-3-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-3-2" aria-controls="tabset-3-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-3-1" class="active tab-pane" aria-labelledby="tabset-3-1-tab">
<div id="fig-workflow-r" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-workflow-r-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="../../posts/2026-04-15-quarto-brand-figures-tables/./typst-render/fig-workflow-r.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figure&nbsp;1: R workflow for branded figures and tables."><img src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/typst-render/fig-workflow-r.png" class="img-fluid w-75 mx-auto d-block quarto-figure quarto-figure-center figure-img" alt="Diagram showing the R workflow: quarto render sets QUARTO_EXECUTE_INFO, which is parsed by get_brand_info(). That feeds configure_brand_fonts() using systemfonts, theme_brand() using ggplot2 and scales, and gt_brand() using gt, producing light/dark figures and tables."></a></p>
</figure>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-workflow-r-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: R workflow for branded figures and tables.
</figcaption>
</figure>
</div>
</div>
<div id="tabset-3-2" class="tab-pane" aria-labelledby="tabset-3-2-tab">
<div id="fig-workflow-py" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-workflow-py-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="../../posts/2026-04-15-quarto-brand-figures-tables/./typst-render/fig-workflow-py.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Figure&nbsp;2: Python workflow for branded figures and tables."><img src="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/typst-render/fig-workflow-py.png" class="img-fluid w-75 mx-auto d-block quarto-figure quarto-figure-center figure-img" alt="Diagram showing the Python workflow: quarto render sets QUARTO_EXECUTE_INFO, which is parsed by get_brand_info(). That feeds configure_brand_fonts() using pyfonts and matplotlib, theme_brand() using plotnine, and gt_brand() using great_tables, producing light/dark figures and tables."></a></p>
</figure>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-workflow-py-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;2: Python workflow for branded figures and tables.
</figcaption>
</figure>
</div>
</div>
</div>
</div>
</section>
<section id="dependencies" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="dependencies"><span class="header-section-number">4</span> Dependencies</h2>
<p>This approach relies on a handful of packages for each language.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-4-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-1" aria-controls="tabset-4-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-4-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-4-2" aria-controls="tabset-4-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-4-1" class="active tab-pane" aria-labelledby="tabset-4-1-tab">
<ul>
<li><a href="https://cran.r-project.org/package=jsonlite"><code>jsonlite</code></a>: parse the <code>QUARTO_EXECUTE_INFO</code> JSON.</li>
<li><a href="https://ggplot2.tidyverse.org/"><code>ggplot2</code></a>: the <code>ink</code>, <code>paper</code>, <code>accent</code>, and <code>palette.*</code> theme elements used by <code>theme_brand()</code> are not yet on CRAN.</li>
<li><a href="https://gt.rstudio.com/"><code>gt</code></a>: table styling.</li>
<li><a href="https://scales.r-lib.org/"><code>scales</code></a>: colour blending via <code>col_mix()</code>.</li>
<li><a href="https://systemfonts.r-lib.org/"><code>systemfonts</code></a>: font discovery, registration, and web font embedding for SVG output.</li>
</ul>
</div>
<div id="tabset-4-2" class="tab-pane" aria-labelledby="tabset-4-2-tab">
<ul>
<li><a href="https://plotnine.org/"><code>plotnine</code></a>: <code>ggplot2</code>-style plotting.</li>
<li><a href="https://posit-dev.github.io/great-tables/"><code>great_tables</code></a>: table styling.</li>
<li><a href="https://pola.rs/"><code>polars</code></a>: data manipulation.</li>
<li><a href="https://github.com/y-sunflower/pyfonts"><code>pyfonts</code></a>: font loading from Bunny Fonts and Google Fonts, created by <a href="https://github.com/JosephBARBIERDARNAL">Joseph Barbier</a>. The companion repository uses a development version of <code>pyfonts</code> for improved font subset selection.</li>
</ul>
</div>
</div>
</div>
</section>
<section id="font-registration" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="font-registration"><span class="header-section-number">5</span> Font Registration</h2>
<p>Web fonts defined in a brand configuration are not available to R or Python graphics devices by default. Before creating any plots or tables, a <code>configure_brand_fonts()</code> function registers each font so that graphics devices can find them. Call it once in a setup chunk at the top of your document.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-5-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-1" aria-controls="tabset-5-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-5-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-5-2" aria-controls="tabset-5-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-5-1" class="active tab-pane" aria-labelledby="tabset-5-1-tab">
<p>The <code>systemfonts</code> package handles font discovery and registration.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-8" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode r"><span id="annotated-cell-8-1">configure_brand_fonts <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">brand_mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) {</span>
<span id="annotated-cell-8-2">  info <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_brand_info</span>()</span>
<span id="annotated-cell-8-3">  brand <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> info[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-8-4" class="code-annotation-target">  doc_dir <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dirname</span>(info[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"document-path"</span>]])</span>
<span id="annotated-cell-8-5"></span>
<span id="annotated-cell-8-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (mode <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">intersect</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span>), <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">names</span>(brand))) {</span>
<span id="annotated-cell-8-7">    fonts <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand[[mode]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fonts"</span>]]</span>
<span id="annotated-cell-8-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (ifont <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">seq_len</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">nrow</span>(fonts))) {</span>
<span id="annotated-cell-8-9">      family <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> fonts[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"family"</span>]][[ifont]]</span>
<span id="annotated-cell-8-10">      source <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> fonts[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"source"</span>]][[ifont]]</span>
<span id="annotated-cell-8-11"></span>
<span id="annotated-cell-8-12">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">identical</span>(source, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>)) {</span>
<span id="annotated-cell-8-13">        files <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> fonts[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"files"</span>]][[ifont]]</span>
<span id="annotated-cell-8-14">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (fp <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">as.character</span>(files[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"path"</span>]])) {</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-8-15" class="code-annotation-target">          resolved <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file.path</span>(doc_dir, fp)</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-8-16" class="code-annotation-target">          systemfonts<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">register_font</span>(family, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plain =</span> resolved)</span>
<span id="annotated-cell-8-17">        }</span>
<span id="annotated-cell-8-18">      } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-8-19" class="code-annotation-target">        systemfonts<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require_font</span>(family, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">repositories =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Bunny Fonts"</span>)</span>
<span id="annotated-cell-8-20">      }</span>
<span id="annotated-cell-8-21">    }</span>
<span id="annotated-cell-8-22">  }</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-8-23" class="code-annotation-target">  systemfonts<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reset_font_cache</span>()</span>
<span id="annotated-cell-8-24">}</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-8" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="4" data-code-annotation="1">Resolve the document directory from <code>QUARTO_EXECUTE_INFO</code> for relative font paths.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="15" data-code-annotation="2">Build the full path to each local font file.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="16" data-code-annotation="3">Register local font files with <code>systemfonts::register_font()</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="19" data-code-annotation="4">Download web fonts from Bunny Fonts (or Google Fonts) with <code>systemfonts::require_font()</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="23" data-code-annotation="5">Reset the font cache so newly registered fonts become available.</span>
</dd>
</dl>
</div>
<div id="tabset-5-2" class="tab-pane" aria-labelledby="tabset-5-2-tab">
<p>The <code>matplotlib.font_manager</code> module handles font registration, with <code>pyfonts</code> for downloading web fonts.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-9" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode python"><span id="annotated-cell-9-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> matplotlib.font_manager <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> fm</span>
<span id="annotated-cell-9-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> pyfonts</span>
<span id="annotated-cell-9-3"></span>
<span id="annotated-cell-9-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> configure_brand_fonts() <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">None</span>:</span>
<span id="annotated-cell-9-5">    info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_info()</span>
<span id="annotated-cell-9-6">    brand <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-9-7" class="code-annotation-target">    doc_dir <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pathlib.Path(info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"document-path"</span>]).parent</span>
<span id="annotated-cell-9-8"></span>
<span id="annotated-cell-9-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> mode <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span>]:</span>
<span id="annotated-cell-9-10">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> mode <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> brand:</span>
<span id="annotated-cell-9-11">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">continue</span></span>
<span id="annotated-cell-9-12">        fonts <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand[mode][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fonts"</span>]</span>
<span id="annotated-cell-9-13">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> font_spec <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> fonts:</span>
<span id="annotated-cell-9-14">            family <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> font_spec[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"family"</span>]</span>
<span id="annotated-cell-9-15">            source <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> font_spec[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"source"</span>]</span>
<span id="annotated-cell-9-16"></span>
<span id="annotated-cell-9-17">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> source <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"file"</span>:</span>
<span id="annotated-cell-9-18">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> f <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> font_spec.get(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"files"</span>, []):</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-9-19" class="code-annotation-target">                    resolved <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> doc_dir <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> f[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"path"</span>]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-9-20" class="code-annotation-target">                    fm.fontManager.addfont(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>(resolved))</span>
<span id="annotated-cell-9-21">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elif</span> source <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> (<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bunny"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"google"</span>):</span>
<span id="annotated-cell-9-22">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> source <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bunny"</span>:</span>
<span id="annotated-cell-9-23">                    props <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pyfonts.load_bunny_font(family)</span>
<span id="annotated-cell-9-24">                <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span>:</span>
<span id="annotated-cell-9-25">                    props <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pyfonts.load_google_font(family)</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-9-26" class="code-annotation-target">                fm.fontManager.addfont(props.get_file())</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-9" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="7" data-code-annotation="1">Resolve the document directory for relative font paths.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="19" data-code-annotation="2">Build the full path to each local font file.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="20" data-code-annotation="3">Register local font files with <code>matplotlib.font_manager</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="26" data-code-annotation="4">Download web fonts using <code>pyfonts</code> and register them.</span>
</dd>
</dl>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>SVG Font Rendering
</div>
</div>
<div class="callout-body-container callout-body">
<p>When generating figures as SVG, the SVG files must be inlined directly into the HTML page for fonts to render correctly. An SVG file referenced via an <code>&lt;img&gt;</code> tag is treated as an isolated document by the browser: it cannot access the page’s stylesheets or <code>@import</code> rules, so any <code>@font-face</code> declarations inside the SVG that reference external URLs are silently ignored. Inlining the SVG into the page DOM removes this boundary, allowing the browser to resolve font imports just like any other CSS resource.</p>
<p>The companion repository includes an <code>inline-svglite.lua</code> Lua filter that replaces <code>&lt;img&gt;</code> references with raw SVG content automatically. See the <a href="https://github.com/mcanouil/quarto-brand-renderings">companion repository</a> for the full implementation.</p>
</div>
</div>
</section>
<section id="branded-figures" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="branded-figures"><span class="header-section-number">6</span> Branded Figures</h2>
<p>The <code>theme_brand()</code> function extracts colours and fonts from the brand data and builds a complete <code>ggplot2</code> or plotnine theme. The idea is straightforward: pull out the ink (foreground), paper (background), and palette colours, then pass them to the theme constructor. In R, the latest <code>ggplot2</code> supports <code>ink</code>, <code>paper</code>, and <code>accent</code> parameters directly in <code>theme_minimal()</code>; in Python, each theme element is styled individually.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-6-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-1" aria-controls="tabset-6-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-6-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-6-2" aria-controls="tabset-6-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-6-1" class="active tab-pane" aria-labelledby="tabset-6-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-10" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode r"><span id="annotated-cell-10-1">theme_brand <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">brand_mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) {</span>
<span id="annotated-cell-10-2">  info <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_brand_info</span>()</span>
<span id="annotated-cell-10-3">  brand <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> info[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]]</span>
<span id="annotated-cell-10-4">  brand_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand[[brand_mode]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>]]</span>
<span id="annotated-cell-10-5">  colors <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand_data[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color"</span>]]</span>
<span id="annotated-cell-10-6">  typography <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand_data[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>]]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-10-7" class="code-annotation-target">  palette_values <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unname</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"palette"</span>]]))</span>
<span id="annotated-cell-10-8"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-10-9" class="code-annotation-target">  ink <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foreground"</span>]]</span>
<span id="annotated-cell-10-10">  paper <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background"</span>]]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-10-11" class="code-annotation-target">  accent <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> scales<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_mix</span>(ink, paper, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">amount =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>)</span>
<span id="annotated-cell-10-12"></span>
<span id="annotated-cell-10-13">  ggplot2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(</span>
<span id="annotated-cell-10-14">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> base_size,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-10-15" class="code-annotation-target">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_family =</span> typography[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"base"</span>]],</span>
<span id="annotated-cell-10-16">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">header_family =</span> typography[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"headings"</span>]],</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-10-17" class="code-annotation-target">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">ink =</span> ink,</span>
<span id="annotated-cell-10-18">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">paper =</span> paper,</span>
<span id="annotated-cell-10-19">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">accent =</span> accent</span>
<span id="annotated-cell-10-20">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="annotated-cell-10-21">    ggplot2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(</span>
<span id="annotated-cell-10-22">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot.title =</span> ggplot2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(</span>
<span id="annotated-cell-10-23">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> accent,</span>
<span id="annotated-cell-10-24">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">size =</span> ggplot2<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">rel</span>(<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>)</span>
<span id="annotated-cell-10-25">      ),</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-10-26" class="code-annotation-target">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">palette.colour.discrete =</span> palette_values,</span>
<span id="annotated-cell-10-27">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">palette.fill.discrete =</span> palette_values</span>
<span id="annotated-cell-10-28">    )</span>
<span id="annotated-cell-10-29">}</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-10" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="7" data-code-annotation="1">Flatten the named palette into a character vector.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="9,10" data-code-annotation="2">Extract foreground (ink) and background (paper) from the brand colours.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="11" data-code-annotation="3">Blend ink and paper to create an accent colour for titles and axes.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="15,16" data-code-annotation="4">Apply brand fonts to body text and headings.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="17,18,19" data-code-annotation="5">Pass ink, paper, and accent to <code>theme_minimal()</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="26,27" data-code-annotation="6">Set the discrete colour palette for geoms.</span>
</dd>
</dl>
</div>
<div id="tabset-6-2" class="tab-pane" aria-labelledby="tabset-6-2-tab">
<p>R uses <code>scales::col_mix()</code> to blend colours. Python needs a small helper since there is no equivalent in plotnine:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> _col_mix(a: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, b: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>, amount: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">float</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>:</span>
<span id="cb4-2">    ra, ga, ba <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(a[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(a[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(a[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>)</span>
<span id="cb4-3">    rb, gb, bb <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>), <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(b[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span>:<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">7</span>], <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">16</span>)</span>
<span id="cb4-4">    r <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(ra <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (rb <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ra) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> amount)</span>
<span id="cb4-5">    g <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(ga <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (gb <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ga) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> amount)</span>
<span id="cb4-6">    b_val <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span>(ba <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> (bb <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> ba) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> amount)</span>
<span id="cb4-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">f"#</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span>r<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02x}{</span>g<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02x}{</span>b_val<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:02x}</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span></code></pre></div></div>
<p>Because <code>plotnine</code> does not support <code>palette.*</code> theme elements, a separate helper extracts the colour palette for use with <code>scale_color_manual()</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> get_brand_palette(brand_mode: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>[<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span>]:</span>
<span id="cb5-2">    info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_info()</span>
<span id="cb5-3">    brand <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]</span>
<span id="cb5-4">    palette <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand[brand_mode][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"palette"</span>]</span>
<span id="cb5-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(palette.values())</span></code></pre></div></div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-13" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode python"><span id="annotated-cell-13-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> plotnine <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> (</span>
<span id="annotated-cell-13-2">    element_line, element_rect, element_text, theme, theme_minimal</span>
<span id="annotated-cell-13-3">)</span>
<span id="annotated-cell-13-4"></span>
<span id="annotated-cell-13-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> theme_brand(base_size: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">int</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>, brand_mode: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> theme:</span>
<span id="annotated-cell-13-6">    info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_info()</span>
<span id="annotated-cell-13-7">    brand <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]</span>
<span id="annotated-cell-13-8">    brand_data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand[brand_mode][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>]</span>
<span id="annotated-cell-13-9">    colors <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand_data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color"</span>]</span>
<span id="annotated-cell-13-10">    typography <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand_data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>]</span>
<span id="annotated-cell-13-11"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-13-12" class="code-annotation-target">    ink <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colors[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foreground"</span>]</span>
<span id="annotated-cell-13-13">    paper <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colors[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background"</span>]</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-13-14" class="code-annotation-target">    accent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> _col_mix(ink, paper, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>)</span>
<span id="annotated-cell-13-15">    base_family <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> typography[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"base"</span>]</span>
<span id="annotated-cell-13-16">    heading_family <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> typography[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"headings"</span>]</span>
<span id="annotated-cell-13-17"></span>
<span id="annotated-cell-13-18">    base_line_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> base_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">22</span></span>
<span id="annotated-cell-13-19">    grid_major_colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> _col_mix(ink, paper, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>)</span>
<span id="annotated-cell-13-20">    grid_minor_colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> _col_mix(ink, paper, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.9</span>)</span>
<span id="annotated-cell-13-21"></span>
<span id="annotated-cell-13-22">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> theme_minimal(</span>
<span id="annotated-cell-13-23">        base_size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>base_size,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-13-24" class="code-annotation-target">        base_family<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>base_family</span>
<span id="annotated-cell-13-25">    ) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> theme(</span>
<span id="annotated-cell-13-26">        plot_background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_rect(fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>paper, colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>),</span>
<span id="annotated-cell-13-27">        panel_background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_rect(fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>paper, colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>),</span>
<span id="annotated-cell-13-28">        panel_grid_major<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_line(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>grid_major_colour, size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>base_line_size),</span>
<span id="annotated-cell-13-29">        panel_grid_minor<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_line(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>grid_minor_colour, size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>base_line_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5</span>),</span>
<span id="annotated-cell-13-30">        text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_text(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ink),</span>
<span id="annotated-cell-13-31">        axis_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_text(family<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>heading_family),</span>
<span id="annotated-cell-13-32">        axis_line_x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_line(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent),</span>
<span id="annotated-cell-13-33">        axis_line_y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_line(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent),</span>
<span id="annotated-cell-13-34">        plot_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_text(</span>
<span id="annotated-cell-13-35">            family<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>heading_family,</span>
<span id="annotated-cell-13-36">            colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent,</span>
<span id="annotated-cell-13-37">            size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>base_size <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">*</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span>,</span>
<span id="annotated-cell-13-38">        ),</span>
<span id="annotated-cell-13-39">        plot_subtitle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_text(colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ink),</span>
<span id="annotated-cell-13-40">        legend_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_text(family<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>heading_family, colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent),</span>
<span id="annotated-cell-13-41">        legend_background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>element_rect(fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>paper, colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span>),</span>
<span id="annotated-cell-13-42">    )</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-13" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="12,13" data-code-annotation="1">Extract foreground (ink) and background (paper) from the brand colours.</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="14" data-code-annotation="2">Blend ink and paper to create an accent colour (custom <code>_col_mix()</code> helper).</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="24,35" data-code-annotation="3">Apply brand fonts to body text and headings.</span>
</dd>
</dl>
</div>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The latest version of <code>ggplot2</code> supports <code>ink</code>, <code>paper</code>, <code>accent</code>, and <code>palette.*</code> theme elements natively. <code>plotnine</code> does not have these parameters, so each element must be styled individually.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Companion Repository
</div>
</div>
<div class="callout-body-container callout-body">
<p>The code samples in this post are intentionally simplified. The <a href="https://github.com/mcanouil/quarto-brand-renderings">companion repository</a> contains production-ready versions with full error handling, defensive <code>.get()</code> fallbacks, and additional styling such as continuous palette support in R.</p>
</div>
</div>
<section id="usage" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="usage"><span class="header-section-number">6.1</span> Usage</h3>
<p>With <code>theme_brand()</code> defined, applying it to a plot is straightforward.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-7-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-1" aria-controls="tabset-7-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-7-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-7-2" aria-controls="tabset-7-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-7-1" class="active tab-pane" aria-labelledby="tabset-7-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r"><span id="cb6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb6-2">penguins <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">read.csv</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"penguins.csv"</span>)</span>
<span id="cb6-3"></span>
<span id="cb6-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(penguins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-5">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> bill_length_mm, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> bill_depth_mm, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> species) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-6">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_point</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_brand</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">brand_mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-8">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">title =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Light Mode"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">subtitle =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Using brand colours and fonts"</span>)</span></code></pre></div></div>
</div>
<div id="tabset-7-2" class="tab-pane" aria-labelledby="tabset-7-2-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-with-copy"><code class="sourceCode python"><span id="cb7-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> polars <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">as</span> pl</span>
<span id="cb7-2"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> plotnine <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> aes, geom_point, ggplot, labs, scale_color_manual</span>
<span id="cb7-3"></span>
<span id="cb7-4">penguins <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> pl.read_csv(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"penguins.csv"</span>, null_values<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"NA"</span>).drop_nulls(</span>
<span id="cb7-5">    [<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill_length_mm"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill_depth_mm"</span>]</span>
<span id="cb7-6">)</span>
<span id="cb7-7"></span>
<span id="cb7-8">palette <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_palette(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>)</span>
<span id="cb7-9"></span>
<span id="cb7-10">(</span>
<span id="cb7-11">    ggplot(penguins.to_pandas())</span>
<span id="cb7-12">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> aes(x<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill_length_mm"</span>, y<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bill_depth_mm"</span>, color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"species"</span>)</span>
<span id="cb7-13">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> geom_point()</span>
<span id="cb7-14">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> theme_brand(brand_mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>)</span>
<span id="cb7-15">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> scale_color_manual(values<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>palette)</span>
<span id="cb7-16">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> labs(title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Light Mode"</span>, subtitle<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Using brand colours and fonts"</span>)</span>
<span id="cb7-17">)</span></code></pre></div></div>
</div>
</div>
</div>
</section>
</section>
<section id="branded-tables" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="branded-tables"><span class="header-section-number">7</span> Branded Tables</h2>
<p>The <code>gt_brand()</code> function follows the same pattern: extract brand colours and fonts, then apply them to the table. Column headers get the first palette colour as a background, body borders use an accent derived from blending ink and paper, and the base and heading fonts are applied throughout.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset" data-group="language">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-8-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-1" aria-controls="tabset-8-1" aria-selected="true" href=""><iconify-icon inline="" icon="fa7-brands:r-project" aria-label="Icon r-project from fa7-brands Iconify.design set." title="Icon r-project from fa7-brands Iconify.design set."></iconify-icon> R</a></li><li class="nav-item"><a class="nav-link" id="tabset-8-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-8-2" aria-controls="tabset-8-2" aria-selected="false" href=""><iconify-icon inline="" icon="fa7-brands:python" aria-label="Icon python from fa7-brands Iconify.design set." title="Icon python from fa7-brands Iconify.design set."></iconify-icon> Python</a></li></ul>
<div class="tab-content" data-group="language">
<div id="tabset-8-1" class="active tab-pane" aria-labelledby="tabset-8-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-16" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode r"><span id="annotated-cell-16-1">gt_brand <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(data, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">brand_mode =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) {</span>
<span id="annotated-cell-16-2">  info <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">get_brand_info</span>()</span>
<span id="annotated-cell-16-3">  brand <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> info[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]]</span>
<span id="annotated-cell-16-4">  brand_data <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand[[brand_mode]][[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>]]</span>
<span id="annotated-cell-16-5">  colors <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand_data[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color"</span>]]</span>
<span id="annotated-cell-16-6">  typography <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> brand_data[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>]]</span>
<span id="annotated-cell-16-7">  palette <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unname</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">unlist</span>(colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"palette"</span>]]))</span>
<span id="annotated-cell-16-8"></span>
<span id="annotated-cell-16-9">  ink <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foreground"</span>]]</span>
<span id="annotated-cell-16-10">  paper <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> colors[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background"</span>]]</span>
<span id="annotated-cell-16-11">  accent <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> scales<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">col_mix</span>(ink, paper, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">amount =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>)</span>
<span id="annotated-cell-16-12"></span>
<span id="annotated-cell-16-13">  gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gt</span>(data) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-16" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-16-14" class="code-annotation-target">    gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opt_table_font</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">font =</span> typography[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"base"</span>]]) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="annotated-cell-16-15">    gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tab_options</span>(</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-16" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-16-16" class="code-annotation-target">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">table.background.color =</span> paper,</span>
<span id="annotated-cell-16-17">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">table.font.color =</span> ink,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-16" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-16-18" class="code-annotation-target">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">column_labels.background.color =</span> palette[[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>L]],</span>
<span id="annotated-cell-16-19">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">column_labels.font.weight =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span>,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-16" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-16-20" class="code-annotation-target">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">table_body.border.top.color =</span> accent,</span>
<span id="annotated-cell-16-21">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">table_body.border.bottom.color =</span> accent</span>
<span id="annotated-cell-16-22">    ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">|&gt;</span></span>
<span id="annotated-cell-16-23">    gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tab_style</span>(</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-16" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-16-24" class="code-annotation-target">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style =</span> gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cell_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">font =</span> typography[[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"headings"</span>]], <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> paper),</span>
<span id="annotated-cell-16-25">      <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">locations =</span> gt<span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">::</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cells_column_labels</span>()</span>
<span id="annotated-cell-16-26">    )</span>
<span id="annotated-cell-16-27">}</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-16" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-16" data-code-lines="14" data-code-annotation="1">Set the base font for the entire table.</span>
</dd>
<dt data-target-cell="annotated-cell-16" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-16" data-code-lines="16,17" data-code-annotation="2">Apply brand background and text colours.</span>
</dd>
<dt data-target-cell="annotated-cell-16" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-16" data-code-lines="18" data-code-annotation="3">Use the first palette colour for column header background.</span>
</dd>
<dt data-target-cell="annotated-cell-16" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-16" data-code-lines="20,21" data-code-annotation="4">Use the accent colour for table body borders.</span>
</dd>
<dt data-target-cell="annotated-cell-16" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-16" data-code-lines="24" data-code-annotation="5">Style column labels with the heading font and contrasting colour.</span>
</dd>
</dl>
</div>
<div id="tabset-8-2" class="tab-pane" aria-labelledby="tabset-8-2-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-17" data-filename="python" style="background: #f1f3f5;"><pre class="sourceCode python cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode python"><span id="annotated-cell-17-1"><span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">from</span> great_tables <span class="im" style="color: #00769E;
background-color: null;
font-style: inherit;">import</span> GT, loc, style</span>
<span id="annotated-cell-17-2"></span>
<span id="annotated-cell-17-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">def</span> gt_brand(data, brand_mode: <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">str</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> GT:</span>
<span id="annotated-cell-17-4">    info <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get_brand_info()</span>
<span id="annotated-cell-17-5">    brand <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> info[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"format"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"render"</span>][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"brand"</span>]</span>
<span id="annotated-cell-17-6">    brand_data <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand[brand_mode][<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data"</span>]</span>
<span id="annotated-cell-17-7">    colors <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand_data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"color"</span>]</span>
<span id="annotated-cell-17-8">    typography <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> brand_data[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"typography"</span>]</span>
<span id="annotated-cell-17-9">    palette <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">list</span>(colors[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"palette"</span>].values())</span>
<span id="annotated-cell-17-10"></span>
<span id="annotated-cell-17-11">    ink <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colors[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foreground"</span>]</span>
<span id="annotated-cell-17-12">    paper <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colors[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background"</span>]</span>
<span id="annotated-cell-17-13">    accent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> _col_mix(ink, paper, <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25</span>)</span>
<span id="annotated-cell-17-14"></span>
<span id="annotated-cell-17-15">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> (</span>
<span id="annotated-cell-17-16">        GT(data)</span>
<span id="annotated-cell-17-17">        .tab_options(</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-17" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-17-18" class="code-annotation-target">            table_font_names<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>typography[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"base"</span>],</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-17" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-17-19" class="code-annotation-target">            table_font_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>ink,</span>
<span id="annotated-cell-17-20">            table_background_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>paper,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-17" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-17-21" class="code-annotation-target">            column_labels_background_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>palette[<span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span>] <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> palette <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> accent,</span>
<span id="annotated-cell-17-22">            column_labels_font_weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span>,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-17" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-17-23" class="code-annotation-target">            table_body_border_top_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent,</span>
<span id="annotated-cell-17-24">            table_body_border_bottom_color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>accent,</span>
<span id="annotated-cell-17-25">        )</span>
<span id="annotated-cell-17-26">        .tab_style(</span>
<span id="annotated-cell-17-27">            style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>style.text(</span>
<span id="annotated-cell-17-28">                font<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>typography[<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"headings"</span>],</span>
<span id="annotated-cell-17-29">                color<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>paper,</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-17" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-17-30" class="code-annotation-target">                weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span>,</span>
<span id="annotated-cell-17-31">            ),</span>
<span id="annotated-cell-17-32">            locations<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>loc.column_labels(),</span>
<span id="annotated-cell-17-33">        )</span>
<span id="annotated-cell-17-34">    )</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-17" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-17" data-code-lines="18" data-code-annotation="1">Set the base font for the entire table.</span>
</dd>
<dt data-target-cell="annotated-cell-17" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-17" data-code-lines="19,20" data-code-annotation="2">Apply brand background and text colours.</span>
</dd>
<dt data-target-cell="annotated-cell-17" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-17" data-code-lines="21" data-code-annotation="3">Use the first palette colour for column header background.</span>
</dd>
<dt data-target-cell="annotated-cell-17" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-17" data-code-lines="23,24" data-code-annotation="4">Use the accent colour for table body borders.</span>
</dd>
<dt data-target-cell="annotated-cell-17" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-17" data-code-lines="30" data-code-annotation="5">Style column labels with the heading font and contrasting colour.</span>
</dd>
</dl>
</div>
</div>
</div>
</section>
<section id="light-and-dark-mode" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="light-and-dark-mode"><span class="header-section-number">8</span> Light and Dark Mode</h2>
<p>Quarto supports light and dark colour modes, and the brand data includes separate colour definitions for each. The helper functions accept a <code>brand_mode</code> parameter to select which variant to use.</p>
<p>For figures, Quarto’s <code>renderings</code> cell option generates both variants from a single code cell.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode default code-with-copy"><code class="sourceCode default"><span id="cb8-1">#| renderings: [light, dark]</span></code></pre></div></div>
<p>The code loops over both modes and prints each plot. Quarto then displays the correct one based on the reader’s selected theme.</p>
<p>For tables, use Quarto’s <code>.light-content</code> and <code>.dark-content</code> CSS classes to conditionally display the appropriate version.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb9-1">::: {.light-content}</span>
<span id="cb9-2">Table rendered with <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`brand_mode = "light"`</span>.</span>
<span id="cb9-3">:::</span>
<span id="cb9-4"></span>
<span id="cb9-5">::: {.dark-content}</span>
<span id="cb9-6">Table rendered with <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`brand_mode = "dark"`</span>.</span>
<span id="cb9-7">:::</span></code></pre></div></div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Automatic Theme Matching
</div>
</div>
<div class="callout-body-container callout-body">
<p>Combining <code>renderings: [light, dark]</code> with branded themes means figures and tables automatically match the reader’s selected theme without any JavaScript.</p>
</div>
</div>
</section>
<section id="putting-it-all-together" class="level2" data-number="9">
<h2 data-number="9" class="anchored" data-anchor-id="putting-it-all-together"><span class="header-section-number">9</span> Putting It All Together</h2>
<p>The full workflow boils down to four steps:</p>
<ol type="1">
<li>Call <code>configure_brand_fonts()</code> once in a hidden setup chunk (<code>#| include: false</code>) to register brand fonts before any plotting.</li>
<li>Apply <code>theme_brand(brand_mode = ...)</code> to every <code>ggplot2</code> or <code>plotnine</code> figure.</li>
<li>Pass your data through <code>gt_brand(data, brand_mode = ...)</code> for every <code>gt</code> or <code>great_tables</code> table.</li>
<li>Use <code>renderings: [light, dark]</code> on figure cells and <code>.light-content</code>/<code>.dark-content</code> divs on tables so the reader’s theme preference is respected automatically.</li>
</ol>
<p>That is the entire pattern. Every helper reads from the same <code>QUARTO_EXECUTE_INFO</code> source, so switching to a different brand configuration is just a matter of changing your <code>_brand.yml</code> file; no code changes are needed.</p>
</section>
<section id="conclusion" class="level2" data-number="10">
<h2 data-number="10" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">10</span> Conclusion</h2>
<p>The <code>QUARTO_EXECUTE_INFO</code> environment variable provides language-agnostic access to Quarto’s resolved brand configuration at render time. With a small set of helper functions, you can apply consistent brand colours, fonts, and palettes to every figure and table your code produces.</p>
<p>This is one approach among several (see the note on <code>brand.yml</code> packages above), but it has the advantage of working identically in R and Python, supporting full font registration, and requiring no dependencies beyond a JSON parser and the plotting libraries you are already using. Browse the <a href="https://github.com/mcanouil/quarto-brand-renderings">companion repository</a> for six complete brand configurations with production-ready implementations, and feel free to adapt the helpers to your own projects.</p>
</section>
<section id="further-resources" class="level2" data-number="11">
<h2 data-number="11" class="anchored" data-anchor-id="further-resources"><span class="header-section-number">11</span> Further Resources</h2>
<ul>
<li><a href="https://quarto.org/docs/authoring/brand.html">Multiformat Branding with <code>_brand.yml</code></a>: the official Quarto documentation on the brand feature.</li>
<li><a href="https://quarto.org/docs/advanced/quarto-execute-info.html">Execution Context Information</a>: how <code>QUARTO_EXECUTE_INFO</code> works and what data it exposes.</li>
<li><a href="https://quarto.org/docs/computations/execution-options.html">Execution Options</a>: code cell options including <code>renderings</code> for light/dark mode.</li>
<li><a href="https://quarto.org/docs/output-formats/html-themes.html">HTML Theming</a>: customising HTML themes, Sass variables, and dark mode support.</li>
<li><a href="https://posit-dev.github.io/brand-yml/"><code>brand.yml</code> Specification</a>: the full specification and documentation for <code>_brand.yml</code> files.</li>
</ul>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Branded {Figures} and {Tables} in {R} and {Python} with
    {Quarto}},
  date = {2026-04-15},
  url = {https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-04-15). Branded Figures and Tables in R and Python
with Quarto. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/">https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>brand</category>
  <category>r</category>
  <category>python</category>
  <category>ggplot2</category>
  <category>plotnine</category>
  <guid>https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/</guid>
  <pubDate>Wed, 15 Apr 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-04-15-quarto-brand-figures-tables/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Building Quarto Typst Templates: Advanced Patterns (Part 2)</title>
  <link>https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Feature image for &quot;Quarto + Typst Part 2&quot; blog post. Dark background
with the official Quarto logo (four blue quadrants with wordmark) and
Typst logo (teal wordmark) centered at the top. Below, &quot;Part 2&quot; in serif
font with subtitle &quot;Modern Scientific Publishing&quot;. File format pipeline
shows .qmd → .typ → .pdf. Decorative elements include floating code
snippets and gradient orbs in blue and teal.
" width="600"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Quarto Version
</div>
</div>
<div class="callout-body-container callout-body">
<p>This tutorial was written and tested with <strong>Quarto CLI 1.9.29 (pre-release)</strong>. Some APIs, template syntax, or extension behaviours may differ in earlier stable releases.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<p>This tutorial covers advanced patterns for production-quality Quarto Typst extensions. Read <a href="../../posts/2026-02-27-typst-template-tutorial-part1/">Part 1</a> first for the foundational concepts.</p>
<ul>
<li><strong>Handler factories</strong>: eliminate boilerplate by generating handlers with a closure-captured configuration flag.</li>
<li><strong>Configuration system</strong>: let users extend the extension with their own class-to-function mappings via document metadata.</li>
<li><strong>Type-safe value conversion</strong>: <code>typst_value</code> maps Lua types to correct Typst syntax (strings, numbers, keywords, units).</li>
<li><strong>WCAG-compliant badges</strong>: iterative contrast adjustment ensures accessible colour combinations.</li>
<li><strong>Shortcodes</strong>: explicit syntax for components without a natural markdown representation.</li>
</ul>
<div style="text-align: center;">
<p><a href="./tutorial-part2.zip" class="btn btn-outline-light" target="_blank" rel="noopener noreferrer">Part 2 (ZIP)</a></p>
</div>
</div>
</div>
<section id="continuing-from-part-1" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="continuing-from-part-1"><span class="header-section-number">1</span> Continuing from Part 1</h2>
<p><a href="../../posts/2026-02-27-typst-template-tutorial-part1/">Part 1</a> introduced the dual-layer architecture for building Quarto Typst templates. Here is a brief recap of the building blocks this tutorial relies on:</p>
<ul>
<li><strong>Lua filters</strong> intercept Pandoc AST elements (spans, divs) and generate raw Typst code via <code>pandoc.RawBlock</code> and <code>pandoc.RawInline</code>.</li>
<li><strong>Typst rendering functions</strong> receive that generated code and produce styled output with access to document context.</li>
<li><strong>Wrapper functions</strong> in <code>typst-show.typ</code> bridge parse-time (Lua) and render-time (Typst), injecting runtime context such as colour schemes.</li>
<li><strong><code>build_wrapped_content</code></strong> handles the common pattern of wrapping div content with opening and closing Typst function calls, optionally extracting a title from the first heading.</li>
</ul>
<p>This tutorial builds on those foundations with advanced patterns for production-quality extensions.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Part 1 used <code>filter.lua</code> as the filter filename in its conceptual examples. Part 2 uses <code>typst-markdown.lua</code>, which is the production filename used in the downloadable extension. The role is identical: it is the Pandoc filter entrypoint that loads shared modules and registers the <code>Div</code> and <code>Span</code> handlers. When building your own extension, you can choose any descriptive name; just ensure it matches the filename declared under <code>filters:</code> in <code>_extension.yml</code>.</p>
</div>
</div>
<p>To keep the article and downloadable zip perfectly aligned, source-backed code blocks below show the full file content from the assets bundle. Each section highlights the exact functions to focus on, so you can scan purposefully instead of reading every line top-to-bottom.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is <strong>Part 2</strong> of a two-part series. If you have not read <a href="../../posts/2026-02-27-typst-template-tutorial-part1/">Part 1</a>, start there first to understand the basic concepts of Lua filters, Typst functions, and wrapper patterns.</p>
<p>The <a href="https://github.com/mcanouil/quarto-mcanouil">‘mcanouil’ Quarto extension</a> demonstrates all patterns covered in this tutorial.</p>
</div>
</div>
</section>
<section id="handler-factories-reducing-repetition" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="handler-factories-reducing-repetition"><span class="header-section-number">2</span> Handler Factories: Reducing Repetition</h2>
<p>As you add components, the same handler structure keeps appearing. Part 1’s panel handler called <code>build_wrapped_content</code> directly. With one or two components that is fine, but each new handler duplicates the same boilerplate:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) without factories</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-1" data-filename="(conceptual) without factories" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_HANDLERS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-2">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-3" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-4">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-5">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-1-6" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-7">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="3" data-code-annotation="1">Extract title from first heading.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="6" data-code-annotation="2">No title extraction; every new wrapped component repeats this same structure.</span>
</dd>
</dl>
<p>The only thing that varies is the <code>extract_title</code> flag. A factory function captures that variation in a <a href="https://www.lua.org/pil/6.1.html">closure</a>, eliminating the repetition entirely. A closure is a function that remembers variables from the scope where it was created. When <code>create_wrapped_handler(true)</code> returns a new function, that function carries <code>should_extract_title = true</code> inside it, so every call to the returned handler automatically uses the correct flag without it needing to be passed again. This is necessary because the filter loop (shown below in “Using Factories”) calls each handler as <code>DIV_FACTORY_HANDLERS[class](div, class_config)</code>, passing only the div element and the class config; the flag is never passed. By baking the flag into the closure at declaration time, each entry in <code>DIV_FACTORY_HANDLERS</code> becomes a self-contained handler with no extra parameters to manage.</p>
<section id="the-factory-pattern" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="the-factory-pattern"><span class="header-section-number">2.1</span> The Factory Pattern</h3>
<p>In this file, focus on <code>create_wrapped_handler</code> and how it forwards to <code>build_wrapped_content</code>; the rest are shared helpers used by multiple components.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/wrapper.lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-2" data-filename="_extensions/my-extension/_modules/wrapper.lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-2-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/typst-utils.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%.lua$'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-2-2"></span>
<span id="annotated-cell-2-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-4">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attributes_to_table</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attributes_to_table</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-2-6"></span>
<span id="annotated-cell-2-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Extract first heading as title attribute</span></span>
<span id="annotated-cell-2-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>extract_first_heading_as_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'title'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-2-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-2-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">t</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Header'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-2-12">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'title'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-13">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-2-14">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-2-15">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="annotated-cell-2-16">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-17">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span></span>
<span id="annotated-cell-2-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-19">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-20"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-21"></span>
<span id="annotated-cell-2-22"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build Typst block wrappers with optional attributes</span></span>
<span id="annotated-cell-2-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_typst_block_wrappers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-24">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">has_attributes</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">next</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span></span>
<span id="annotated-cell-2-25"></span>
<span id="annotated-cell-2-26">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">has_attributes</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-2-27">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_attribute_string<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-28">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s(%s)['</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">']'</span></span>
<span id="annotated-cell-2-29">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span></span>
<span id="annotated-cell-2-30">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s['</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">']'</span></span>
<span id="annotated-cell-2-31">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-32"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-33"></span>
<span id="annotated-cell-2-34"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build wrapped content for a div</span></span>
<span id="annotated-cell-2-35"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">should_extract_title</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-36">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>attributes_to_table<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-37">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">should_extract_title</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-2-38">    <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>extract_first_heading_as_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-39">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-40"></span>
<span id="annotated-cell-2-41">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_typst_block_wrappers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-42">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-2-43">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">_</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ipairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-2-44">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-45">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-46">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-2-47">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span></span>
<span id="annotated-cell-2-48"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-49"></span>
<span id="annotated-cell-2-50"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Factory: create handler for wrapped content components</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-51" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>create_wrapped_handler<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">should_extract_title</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-52" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-53">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">should_extract_title</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-54">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-55"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-2-56"></span>
<span id="annotated-cell-2-57"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="51" data-code-annotation="1"><code>should_extract_title</code> is captured in the returned closure; each call to <code>create_wrapped_handler</code> produces a distinct handler locked to that flag value.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="52" data-code-annotation="2">The returned function takes <code>div</code> (the Pandoc div element passed by the dispatcher) and <code>config</code> (the mapping entry for this class, e.g., <code>{ wrapper='my-panel', arguments=true }</code>); this signature matches exactly what the dispatcher passes when it calls <code>DIV_FACTORY_HANDLERS[class](div, class_config)</code>.</span>
</dd>
</dl>
</section>
<section id="using-factories" class="level3" data-number="2.2">
<h3 data-number="2.2" class="anchored" data-anchor-id="using-factories"><span class="header-section-number">2.2</span> Using Factories</h3>
<p>These factories let you declare handlers concisely. <code>DIV_FACTORY_HANDLERS</code> is where each class opts into title extraction or content-only wrapping; <code>DIV_HANDLERS</code> is reserved for components that need custom extraction logic.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (handler tables excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="_extensions/my-extension/typst-markdown.lua (handler tables excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-3-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_HANDLERS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-2" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quote_card</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">process_div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-4"></span>
<span id="annotated-cell-3-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_FACTORY_HANDLERS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-3-6" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>create_wrapped_handler<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-3-7" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>create_wrapped_handler<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-3-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="2" data-code-annotation="1">Custom handler: handles component-specific extraction logic (author from heading).</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="6" data-code-annotation="2">Factory handler with title extraction enabled; the first heading inside the div becomes the <code>title</code> argument.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="7" data-code-annotation="3">Factory handler without title extraction; content passes through unchanged.</span>
</dd>
</dl>
<p>The dispatcher checks each div class in priority order: custom handlers first, factory handlers second, and the default wrapper last.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (Div filter excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-4" data-filename="_extensions/my-extension/typst-markdown.lua (Div filter excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-4-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">_</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ipairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-4-2">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged_div_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-4-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-4" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_HANDLERS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-4-5">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_HANDLERS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-4-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-4-7"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-4-8" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_FACTORY_HANDLERS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-4-9">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_FACTORY_HANDLERS</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-4-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-4-11"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-4-12" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-4-13">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-4-14"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="4" data-code-annotation="1">Custom handlers run first, before factory or default logic.</span>
</dd>
<dt data-target-cell="annotated-cell-4" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="8" data-code-annotation="2">Factory-generated handlers run next.</span>
</dd>
<dt data-target-cell="annotated-cell-4" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="12" data-code-annotation="3">Default fallback: simple content wrap, no title extraction. User-defined mappings added via YAML always reach this default branch; they cannot opt into title extraction or custom logic without a corresponding factory or custom handler in the extension code.</span>
</dd>
</dl>
<div class="highlight">
<p><strong>Handler factories</strong> encapsulate common patterns. Use <code>create_wrapped_handler(true)</code> for components with titles and <code>create_wrapped_handler(false)</code> for content-only wrappers.</p>
</div>
</section>
<section id="the-span-dispatcher" class="level3" data-number="2.3">
<h3 data-number="2.3" class="anchored" data-anchor-id="the-span-dispatcher"><span class="header-section-number">2.3</span> The Span Dispatcher</h3>
<p>Inline spans (badges and custom highlights) use a simpler dispatcher than divs. There are no factory handlers or custom handlers for spans: every inline class goes through the same lookup-and-format loop.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (Span filter excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename="_extensions/my-extension/typst-markdown.lua (Span filter excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-1" class="code-annotation-target"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">_</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ipairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-5-2">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged_span_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-5-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-5-4" class="code-annotation-target">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-5">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>attributes_to_table<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-5-6" class="code-annotation-target">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_attribute_string<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-7"></span>
<span id="annotated-cell-5-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span></span>
<span id="annotated-cell-5-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-5-10" class="code-annotation-target">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s(%s)[%s]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span></span>
<span id="annotated-cell-5-12">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s[%s]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-5-14"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-5-15" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-16">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-5-17"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="1" data-code-annotation="1">Spans can carry multiple classes (e.g., <code>[text]{.badge .highlight}</code>); <code>span.classes</code> preserves declaration order (left to right as written in the markdown), so iterating finds the first mapped class and <code>return</code> exits immediately, ensuring only one handler fires per span.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="4" data-code-annotation="2">Span content is flattened to plain text; inline elements cannot preserve nested structure the way block divs can.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="6" data-code-annotation="3">Build the attribute string from any key-value pairs on the span (e.g., <code>colour="success"</code> becomes <code>colour: "success"</code>).</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="10" data-code-annotation="4">If attributes are present, generate <code>#wrapper(attr: value)[content]</code>; otherwise use the shorter <code>#wrapper[content]</code> form.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="15" data-code-annotation="5">Return <code>RawInline</code> (not <code>RawBlock</code>) so the result sits inline within the surrounding paragraph, not on its own line.</span>
</dd>
</dl>
</section>
<section id="choosing-your-approach" class="level3" data-number="2.4">
<h3 data-number="2.4" class="anchored" data-anchor-id="choosing-your-approach"><span class="header-section-number">2.4</span> Choosing Your Approach</h3>
<p>When building a new component, consider:</p>
<ul>
<li><strong>No handler (default)</strong>: Simple wrapper around content; just add a mapping in document YAML.</li>
<li><strong><code>create_wrapped_handler(true)</code></strong>: Wraps content and extracts title from first heading (e.g., panels with titles).</li>
<li><strong><code>create_wrapped_handler(false)</code></strong>: Wraps content without title extraction (e.g., simple content wrappers).</li>
<li><strong>Custom handler</strong>: Complex data extraction from nested elements (e.g., quote cards with author attribution).</li>
<li><strong>Shortcode</strong>: No natural markdown syntax; uses explicit <code>{{&lt; shortcode &gt;}}</code> syntax.</li>
</ul>
</section>
</section>
<section id="sec-config" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="sec-config"><span class="header-section-number">3</span> The Configuration System</h2>
<p>A key design goal for reusable extensions is extensibility. Users should be able to add their own components or override built-in ones without modifying the extension’s source code. The <a href="https://github.com/mcanouil/quarto-mcanouil/blob/main/_extensions/mcanouil/typst/_modules/config.lua">configuration system</a> enables this through metadata-driven element mappings.</p>
<section id="built-in-mappings" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="built-in-mappings"><span class="header-section-number">3.1</span> Built-in Mappings</h3>
<p>The extension defines default mappings for all its components. <code>get_builtin_mappings()</code> is the canonical baseline before any user metadata overrides are merged.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/config.lua (built-in mappings excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename="_extensions/my-extension/_modules/config.lua (built-in mappings excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Return default div/span mappings for all components</span></span>
<span id="annotated-cell-6-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>get_builtin_mappings<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="annotated-cell-6-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-6-4">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-5" class="code-annotation-target">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-6-6" class="code-annotation-target">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-6-7">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-6-8">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-6-9">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-6-10">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-6-11">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-6-12">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-6-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="5" data-code-annotation="1"><code>arguments = false</code>: parameter list generated only when attributes are present.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="6" data-code-annotation="2"><code>arguments = true</code>: parameter list always generated, even when empty, because the Typst function always expects named arguments.</span>
</dd>
</dl>
<p>Each mapping specifies:</p>
<ul>
<li><strong><code>wrapper</code></strong>: The Typst function name to call.</li>
<li><strong><code>arguments</code></strong>: Whether to always pass attributes (even if empty).</li>
</ul>
</section>
<section id="user-configuration-via-metadata" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="user-configuration-via-metadata"><span class="header-section-number">3.2</span> User Configuration via Metadata</h3>
<p>Users can extend or override mappings through document metadata:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) document.qmd</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-7" data-filename="(conceptual) document.qmd" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-7-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span>
<span id="annotated-cell-7-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Document"</span></span>
<span id="annotated-cell-7-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">extensions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-7-4" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst-markdown</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-7-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">divs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-7-6" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">my-callout</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> my-custom-callout</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-7-7" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">my-box</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-7-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">function</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> my-custom-box</span></span>
<span id="annotated-cell-7-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">arguments</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="annotated-cell-7-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">spans</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-7-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">my-highlight</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> my-text-highlight</span></span>
<span id="annotated-cell-7-12"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-7" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="4" data-code-annotation="1">User mappings live under the <code>extensions.typst-markdown</code> namespace.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="6" data-code-annotation="2">Simple format: class name maps directly to a Typst function name.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="7" data-code-annotation="3">Detailed format: allows setting <code>arguments: true</code> to always generate a parameter list. Both <code>function:</code> and <code>wrapper:</code> are accepted as the key name in the detailed format and are true aliases; <code>wrapper:</code> matches the internal Lua variable name, whilst <code>function:</code> is more descriptive in a YAML context, so <code>function:</code> is preferred. Use either the simple or detailed format for each class; they are not combined: a class entry is either a bare string or an object, not both.</span>
</dd>
</dl>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>The YAML mapping tells the Lua dispatcher <em>which Typst function to call</em>, but you must also <strong>provide that function’s implementation</strong>. The extension has no built-in <code>my-custom-box</code> or <code>my-custom-callout</code>; those names are placeholders you define yourself. Add the Typst function to your document’s Typst preamble via <code>include-in-header</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) document.qmd front matter</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="(conceptual) document.qmd front matter" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">include-in-header</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">  - </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">text</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="cb1-3">      #let my-custom-box(content) = {</span>
<span id="cb1-4">        block(fill: luma(240), radius: 4pt, inset: 1em, content)</span>
<span id="cb1-5">      }</span></code></pre></div></div>
</div>
<p>Alternatively, place the function in a <code>.typ</code> file and include it the same way. The function signature must match the arguments the dispatcher will generate based on your <code>arguments</code> setting.</p>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>If you want another real-world comparison, <a href="https://github.com/christopherkenny/typst-function"><code>christopherkenny/typst-function</code></a> uses a similar metadata-driven mapping idea with different naming and module structure. Comparing both implementations is useful for understanding which parts are pattern-level versus project-specific choices.</p>
</div>
</div>
</section>
<section id="loading-user-configuration" class="level3" data-number="3.3">
<h3 data-number="3.3" class="anchored" data-anchor-id="loading-user-configuration"><span class="header-section-number">3.3</span> Loading User Configuration</h3>
<p>The configuration module parses user settings from metadata. Two helpers keep the logic simple and robust:</p>
<ul>
<li><code>meta_get(...)</code> reads metadata maps safely, whether keys are plain strings or Pandoc metadata objects.</li>
<li><code>parse_mapping(...)</code> normalises both supported user formats into one internal shape (<code>{wrapper=..., arguments=...}</code>).</li>
</ul>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/config.lua (user config excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-8" data-filename="_extensions/my-extension/_modules/config.lua (user config excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> meta_get<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta_map</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wanted_key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta_map</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'table'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta_map</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wanted_key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta_map</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wanted_key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-4">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Pandoc meta maps may use non-string key objects; normalise via stringify.</span></span>
<span id="annotated-cell-8-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta_map</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-8-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wanted_key</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span></span>
<span id="annotated-cell-8-9"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-10"></span>
<span id="annotated-cell-8-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Parse simple or detailed config format</span></span>
<span id="annotated-cell-8-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> parse_mapping<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-8-13" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'string'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-8-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-15">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-16"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-8-17" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">fn_name</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> meta_get<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'function'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> meta_get<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'wrapper'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-18">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">fn_name</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-8-19">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arg_value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> meta_get<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'arguments'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-21">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">fn_name</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-8-22">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arg_value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arg_value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'true'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-8-23">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-24">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-25"></span>
<span id="annotated-cell-8-26">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Simple form: `custom-highlight: my-custom-highlight`.</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-8-27" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple_fn</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-28">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple_fn</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-8-29">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple_fn</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-30">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-8-31"></span>
<span id="annotated-cell-8-32">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span></span>
<span id="annotated-cell-8-33"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-8" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="13" data-code-annotation="1">Simple string mapping: <code>my-callout: my-custom-callout</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="17" data-code-annotation="2">Detailed mapping: both <code>function</code> and <code>wrapper</code> are accepted as the key name, normalised to <code>wrapper</code> internally; <code>arguments = arg_value and ... or false</code> is the Lua ternary idiom (<code>condition and x or y</code>) since Lua has no <code>?:</code> operator, and the value is stringified before comparison because Pandoc delivers YAML booleans as metadata strings, not Lua booleans.</span>
</dd>
<dt data-target-cell="annotated-cell-8" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="27" data-code-annotation="3">Fallback: Pandoc may wrap a plain string in a metadata object; <code>stringify</code> unwraps it.</span>
</dd>
</dl>
</section>
<section id="merging-configurations" class="level3" data-number="3.4">
<h3 data-number="3.4" class="anchored" data-anchor-id="merging-configurations"><span class="header-section-number">3.4</span> Merging Configurations</h3>
<p>User configuration overrides built-in defaults. <code>merge_configurations(...)</code> performs a simple two-pass merge: built-ins first, then user mappings which overwrite any matching key.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/config.lua (merge excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-9" data-filename="_extensions/my-extension/_modules/config.lua (merge excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-9-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Merge configurations: user overrides built-in</span></span>
<span id="annotated-cell-9-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>merge_configurations<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">builtin</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">user</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-9-3">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-9-4" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cfg</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">builtin</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-9-5">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cfg</span></span>
<span id="annotated-cell-9-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-9-7" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cfg</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">user</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-9-8">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">class</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">cfg</span></span>
<span id="annotated-cell-9-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-9-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">merged</span></span>
<span id="annotated-cell-9-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-9" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="4" data-code-annotation="1">Copy all built-in mappings first.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="7" data-code-annotation="2">User mappings overwrite built-in ones with the same key; new keys are added as extensions.</span>
</dd>
</dl>
</section>
</section>
<section id="sec-value-conversion" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="sec-value-conversion"><span class="header-section-number">4</span> Value Conversion and Type Safety</h2>
<p>When passing values from Lua to Typst, type conversion matters. Strings need quoting. Numbers should not. Booleans map to Typst’s <code>true</code>/<code>false</code>. Getting this wrong produces syntax errors or unexpected behaviour.</p>
<section id="the-typst_value-function" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="the-typst_value-function"><span class="header-section-number">4.1</span> The <code>typst_value</code> Function</h3>
<p>The <a href="https://github.com/mcanouil/quarto-mcanouil/blob/main/_extensions/mcanouil/typst/_modules/typst-utils.lua"><code>typst-utils</code> module</a> provides a <code>typst_value</code> function that handles conversion.</p>
<p>Focus on <code>typst_value(...)</code>: it is the single conversion gate between Lua values and valid Typst syntax.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/typst-utils.lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-10" data-filename="_extensions/my-extension/_modules/typst-utils.lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-10-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-10-2"></span>
<span id="annotated-cell-10-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Convert a Lua value to Typst syntax</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-10-4" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-10-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'none'</span></span>
<span id="annotated-cell-10-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elseif</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'boolean'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-10-8" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'true'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'false'</span></span>
<span id="annotated-cell-10-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elseif</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'number'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-10-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tostring</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-11">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elseif</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">type</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'string'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-10-12" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'none'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'auto'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'true'</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'false'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-10-13">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span></span>
<span id="annotated-cell-10-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-10-15" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">match</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'^%-?%d+%.?%d*[a-z%%]+$'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-10-16">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span></span>
<span id="annotated-cell-10-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-10-18" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span></span>
<span id="annotated-cell-10-19">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span></span>
<span id="annotated-cell-10-20">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">tostring</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'</span><span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">\\</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'"'</span></span>
<span id="annotated-cell-10-21">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-22"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-23"></span>
<span id="annotated-cell-10-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Convert element attributes to a plain Lua table</span></span>
<span id="annotated-cell-10-25"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>attributes_to_table<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">element</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-26">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-10-27">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">element</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attributes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-10-28">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span></span>
<span id="annotated-cell-10-29">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-30">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span></span>
<span id="annotated-cell-10-31"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-32"></span>
<span id="annotated-cell-10-33"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build attribute string for Typst function calls</span></span>
<span id="annotated-cell-10-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_attribute_string<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-35">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-10-36">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-10-37">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_key</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span></span>
<span id="annotated-cell-10-38">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_val</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-39">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%s: %s'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_val</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-10-40">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-41">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.concat</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">', '</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-10-42"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-10-43"></span>
<span id="annotated-cell-10-44"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-10" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="4" data-code-annotation="1">Entry point: dispatches on Lua type; all paths return a string of valid Typst syntax.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="8" data-code-annotation="2">Ternary pattern: <code>condition and true_value or false_value</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="12" data-code-annotation="3">Typst keywords (<code>none</code>, <code>auto</code>, <code>true</code>, <code>false</code>) passed through without quotes.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="15" data-code-annotation="4"><a href="https://www.lua.org/manual/5.4/manual.html#6.4.1">Lua pattern</a> matches Typst measurement values like <code>1em</code>, <code>2.5pt</code>, <code>100%</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-10" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="18" data-code-annotation="5">Escape double quotes inside string values to prevent broken Typst syntax.</span>
</dd>
</dl>
</section>
<section id="usage-examples" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="usage-examples"><span class="header-section-number">4.2</span> Usage Examples</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-11" data-filename="lua" style="background: #f1f3f5;"><pre class="sourceCode lua cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-11-1" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-11-2" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-11-3" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">42</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-11-4" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"hello"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-11-5" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1em"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-11-6" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'none'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-11" data-target-annotation="7" onclick="event.preventDefault();">7</a><span id="annotated-cell-11-7" class="code-annotation-target">typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Say "hi"'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-11" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="1" data-code-annotation="1">Returns: <code>none</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="2" data-code-annotation="2">Returns: <code>true</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="3" data-code-annotation="3">Returns: <code>42</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="4" data-code-annotation="4">Returns: <code>"hello"</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="5" data-code-annotation="5">Returns: <code>1em</code> (Typst length unit, not quoted).</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="6" data-code-annotation="6">Returns: <code>none</code> (Typst keyword, not quoted).</span>
</dd>
<dt data-target-cell="annotated-cell-11" data-target-annotation="7">7</dt>
<dd>
<span data-code-cell="annotated-cell-11" data-code-lines="7" data-code-annotation="7">Returns: <code>"Say \"hi\""</code> (escaped quotes).</span>
</dd>
</dl>
</section>
<section id="building-attribute-strings" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="building-attribute-strings"><span class="header-section-number">4.3</span> Building Attribute Strings</h3>
<p>When generating Typst function calls, attributes need proper formatting. <code>build_attribute_string(...)</code> in <code>typst-utils.lua</code> centralises this so handlers never duplicate the logic.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/typst-utils.lua (attribute string excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-12" data-filename="_extensions/my-extension/_modules/typst-utils.lua (attribute string excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-12-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build attribute string for Typst function calls</span></span>
<span id="annotated-cell-12-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_attribute_string<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-12-3">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-12" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-12-4" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-12" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-12-5" class="code-annotation-target">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_key</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span></span>
<span id="annotated-cell-12-6">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_val</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>typst_value<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-12-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%s: %s'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_val</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-12-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-12" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-12-9" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.concat</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">parts</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">', '</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-12-10"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-12" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-12" data-code-lines="4" data-code-annotation="1">Iterate over all attribute key-value pairs.</span>
</dd>
<dt data-target-cell="annotated-cell-12" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-12" data-code-lines="5" data-code-annotation="2">Attribute keys are passed through unchanged; rename here if your Typst function uses a different parameter name.</span>
</dd>
<dt data-target-cell="annotated-cell-12" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-12" data-code-lines="9" data-code-annotation="3">Join with comma separator for Typst named arguments.</span>
</dd>
</dl>
</section>
</section>
<section id="wcag-compliance-for-badges" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="wcag-compliance-for-badges"><span class="header-section-number">5</span> WCAG Compliance for Badges</h2>
<p>Production-quality badges need accessible colour contrast. The WCAG AA standard requires 4.5:1 contrast ratio for small text.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The PDF document generated in this tutorial is not a fully accessible PDF because of the use of a simplified template.</p>
</div>
</div>
<section id="colour-calculation" class="level3" data-number="5.1">
<h3 data-number="5.1" class="anchored" data-anchor-id="colour-calculation"><span class="header-section-number">5.1</span> Colour Calculation</h3>
<p><code>get-badge-colours</code> calls two helper functions defined earlier in <code>components.typ</code>. <code>get-accent-colour</code> maps semantic names (<code>"success"</code>, <code>"warning"</code>, <code>"danger"</code>, <code>"info"</code>) to a fixed palette of Quarto callout colours. <code>ensure-contrast</code> iteratively lightens or darkens a foreground colour until the perceived contrast against the background is sufficient. Both helpers are in the full <code>components.typ</code> file from the zip.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/components.typ (badges excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-13" data-filename="_extensions/my-extension/components.typ (badges excerpt)" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-13-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Compute badge colours with dark-mode support</span></span>
<span id="annotated-cell-13-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">get-badge-colours</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-13-3">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">fg-components</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>foreground<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>components<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-13-4" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">is-dark-mode</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> fg-components<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">50%</span></span>
<span id="annotated-cell-13-5"></span>
<span id="annotated-cell-13-6">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">base</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> get-accent-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tip"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-13-7">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"warning"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> get-accent-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"warning"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-13-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"danger"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> get-accent-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"important"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-13-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> get-accent-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"note"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-13-10" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>muted <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-13-11"></span>
<span id="annotated-cell-13-12">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">background</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark-mode <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>darken<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">60%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-13-13" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>lighten<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">80%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-13-14"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-13-15" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">text-colour</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> ensure-contrast<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> min-ratio<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4.5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-13-16"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-13" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-13-17" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>background: background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> border: base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> text: text-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-13-18"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-13" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="4" data-code-annotation="1">Detect dark mode: light foreground (<code>&gt; 50%</code> lightness) means the page background is dark.</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="10" data-code-annotation="2">Map semantic colour names to Quarto’s <a href="https://quarto.org/docs/authoring/callouts.html">callout theme colours</a>; fall back to <code>muted</code> for unknown names.</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="13" data-code-annotation="3">Darken base colour for dark mode backgrounds; lighten for light mode.</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="15" data-code-annotation="4">Adjust text colour iteratively until the contrast ratio meets the 4.5:1 WCAG AA threshold.</span>
</dd>
<dt data-target-cell="annotated-cell-13" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-13" data-code-lines="17" data-code-annotation="5">Return a <a href="https://typst.app/docs/reference/foundations/dictionary/">dictionary</a> with background, border, and text colours.</span>
</dd>
</dl>
</section>
<section id="integrating-get-badge-colours-in-the-component" class="level3" data-number="5.2">
<h3 data-number="5.2" class="anchored" data-anchor-id="integrating-get-badge-colours-in-the-component"><span class="header-section-number">5.2</span> Integrating <code>get-badge-colours</code> in the Component</h3>
<p>Use <code>get-badge-colours</code> inside the badge component, then inject runtime colours from the wrapper.</p>
<p>Focus on the handoff: <code>my-badge</code> resolves runtime theme colours once, and <code>simple-badge</code> consumes the resulting dictionary.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-show.typ (badge wrapper excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-14" data-filename="_extensions/my-extension/typst-show.typ (badge wrapper excerpt)" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-14-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Badge wrapper (WCAG-aware)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-14" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-14-2" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">my-badge</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"neutral"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-14-3">  simple-badge<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-14-4">    content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-14-5">    colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-14" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-14-6" class="code-annotation-target">    colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> get-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> effective-brand-mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-14-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-14-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-14" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-14" data-code-lines="2" data-code-annotation="1"><code>my-badge</code> is the public function Lua calls; it resolves the colour scheme once and passes it into <code>simple-badge</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-14" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-14" data-code-lines="6" data-code-annotation="2"><code>effective-brand-mode</code> is the Pandoc template variable introduced in Part 1 that Quarto resolves at render time; <code>get-colours(mode: ...)</code> returns the full colour dictionary, and <code>simple-badge</code> delegates palette computation to <code>get-badge-colours</code>.</span>
</dd>
</dl>
<div class="highlight">
<p><strong>WCAG AA compliance</strong> requires 4.5:1 contrast ratio for small text (below 14pt bold or 18pt regular). Badge text at 0.85em typically falls into this category.</p>
</div>
</section>
</section>
<section id="shortcodes-an-alternative-approach" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="shortcodes-an-alternative-approach"><span class="header-section-number">6</span> Shortcodes: An Alternative Approach</h2>
<p>For components without a natural markdown representation, shortcodes provide explicit syntax.</p>
<section id="when-to-use-shortcodes" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="when-to-use-shortcodes"><span class="header-section-number">6.1</span> When to Use Shortcodes</h3>
<p>Shortcodes work well for:</p>
<ul>
<li>Components with many required parameters.</li>
<li>Elements that do not map naturally to divs or spans.</li>
<li>Functionality that needs explicit invocation.</li>
</ul>
</section>
<section id="basic-shortcode-structure" class="level3" data-number="6.2">
<h3 data-number="6.2" class="anchored" data-anchor-id="basic-shortcode-structure"><span class="header-section-number">6.2</span> Basic Shortcode Structure</h3>
<p>Focus on the returned raw Typst call: shortcode code should stay thin and delegate visual logic to shared Typst wrappers.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/shortcodes.lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-15" data-filename="_extensions/my-extension/shortcodes.lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-15-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-15-2" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'status-badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">kwargs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-15-4">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>Null<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="annotated-cell-15-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-15-6"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-15-7" class="code-annotation-target">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">label</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">kwargs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'label'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Status'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">style</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">kwargs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'style'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'info'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-9"></span>
<span id="annotated-cell-15-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-15-11" class="code-annotation-target">      <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#my-badge(colour: "%s")[%s]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-15-12">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-15-13">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">label</span></span>
<span id="annotated-cell-15-14">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-15"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-15-16" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-17">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-15-18"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-15" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="2" data-code-annotation="1">Shortcode name maps directly to the handler function; <code>args</code> are positional values, <code>kwargs</code> are named.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="7,8" data-code-annotation="2">Always stringify metadata values to handle Pandoc’s <code>MetaInlines</code> type; the <code>or</code> default is a plain Lua string, so <code>stringify</code> on it is harmless.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="11" data-code-annotation="3">The shortcode’s <code>style</code> attribute maps to <code>my-badge</code>’s <code>colour</code> parameter; both name the same semantic concept but follow different naming conventions (HTML attribute names versus Typst parameter names).</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="16" data-code-annotation="4">Return <code>RawInline</code> for inline output; use <code>RawBlock</code> for block-level shortcodes.</span>
</dd>
</dl>
</section>
<section id="usage" class="level3" data-number="6.3">
<h3 data-number="6.3" class="anchored" data-anchor-id="usage"><span class="header-section-number">6.3</span> Usage</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb2-1">{{&lt; status-badge label="Hello" style="info" &gt;}}</span></code></pre></div></div>
</section>
</section>
<section id="complete-example-quote-card" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="complete-example-quote-card"><span class="header-section-number">7</span> Complete Example: Quote Card</h2>
<p>Let us build a complete quote card component from scratch, demonstrating all patterns covered.</p>
<section id="step-1-design-the-markdown-syntax" class="level3" data-number="7.1">
<h3 data-number="7.1" class="anchored" data-anchor-id="step-1-design-the-markdown-syntax"><span class="header-section-number">7.1</span> Step 1: Design the Markdown Syntax</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb3-1">::: {.quote-card author="Alan Kay" source="1971"}</span>
<span id="cb3-2">The best way to predict the future is to invent it.</span>
<span id="cb3-3">:::</span></code></pre></div></div>
<p>Alternatively, support heading-based author attribution:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb4-1">::: {.quote-card source="1971"}</span>
<span id="cb4-2">The best way to predict the future is to invent it.</span>
<span id="cb4-3"></span>
<span id="cb4-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Alan Kay</span></span>
<span id="cb4-5"></span>
<span id="cb4-6">:::</span></code></pre></div></div>
</section>
<section id="step-2-create-the-typst-rendering-function" class="level3" data-number="7.2">
<h3 data-number="7.2" class="anchored" data-anchor-id="step-2-create-the-typst-rendering-function"><span class="header-section-number">7.2</span> Step 2: Create the Typst Rendering Function</h3>
<p>Focus on <code>render-quote-card(...)</code> and its conditional attribution block; the badge and panel functions already exist from earlier sections.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/components.typ (quote-card excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="_extensions/my-extension/components.typ (quote-card excerpt)" style="background: #f1f3f5;"><pre class="sourceCode typst code-with-copy"><code class="sourceCode typst"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">QUOTE-CARD-RADIUS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8pt</span></span>
<span id="cb5-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">QUOTE-CARD-INSET</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.5em</span></span>
<span id="cb5-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">QUOTE-MARK-SIZE</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">3em</span></span>
<span id="cb5-4"></span>
<span id="cb5-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Render a styled quote card</span></span>
<span id="cb5-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">render-quote-card</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb5-7">  content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-8">  author<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-9">  source<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-10">  colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb5-11"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb5-12">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">bg</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"background"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">250</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb5-13">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">fg</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"foreground"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">50</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb5-14">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">muted</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>at<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"muted"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> default<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb5-15">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">accent</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get-accent-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"note"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-16"></span>
<span id="cb5-17">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb5-18">    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-19">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> bg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-20">    stroke<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>left: <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4pt</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> accent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-21">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>right: QUOTE-CARD-RADIUS<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-22">    inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> QUOTE-CARD-INSET<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-23">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb5-24">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Opening quote mark</span></span>
<span id="cb5-25">      place<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-26">        top <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> left<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-27">        dx<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-28">        dy<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.3em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-29">        text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> QUOTE-MARK-SIZE<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> accent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>lighten<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">60%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> sym<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>quote<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>l<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>double<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb5-30">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-31"></span>
<span id="cb5-32">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Quote content</span></span>
<span id="cb5-33">      pad<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>left<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)[</span></span>
<span id="cb5-34">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"italic"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> fg<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> -- &lt;4&gt;</span>
<span id="cb5-35">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb5-36"></span>
<span id="cb5-37">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Attribution line</span></span>
<span id="cb5-38">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> author <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> or source <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb5-39">        v<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-40">        align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>right<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)[</span></span>
<span id="cb5-41">          <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> muted<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)[</span></span>
<span id="cb5-42">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>sym<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>dash<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>em</span>
<span id="cb5-43">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> author <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>author<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb5-44">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> source <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>, <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>emph<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>source<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb5-45">          <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb5-46">        <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="cb5-47">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb5-48">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="cb5-49">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-50"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
</div>
<ol type="1">
<li><a href="https://typst.app/docs/reference/visualize/stroke/">Directional stroke</a>: only the left edge has a border.</li>
<li><a href="https://typst.app/docs/reference/layout/block/#parameters-radius">Directional radius</a>: rounds only the right corners.</li>
<li><a href="https://typst.app/docs/reference/layout/place/"><code>place</code></a> positions the opening quote mark absolutely within the container.</li>
<li>Quote content rendered in italic with the foreground colour.</li>
<li>Attribution line conditionally displayed when author or source is provided.</li>
</ol>
</section>
<section id="step-3-create-the-wrapper-function" class="level3" data-number="7.3">
<h3 data-number="7.3" class="anchored" data-anchor-id="step-3-create-the-wrapper-function"><span class="header-section-number">7.3</span> Step 3: Create the Wrapper Function</h3>
<p>Focus on argument forwarding (<code>..args</code>) and runtime colour injection; this keeps Lua extraction and Typst rendering concerns cleanly separated.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-show.typ (quote-card wrapper excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-20" data-filename="_extensions/my-extension/typst-show.typ (quote-card wrapper excerpt)" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-20-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Quote card wrapper</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-20" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-20-2" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quote-card</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-20-3">  render-quote-card<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-20-4">    content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-20" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-20-5" class="code-annotation-target">    colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> get-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> effective-brand-mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-20" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-20-6" class="code-annotation-target">    ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-20-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-20-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-20" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-20" data-code-lines="2" data-code-annotation="1">Wrapper captures all extra named arguments via <a href="https://typst.app/docs/reference/foundations/arguments/">sink parameter</a> <code>..args</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-20" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-20" data-code-lines="5" data-code-annotation="2">Injects the runtime colour scheme; Lua cannot know this at parse time.</span>
</dd>
<dt data-target-cell="annotated-cell-20" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-20" data-code-lines="6" data-code-annotation="3"><a href="https://typst.app/docs/reference/foundations/arguments/#spreading">Spread operator</a> forwards <code>author</code>, <code>source</code>, and any other arguments to the rendering function.</span>
</dd>
</dl>
</section>
<section id="step-4-create-the-lua-handler" class="level3" data-number="7.4">
<h3 data-number="7.4" class="anchored" data-anchor-id="step-4-create-the-lua-handler"><span class="header-section-number">7.4</span> Step 4: Create the Lua Handler</h3>
<p>Focus on the last-header extraction branch and the shared wrapper reuse, which avoids reimplementing block wrapper assembly.</p>
<p>This handler reuses the same wrapped content pattern from Part 1’s panel, with one twist: instead of extracting the <em>first</em> heading as a title, it looks for a heading at the <em>end</em> of the content as an author attribution line. This kind of component-specific extraction logic is why some components need a custom handler rather than a factory-generated one.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/quote-card.lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-21" data-filename="_extensions/my-extension/_modules/quote-card.lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-21-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/wrapper.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%.lua$'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-21-2"></span>
<span id="annotated-cell-21-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-21-4"></span>
<span id="annotated-cell-21-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Process quote card: extract author from last heading or attributes</span></span>
<span id="annotated-cell-21-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>process_div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-21-7" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>attributes_to_table<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-8"></span>
<span id="annotated-cell-21-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'author'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-21-10" class="code-annotation-target">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">last_elem</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-21-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">last_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">t</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Header'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-21-12">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'author'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">last_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-13">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-21-14">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-21-15">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="annotated-cell-21-16">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-17">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span></span>
<span id="annotated-cell-21-18">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-19">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-20"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-21-21" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_typst_block_wrappers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-22">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-21-23">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">_</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ipairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-21-24">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-25">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-26">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-21-27">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span></span>
<span id="annotated-cell-21-28"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-29"></span>
<span id="annotated-cell-21-30"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-21" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="1" data-code-annotation="1">Load the shared wrapper module using Quarto’s path resolution from the extension root.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="7" data-code-annotation="2">Pandoc’s <code>div.attributes</code> is a metadata map, not a plain Lua table; this conversion produces a standard key-value table that can be iterated and passed to <code>build_attribute_string</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="10" data-code-annotation="3">Check the <em>last</em> element, unlike panel’s first-heading extraction; this supports the <code>## Author Name</code> syntax at the end of the div.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="21" data-code-annotation="4">Delegates to the shared wrapper module; the opening/closing pattern is identical to Part 1’s panels.</span>
</dd>
</dl>
</section>
<section id="step-5-register-the-component" class="level3" data-number="7.5">
<h3 data-number="7.5" class="anchored" data-anchor-id="step-5-register-the-component"><span class="header-section-number">7.5</span> Step 5: Register the Component</h3>
<p>Add the <code>quote-card</code> entry to <code>get_builtin_mappings()</code> in <code>config.lua</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_modules/config.lua (registration excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-22" data-filename="_extensions/my-extension/_modules/config.lua (registration excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-22-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>get_builtin_mappings<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="annotated-cell-22-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-22-3">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-22-4">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-highlight'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-22-5">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-22" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-22-6" class="code-annotation-target">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-22-7">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-22-8">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-22-9">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-22-10">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-22-11">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-22-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-22" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-22" data-code-lines="6" data-code-annotation="1"><code>arguments = true</code> ensures the parameter list is always generated, which is needed because <code>quote-card</code> always passes at least <code>colours:</code> via the wrapper function.</span>
</dd>
</dl>
<p>Then register the custom handler in <code>typst-markdown.lua</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (handler registration excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-23" data-filename="_extensions/my-extension/typst-markdown.lua (handler registration excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-23-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DIV_HANDLERS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-23" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-23-2" class="code-annotation-target">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quote-card'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quote_card</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">process_div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-23-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-23" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-23" data-code-lines="2" data-code-annotation="1"><code>DIV_HANDLERS</code> maps class names to custom handler functions; the quote card goes here because it has component-specific extraction logic (last-heading author) that a factory cannot express.</span>
</dd>
</dl>
</section>
</section>
<section id="advanced-topics" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="advanced-topics"><span class="header-section-number">8</span> Advanced Topics</h2>
<section id="checking-output-format" class="level3" data-number="8.1">
<h3 data-number="8.1" class="anchored" data-anchor-id="checking-output-format"><span class="header-section-number">8.1</span> Checking Output Format</h3>
<p>Filters should only run for Typst output; without this guard, transformation logic leaks into HTML, DOCX, and other targets.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (format guard excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-24" data-filename="_extensions/my-extension/typst-markdown.lua (format guard excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-24-1"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Div</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-24" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-24-2" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-24-3">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span></span>
<span id="annotated-cell-24-4">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-24-5">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- ... transformation logic</span></span>
<span id="annotated-cell-24-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-24" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-24" data-code-lines="2" data-code-annotation="1"><a href="https://quarto.org/docs/extensions/lua-api.html#quarto.doc.is_format"><code>quarto.doc.is_format()</code></a> checks the current render target; returning the element unchanged lets Pandoc handle it normally for non-Typst outputs.</span>
</dd>
</dl>
</section>
<section id="module-loading" class="level3" data-number="8.2">
<h3 data-number="8.2" class="anchored" data-anchor-id="module-loading"><span class="header-section-number">8.2</span> Module Loading</h3>
<p>Load shared modules using Quarto’s path resolution.</p>
<p>The <code>require_local(...)</code> helper keeps imports consistent and reduces repeated path-normalisation boilerplate.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-markdown.lua (module loading excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-25" data-filename="_extensions/my-extension/typst-markdown.lua (module loading excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-25" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-25-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">path</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-25-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">path</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%.lua$'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-25-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-25-4"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-25" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-25-5" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span>      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/config.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-25-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quote_card</span>  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/quote-card.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-25-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span>     <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/wrapper.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-25-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_utils</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/typst-utils.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-25" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-25" data-code-lines="1" data-code-annotation="1"><code>require_local</code> wraps <a href="https://quarto.org/docs/extensions/lua-api.html#quarto.utils.resolve_path"><code>quarto.utils.resolve_path</code></a> to anchor paths at the extension root; <code>gsub</code> strips <code>.lua</code> because <code>require</code> resolves by module name, not file path.</span>
</dd>
<dt data-target-cell="annotated-cell-25" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-25" data-code-lines="5" data-code-annotation="2">With a top-level <code>typst-markdown.lua</code>, all shared modules live under <code>_modules/</code>; adjust this prefix if your structure differs.</span>
</dd>
</dl>
</section>
<section id="filter-ordering" class="level3" data-number="8.3">
<h3 data-number="8.3" class="anchored" data-anchor-id="filter-ordering"><span class="header-section-number">8.3</span> Filter Ordering</h3>
<p>When using multiple filters, order matters. Declare filters in <code>_extension.yml</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-26" data-filename="_extensions/my-extension/_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-26-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> My Extension</span></span>
<span id="annotated-cell-26-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2.0.0</span></span>
<span id="annotated-cell-26-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contributes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-26-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formats</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-26-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-26-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> template.typ</span></span>
<span id="annotated-cell-26-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template-partials</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-26-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> typst-show.typ</span></span>
<span id="annotated-cell-26-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> components.typ</span></span>
<span id="annotated-cell-26-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-26" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-26-11" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> typst-markdown.lua</span></span>
<span id="annotated-cell-26-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shortcodes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-26-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> shortcodes.lua</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-26" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-26" data-code-lines="11" data-code-annotation="1">A single filter entry; a Lua filter file can return a table containing multiple filter tables rather than one flat filter table. Here <code>typst-markdown.lua</code> returns <code>{ {Meta=...}, {Div=..., Span=...} }</code>, an array of two filter tables. Pandoc applies each inner table as a separate pass in order; both passes operate on the same document. The first pass runs the <code>Meta</code> handler, which reads configuration from document metadata and stores it in module-level variables. The second pass runs the element handlers (<code>Div</code>, <code>Span</code>), which can then read the already-loaded configuration.</span>
</dd>
</dl>
</section>
<section id="debugging-tips" class="level3" data-number="8.4">
<h3 data-number="8.4" class="anchored" data-anchor-id="debugging-tips"><span class="header-section-number">8.4</span> Debugging Tips</h3>
<p>Use <code>quarto.log.warning()</code> to inspect values during development:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) debugging logs</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-27" data-filename="(conceptual) debugging logs" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-27" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-27-1" class="code-annotation-target"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>warning<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Processing div with classes: '</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.concat</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">', '</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-27" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-27-2" class="code-annotation-target"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>warning<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Attributes: '</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>MetaMap<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)))</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-27" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-27" data-code-lines="1" data-code-annotation="1"><a href="https://www.lua.org/manual/5.4/manual.html#pdf-table.concat"><code>table.concat</code></a> joins the class list into a readable string.</span>
</dd>
<dt data-target-cell="annotated-cell-27" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-27" data-code-lines="2" data-code-annotation="2">Wrap the attributes table in <a href="https://pandoc.org/lua-filters.html#type-metamap"><code>pandoc.MetaMap</code></a> so <code>stringify</code> can serialise it.</span>
</dd>
</dl>
<p>Output appears in the Quarto render log.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>example.qmd</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="example.qmd" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb6-2"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">title:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "Part 2 Extension Demo"</span></span>
<span id="cb6-3"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">format:</span></span>
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  my-extension-typst:</span></span>
<span id="cb6-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    syntax-highlighting: idiomatic</span></span>
<span id="cb6-6"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">extensions:</span></span>
<span id="cb6-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  typst-markdown:</span></span>
<span id="cb6-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    divs:</span></span>
<span id="cb6-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      custom-highlight: my-custom-highlight</span></span>
<span id="cb6-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb6-11"></span>
<span id="cb6-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Badges (WCAG-Aware)</span></span>
<span id="cb6-13"></span>
<span id="cb6-14">Inline badges with accessible colours:</span>
<span id="cb6-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Completed</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="success"},</span>
<span id="cb6-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Pending</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="warning"},</span>
<span id="cb6-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Failed</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="danger"},</span>
<span id="cb6-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Information</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="info"},</span>
<span id="cb6-19">and <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Default</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge}.</span>
<span id="cb6-20"></span>
<span id="cb6-21"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Highlight (Factory Handler, No Title)</span></span>
<span id="cb6-22"></span>
<span id="cb6-23">::: {.highlight}</span>
<span id="cb6-24">This highlight block uses the factory handler **without** title extraction.</span>
<span id="cb6-25">It simply wraps content in a styled container.</span>
<span id="cb6-26">:::</span>
<span id="cb6-27"></span>
<span id="cb6-28"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Panel (Factory Handler)</span></span>
<span id="cb6-29"></span>
<span id="cb6-30">::: {.panel style="info"}</span>
<span id="cb6-31"></span>
<span id="cb6-32"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Status Summary</span></span>
<span id="cb6-33"></span>
<span id="cb6-34">This panel uses the **info** style.</span>
<span id="cb6-35">The first heading is extracted as the title via the factory handler.</span>
<span id="cb6-36"></span>
<span id="cb6-37">:::</span>
<span id="cb6-38"></span>
<span id="cb6-39"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Quote Card (Attribute-Based Author)</span></span>
<span id="cb6-40"></span>
<span id="cb6-41">::: {.quote-card author="Alan Kay" source="1971"}</span>
<span id="cb6-42">The best way to predict the future is to invent it.</span>
<span id="cb6-43">:::</span>
<span id="cb6-44"></span>
<span id="cb6-45"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Quote Card (Heading-Based Author)</span></span>
<span id="cb6-46"></span>
<span id="cb6-47">::: {.quote-card source="1971"}</span>
<span id="cb6-48">The best way to predict the future is to invent it.</span>
<span id="cb6-49"></span>
<span id="cb6-50"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Alan Kay</span></span>
<span id="cb6-51"></span>
<span id="cb6-52">:::</span>
<span id="cb6-53"></span>
<span id="cb6-54"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Shortcode</span></span>
<span id="cb6-55"></span>
<span id="cb6-56">A live status indicator: {{&lt; status-badge label="Live" style="success" &gt;}}.</span>
<span id="cb6-57"></span>
<span id="cb6-58"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## User-Defined Component (Config System)</span></span>
<span id="cb6-59"></span>
<span id="cb6-60">::: {.custom-highlight}</span>
<span id="cb6-61">This block uses a **user-defined mapping** declared in the YAML front matter.</span>
<span id="cb6-62">The <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`custom-highlight`</span> class maps to the <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`my-custom-highlight`</span> Typst function via the configuration system.</span>
<span id="cb6-63">:::</span></code></pre></div></div>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/example.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2"><img src="https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/assets/example.png" class="img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="PDF render showing seven sections. Badges (WCAG-Aware): five WCAG-compliant inline badges labelled Completed, Pending, Failed, Information, and Default. Highlight: a light green content block with text explaining the factory handler requires no title extraction. Panel (Factory Handler): a blue info panel titled Status Summary, where the title was extracted from the first heading. Two Quote Card sections: each displays the quote The best way to predict the future is to invent it in italic with a left blue accent border, attributed to Alan Kay, 1971 — one using an attribute-based author, one using a heading-based author. Shortcode: a green Live badge inline in a sentence. User-Defined Component: an orange highlight block demonstrating a user-defined class mapped to a custom Typst function via the configuration system."></a></p>
</figure>
</div>
</div>
</div>
</div>
</section>
</section>
<section id="conclusion" class="level2" data-number="9">
<h2 data-number="9" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">9</span> Conclusion</h2>
<p>This tutorial covered advanced patterns for building production-quality Quarto Typst extensions:</p>
<ol type="1">
<li><strong>Handler factories</strong> reduce repetition by generating handlers with configured behaviour.</li>
<li><strong>Configuration systems</strong> enable user extensibility through metadata-driven mappings.</li>
<li><strong>Type-safe value conversion</strong> prevents syntax errors when passing values between Lua and Typst.</li>
<li><strong>WCAG compliance</strong> ensures accessible colour contrast in styled components.</li>
<li><strong>Shortcodes</strong> provide explicit syntax for components without natural markdown representation.</li>
</ol>
<p>You can download the complete code for this part of the tutorial as a zip file containing the extension with all components covered above.</p>
<div style="text-align: center;">
<p><a href="./tutorial-part2.zip" class="btn btn-outline-light btn-lg" target="_blank" rel="noopener noreferrer">Part 2 (ZIP)</a></p>
</div>
<div class="highlight">
<p>The separation of concerns between Lua (transformation) and Typst (rendering) enables flexibility, testability, and maintainability as your extension grows.</p>
</div>
<section id="further-resources" class="level3" data-number="9.1">
<h3 data-number="9.1" class="anchored" data-anchor-id="further-resources"><span class="header-section-number">9.1</span> Further Resources</h3>
<ul>
<li><a href="https://quarto.org/docs/extensions/creating.html">Quarto Documentation: Creating Extensions</a>.</li>
<li><a href="https://pandoc.org/lua-filters.html">Pandoc Lua Filters</a>.</li>
<li><a href="https://typst.app/docs/">Typst Documentation</a>.</li>
<li><a href="https://github.com/mcanouil/quarto-mcanouil">quarto-mcanouil Extension</a>.</li>
<li><a href="../../posts/2026-02-27-typst-template-tutorial-part1/">Part 1: The Lua-Typst Bridge</a>.</li>
</ul>


</section>
</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Building {Quarto} {Typst} {Templates:} {Advanced} {Patterns}
    {(Part} 2)},
  date = {2026-03-05},
  url = {https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-03-05). Building Quarto Typst Templates: Advanced
Patterns (Part 2). <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/">https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>extensions</category>
  <category>typst</category>
  <category>lua</category>
  <category>pdf</category>
  <guid>https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/</guid>
  <pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-03-05-typst-template-tutorial-part2/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Building Quarto Typst Templates: The Lua-Typst Bridge (Part 1)</title>
  <link>https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Feature image for &quot;Quarto + Typst Part 1&quot; blog post. Dark background
with the official Quarto logo (four blue quadrants with wordmark) and
Typst logo (teal wordmark) centered at the top. Below, &quot;Part 1&quot; in serif
font with subtitle &quot;Modern Scientific Publishing&quot;. File format pipeline
shows .qmd → .typ → .pdf. Decorative elements include floating code
snippets and gradient orbs in blue and teal.
" width="600"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Quarto Version
</div>
</div>
<div class="callout-body-container callout-body">
<p>This tutorial was written and tested with <strong>Quarto CLI 1.9.23 (pre-release)</strong>. Some APIs, template syntax, or extension behaviours may differ in earlier stable releases.</p>
</div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>TL;DR
</div>
</div>
<div class="callout-body-container callout-body">
<p>This tutorial introduces the dual-layer architecture for building Quarto Typst extensions: Lua filters intercept markdown elements at parse time, Typst functions render styled output at render time.</p>
<ul>
<li><strong>Badges</strong>: inline pill-shaped indicators built with <code>pandoc.RawInline</code> and a simple Typst <code>box</code> function.</li>
<li><strong>Panels</strong>: block components that wrap rich nested content with opening and closing Typst calls via <code>pandoc.RawBlock</code>.</li>
<li><strong>Template variables</strong>: <code>effective-brand-mode</code> bridges Lua parse-time and Typst render-time for colour scheme selection.</li>
</ul>
<div style="text-align: center;">
<p><a href="./tutorial-part1.zip" class="btn btn-outline-light" target="_blank" rel="noopener noreferrer">Part 1 (ZIP)</a></p>
</div>
</div>
</div>
<section id="why-pdf-branding-matters" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="why-pdf-branding-matters"><span class="header-section-number">1</span> Why PDF Branding Matters</h2>
<p>In professional environments, document presentation is not merely aesthetic; it is a reflection of organisational identity and credibility. Whether you are producing academic papers, corporate reports, client proposals, or technical documentation, consistent branding transforms disparate documents into a cohesive portfolio that reinforces trust and recognition.</p>
<p>Yet achieving this consistency manually is fraught with challenges. Copy-pasting styles between documents introduces errors. Updating a colour scheme across dozens of files becomes a maintenance nightmare. And when multiple team members contribute to documents, stylistic drift is inevitable.</p>
<div class="highlight">
<p><strong>Reproducible branding</strong> through code eliminates manual styling errors and ensures every document reflects your organisation’s identity automatically.</p>
</div>
<p>This is where programmatic document styling becomes valuable.</p>
<p>Typst offers a modern, programmable alternative to LaTeX for PDF generation. Its syntax is cleaner, compilation is faster, and its scripting capabilities make complex layouts achievable without the arcane incantations that LaTeX often requires. When paired with Quarto’s publishing ecosystem, Typst becomes a powerful foundation for branded document production.</p>
</section>
<section id="prior-art-getting-started-with-typst-and-quarto" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="prior-art-getting-started-with-typst-and-quarto"><span class="header-section-number">2</span> Prior Art: Getting Started with Typst and Quarto</h2>
<p>Before diving into advanced techniques, it is worth acknowledging excellent foundational resources recently published. David Keyes wrote a comprehensive introduction to using Typst with Quarto at <a href="https://rfortherestofus.com/2025/11/quarto-typst-pdf">R for the Rest of Us</a>. If you are new to Typst or want a gentler introduction, that post provides an excellent starting point.</p>
<p>This tutorial builds upon that foundation. Where David’s post shows how to use Typst templates with Quarto, this tutorial explores how to <em>build</em> sophisticated templates that pair Lua filters with Typst rendering functions. What does this mean in practice? It means creating reusable components like badges, panels, and quote cards that authors can invoke with simple markdown syntax instead of writing Typst code directly.</p>
<p>In this tutorial, you will learn the architectural patterns that enable rich, interactive components whilst maintaining clean, extensible code.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This is <strong>Part 1</strong> of a two-part series. This part covers the foundational concepts; <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> explores advanced patterns including handler factories, configuration systems, and WCAG-compliant styling.</p>
<p>The examples here are generic, but the <a href="https://github.com/mcanouil/quarto-mcanouil">‘mcanouil’ Quarto extension</a> demonstrates these patterns in a production context.</p>
</div>
</div>
</section>
<section id="the-problem-bridging-markdown-and-typst" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="the-problem-bridging-markdown-and-typst"><span class="header-section-number">3</span> The Problem: Bridging Markdown and Typst</h2>
<p>Consider a simple scenario: you want users to write badges in their Quarto documents using familiar markdown syntax, but have them render as styled components in Typst.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb1-1">The task is <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Completed</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="success"} and ready for review.</span></code></pre></div></div>
<p>This should produce a styled badge in the PDF output. But here is the challenge: Typst templates alone cannot intercept and transform markdown elements. Typst receives content <em>after</em> Quarto and Pandoc have processed it. Without a Lua filter, Pandoc’s default Typst writer simply drops the <code>.badge</code> class and outputs the text content with no styling; your Typst template never sees the class at all.</p>
<p>The solution requires two layers working in concert:</p>
<ol type="1">
<li><strong>Lua filters</strong> that intercept markdown elements during Pandoc processing and generate Typst code.</li>
<li><strong>Typst functions</strong> that receive that generated code and render styled output.</li>
</ol>
<p>The transformation looks like this:</p>
<div id="fig-bridge" class="quarto-float quarto-figure quarto-figure-center anchored">
<figure class="quarto-float quarto-float-fig figure">
<div aria-describedby="fig-bridge-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="../../posts/2026-02-27-typst-template-tutorial-part1/./typst-render/fig-bridge.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Figure&nbsp;1: The bridge between markdown and Typst."><img src="https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/typst-render/fig-bridge.png" class="img-fluid w-75 mx-auto d-block quarto-figure quarto-figure-center figure-img" alt="Two-band cetz diagram. Top band labelled 'Lua layer (Pandoc + filter)' shows four boxes left to right: the markdown source `[Completed]{.badge colour=\&quot;success\&quot;}`, then 'Pandoc AST', then 'Lua filter', then the generated Typst code `#simple-badge(colour: \&quot;success\&quot;, mode: effective-brand-mode)[Completed]`. An arrow drops from that last box down into a second band labelled 'Typst layer (renderer)', which contains a 'Typst function' box and a rendered green pill badge reading 'Completed' with a tick."></a></p>
</figure>
</div>
</div>
<figcaption class="quarto-float-caption-bottom quarto-float-caption quarto-float-fig" id="fig-bridge-caption-0ceaefa1-69ba-4598-a22c-09a6ac19f8ca">
Figure&nbsp;1: The bridge between markdown and Typst.
</figcaption>
</figure>
</div>
</section>
<section id="the-dual-layer-architecture" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="the-dual-layer-architecture"><span class="header-section-number">4</span> The Dual-Layer Architecture</h2>
<p>This tutorial explores a dual-layer architecture where Lua handles document transformation and Typst handles rendering.</p>
<section id="layer-1-document-processing-lua" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="layer-1-document-processing-lua"><span class="header-section-number">4.1</span> Layer 1: Document Processing (Lua)</h3>
<p>Lua filters operate on Pandoc’s Abstract Syntax Tree (AST), the intermediate representation of your document after Quarto parses the markdown. At this stage, you can:</p>
<ul>
<li>Identify elements by their classes (<em>e.g.</em>, <code>.badge</code>, <code>.panel</code>).</li>
<li>Extract attributes (<em>e.g.</em>, <code>colour="success"</code>).</li>
<li>Generate raw Typst code to replace those elements.</li>
</ul>
<p>The key Pandoc functions for this bridge are:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) raw element API</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-2" data-filename="(conceptual) raw element API" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- For block-level elements (divs, paragraphs)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-2" class="code-annotation-target"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#my-function[content]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-2-3"></span>
<span id="annotated-cell-2-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- For inline elements (spans, text)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-5" class="code-annotation-target"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#my-function[content]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="2" data-code-annotation="1">Creates a block-level raw element; see <a href="https://pandoc.org/lua-filters.html#type-rawblock">Pandoc Lua types</a>.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="5" data-code-annotation="2">Creates an inline raw element; see <a href="https://pandoc.org/lua-filters.html#type-rawinline">Pandoc Lua types</a>.</span>
</dd>
</dl>
<p>These functions tell Pandoc to pass the string directly to Typst without further processing.</p>
</section>
<section id="layer-2-template-rendering-typst" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="layer-2-template-rendering-typst"><span class="header-section-number">4.2</span> Layer 2: Template Rendering (Typst)</h3>
<p>On the Typst side, you define functions that receive the generated code and produce styled output. These functions have access to:</p>
<ul>
<li>The document’s colour scheme and typography settings.</li>
<li>Page dimensions and layout context.</li>
<li>Typst’s full styling capabilities.</li>
</ul>
<p>A simple Typst function might look like:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) simple Typst function</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="(conceptual) simple Typst function" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple-badge</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"neutral"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-3-2" class="code-annotation-target">  box<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-3-3" class="code-annotation-target">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">230</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-3-4">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-5">    inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-6">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-3-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-3-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="1" data-code-annotation="1">Function definition with content parameter and optional named parameter.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="2" data-code-annotation="2"><a href="https://typst.app/docs/reference/layout/box/"><code>box</code></a> creates an inline container.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="3" data-code-annotation="3"><a href="https://typst.app/docs/reference/visualize/color/#definitions-luma"><code>luma</code></a> creates a greyscale colour (0-255).</span>
</dd>
</dl>
</section>
<section id="why-this-separation" class="level3" data-number="4.3">
<h3 data-number="4.3" class="anchored" data-anchor-id="why-this-separation"><span class="header-section-number">4.3</span> Why This Separation?</h3>
<p>This dual-layer approach offers several advantages:</p>
<ul>
<li><strong>Separation of concerns</strong>: Lua handles “what to transform”, Typst handles “how to render”.</li>
<li><strong>Flexibility</strong>: Change the rendering without touching the transformation logic, or vice versa.</li>
<li><strong>Testability</strong>: Each layer can be developed and tested independently.</li>
<li><strong>Reusability</strong>: Typst functions can be called directly in Typst code, not just through the Lua bridge.</li>
</ul>
</section>
</section>
<section id="extension-structure-overview" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="extension-structure-overview"><span class="header-section-number">5</span> Extension Structure Overview</h2>
<p>Before diving into component implementation, let us establish the minimal file organisation needed for a Typst extension.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="txt" style="background: #f1f3f5;"><pre class="sourceCode txt cw-auto code-with-copy"><code class="sourceCode default"><span id="cb2-1">_extensions/my-extension/</span>
<span id="cb2-2">├── _extension.yml       # Extension manifest</span>
<span id="cb2-3">├── template.typ         # Main Typst template</span>
<span id="cb2-4">├── typst-show.typ       # Wrapper functions for components</span>
<span id="cb2-5">├── components.typ       # Component rendering functions</span>
<span id="cb2-6">├── wrapper.lua          # Shared wrapper helpers loaded by filter.lua</span>
<span id="cb2-7">└── filter.lua           # Lua filter entrypoint</span></code></pre></div></div>
<p>The <code>_extension.yml</code> manifest declares how these pieces fit together:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="_extensions/my-extension/_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> My Extension</span></span>
<span id="cb3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0.0</span></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contributes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formats</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> template.typ</span></span>
<span id="cb3-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template-partials</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> typst-show.typ</span></span>
<span id="cb3-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> components.typ</span></span>
<span id="cb3-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">filters</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> filter.lua</span></span></code></pre></div></div>
</div>
<p>The <code>template.typ</code> file is the main Typst entry point. It includes the component definitions and wrapper functions via Pandoc template syntax, and defines any shared helpers. For this tutorial, it defines the <code>get-colours</code> helper that the wrapper functions rely on:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/template.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename="_extensions/my-extension/template.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Include component definitions via template partial</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-2" class="code-annotation-target"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$components.typ()$</span></span>
<span id="annotated-cell-6-3"></span>
<span id="annotated-cell-6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Colour scheme for the document</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-6-5" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">get-colours</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-6-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> mode <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-6-7">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>background: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> foreground: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">230</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> muted: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">150</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-6-8">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-6-9">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>background: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">255</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> foreground: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">30</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> muted: luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">100</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-6-10">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-6-11"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-6-12"></span>
<span id="annotated-cell-6-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Include wrapper functions via template partial</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-6-14" class="code-annotation-target"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$typst-show.typ()$</span></span>
<span id="annotated-cell-6-15"></span>
<span id="annotated-cell-6-16"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$for(header-includes)$</span></span>
<span id="annotated-cell-6-17"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$header-includes$</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-6-18" class="code-annotation-target"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$endfor$</span></span>
<span id="annotated-cell-6-19"></span>
<span id="annotated-cell-6-20"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$body$</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="2" data-code-annotation="1">Pandoc template syntax <code>$partial()$</code> includes another file; <code>components.typ</code> must be declared before <code>typst-show.typ</code> so the rendering functions are in scope when the wrappers reference them.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="5" data-code-annotation="2"><code>get-colours</code> returns a dictionary with <code>background</code>, <code>foreground</code>, and <code>muted</code> keys; <code>typst-show.typ</code>’s wrapper functions call this at render time.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="14" data-code-annotation="3">Wrapper functions are included after <code>get-colours</code> is defined so they can call it.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="18" data-code-annotation="4">Pandoc’s template loop injects any per-document Typst preamble content (e.g., items declared via <code>include-in-header</code> in document YAML) before the body.</span>
</dd>
</dl>
<div class="highlight">
<p>Production extensions like <a href="https://github.com/mcanouil/quarto-mcanouil">quarto-mcanouil</a> use more elaborate structures with shared modules and component libraries. <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> covers those architectural patterns.</p>
</div>
<p>With this foundation established, we can now explore how to build components, starting with simple inline badges and progressing to block-level panels.</p>
</section>
<section id="first-component-badges-inline-elements" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="first-component-badges-inline-elements"><span class="header-section-number">6</span> First Component: Badges (Inline Elements)</h2>
<p>Badges are compact inline indicators, perfect for showing status, categories, or tags. They represent the simplest component type: a span element with optional attributes that transforms into a styled box.</p>
<section id="understanding-pandoc-span-elements" class="level3" data-number="6.1">
<h3 data-number="6.1" class="anchored" data-anchor-id="understanding-pandoc-span-elements"><span class="header-section-number">6.1</span> Understanding Pandoc Span Elements</h3>
<p>When you write <code>[Completed]{.badge colour="success"}</code> in your Quarto document, Pandoc parses this into a Span element in its AST. The Span contains:</p>
<ul>
<li><strong><code>span.content</code></strong>: The text content (“Completed”).</li>
<li><strong><code>span.classes</code></strong>: A list of classes ([“badge”]).</li>
<li><strong><code>span.attributes</code></strong>: A table of key-value pairs (<code>{colour="success"}</code>).</li>
</ul>
<p>A Lua filter can inspect these properties and decide how to transform the element.</p>
</section>
<section id="building-the-typst-function-start-simple" class="level3" data-number="6.2">
<h3 data-number="6.2" class="anchored" data-anchor-id="building-the-typst-function-start-simple"><span class="header-section-number">6.2</span> Building the Typst Function: Start Simple</h3>
<p>Let us begin with the simplest possible badge function in Typst:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) badges.typ v1</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="(conceptual) badges.typ v1" style="background: #f1f3f5;"><pre class="sourceCode typst code-with-copy"><code class="sourceCode typst"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple-badge</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb4-2">  box<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb4-3">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">230</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb4-4">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-5">    inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb4-6">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb4-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
</div>
<p>This creates a grey box with rounded corners around any content. It works, but it lacks colour customisation and accessibility considerations.</p>
</section>
<section id="adding-colour-support" class="level3" data-number="6.3">
<h3 data-number="6.3" class="anchored" data-anchor-id="adding-colour-support"><span class="header-section-number">6.3</span> Adding Colour Support</h3>
<p>Next, let us add colour support with predefined semantic colours:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) badges.typ v2</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-8" data-filename="(conceptual) badges.typ v2" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><a class="code-annotation-anchor" data-target-cell="annotated-cell-8" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-8-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">get-badge-colour</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>colour-name<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-2">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">is-dark</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> mode <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span></span>
<span id="annotated-cell-8-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour-name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"success"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#33cc88"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#009955"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-5">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour-name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"warning"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-6">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#ee9944"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#cc6600"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-7">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour-name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"danger"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-8">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#ff5555"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#cc0000"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-9">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> colour-name <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#5599ee"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> rgb<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#0066cc"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-11">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> is-dark <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">170</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> luma<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">128</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// neutral default</span></span>
<span id="annotated-cell-8-13">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-8-15"></span>
<span id="annotated-cell-8-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">simple-badge</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"neutral"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-8-17">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">base</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get-badge-colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>colour<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-18">  box<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-8-19">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>lighten<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">80%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-8-20">    stroke<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.5pt</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>lighten<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">40%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-8-21">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">4pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-8-22">    inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.25em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-8-23">    text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.85em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> base<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-24">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-8-25"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-8" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-8" data-code-lines="1" data-code-annotation="1">Here, colours are hard-coded for simplicity; in a production extension you could retrieve them from <code>_brand.yml</code> (Quarto’s brand configuration file that centralises colours, fonts, and logos for consistent styling) using the Quarto Brand Lua API (see Note&nbsp;1).</span>
</dd>
</dl>
<p>Now users can specify <code>colour: "success"</code> and get a green badge. The <code>mode</code> parameter selects light or dark colour palettes.</p>
</section>
<section id="building-the-lua-handler" class="level3" data-number="6.4">
<h3 data-number="6.4" class="anchored" data-anchor-id="building-the-lua-handler"><span class="header-section-number">6.4</span> Building the Lua Handler</h3>
<p>With the Typst function ready, we need a Lua handler to generate calls to it. In Typst, function calls use the pattern <code>#function-name(named-arg: value)[content]</code>: <code>#</code> marks a function call, named arguments go in parentheses, and the content the function wraps goes in square brackets. Unlike a plain string, square brackets in Typst create a <em>content block</em>: a distinct type that Typst renders as formatted output, preserving nested markup such as bold, italic, and inner function calls. The Lua handler builds this string and hands it to Pandoc as raw Typst code. Here is the simplest approach:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) badges.lua v1</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-9" data-filename="(conceptual) badges.lua v1" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-9-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> Span<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-9-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Only process spans with the 'badge' class</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-9-3" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>includes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-9-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span></span>
<span id="annotated-cell-9-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-9-6"></span>
<span id="annotated-cell-9-7">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Extract content as plain text</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-9-8" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-9-9"></span>
<span id="annotated-cell-9-10">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Get the colour attribute (default to neutral)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-9-11" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attributes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'neutral'</span></span>
<span id="annotated-cell-9-12"></span>
<span id="annotated-cell-9-13">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Generate Typst function call</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-9-14" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-9-15">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#simple-badge(colour: "%s", mode: effective-brand-mode)[%s]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-9-16">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-9-17">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span></span>
<span id="annotated-cell-9-18">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-9-19"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-9" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-9-20" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-9-21"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-9" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="1" data-code-annotation="1"><a href="https://pandoc.org/lua-filters.html#type-span">Span filter function</a> receives each span element.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="3" data-code-annotation="2">Check if span has the target class; return unchanged if not.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="8" data-code-annotation="3"><a href="https://pandoc.org/lua-filters.html#pandoc.utils.stringify"><code>stringify</code></a> converts AST content to plain text, stripping all inline formatting (bold, italic, links); for badge labels this is intentional because short status words do not need inline formatting.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="11" data-code-annotation="4">Access attributes as a Lua table; provide default value.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="14" data-code-annotation="5">Build the Typst function call string with parameters; <code>effective-brand-mode</code> is a Typst variable name defined in <code>typst-show.typ</code> via Pandoc template syntax. Quarto resolves it before passing the file to Typst, so its value is never known to Lua. It is introduced and explained in the next section.</span>
</dd>
<dt data-target-cell="annotated-cell-9" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-9" data-code-lines="20" data-code-annotation="6">Return raw Typst code that Pandoc passes through unchanged.</span>
</dd>
</dl>
<p>This works for our simple use case. The Lua filter checks if a span has the <code>badge</code> class, extracts its content and attributes, and generates a Typst function call.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The attribute value here is always a Lua string because Pandoc exposes HTML attributes as strings. Typst syntax distinguishes strings (<code>"hello"</code>), numbers (<code>42</code>), and keywords (<code>none</code>, <code>true</code>); passing the wrong form produces a syntax error. The <code>wrapper.lua</code> module includes a minimal <code>typst_value</code> helper that handles these common cases: string pass-through for Typst keywords (<code>"none"</code>, <code>"true"</code>, <code>"false"</code>), passing Lua numbers as bare values (so <code>42</code> becomes <code>42</code> in Typst, not <code>"42"</code>), and wrapping everything else in quotes. <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> replaces this with a more robust implementation that also handles actual Lua booleans, <code>nil</code>, and Typst measurement units like <code>1em</code> or <code>2.5pt</code>.</p>
<p>This handler also does not escape Typst special characters in <code>content</code>. Badge labels containing <code>[</code>, <code>]</code>, or <code>#</code> would produce broken Typst syntax because Typst interprets those characters as markup inside <code>[...]</code> content blocks. For typical short labels like <code>"Completed"</code> or <code>"v1.2.0"</code> this is not an issue, but a production implementation would <em>escape</em> those characters before inserting content into the generated code: replacing <code>]</code> with <code>\]</code>, <code>[</code> with <code>\[</code>, and <code>#</code> with <code>\#</code> so Typst does not treat them as markup.</p>
</div>
</div>
</section>
<section id="resolving-brand-mode-at-render-time" class="level3" data-number="6.5">
<h3 data-number="6.5" class="anchored" data-anchor-id="resolving-brand-mode-at-render-time"><span class="header-section-number">6.5</span> Resolving Brand Mode at Render Time</h3>
<p>There is one more piece to the puzzle. The Typst rendering function needs to know whether the document uses a light or dark colour scheme, but Lua generates code at parse time before that value is resolved. The solution is a <strong>template variable</strong> in <code>typst-show.typ</code>.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>Quarto Template Syntax
</div>
</div>
<div class="callout-body-container callout-body">
<p>Quarto’s Typst partials use Pandoc template syntax for variable interpolation:</p>
<ul>
<li><code>$variable$</code> inserts a metadata value.</li>
<li><code>$if(variable)$...$endif$</code> conditionally includes content.</li>
<li><code>$if(variable)$...$else$...$endif$</code> provides a fallback value.</li>
</ul>
<p>These are resolved at render time, not parse time. See <a href="https://quarto.org/docs/journals/templates.html#typst-partials">Typst partials</a> for details.</p>
</div>
</div>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-show.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-10" data-filename="_extensions/my-extension/typst-show.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-10-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Define brand mode with default fallback</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-10" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-10-2" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">effective-brand-mode</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$if(brand-mode)$$brand-mode$$else$light$endif$"</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-10" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-10" data-code-lines="2" data-code-annotation="1">Quarto template syntax (<code>$if()$</code>) resolves at render time. If <code>brand-mode</code> is not set in the document’s YAML front matter, this expression falls back to <code>"light"</code>. To use dark mode, add <code>brand-mode: dark</code> to the document YAML. See <a href="https://quarto.org/docs/journals/templates.html#typst-partials">Typst partials</a>.</span>
</dd>
</dl>
<p>The Lua filter generates code that references <code>effective-brand-mode</code> by name (<em>e.g.</em>, <code>#simple-badge(colour: "success", mode: effective-brand-mode)[Completed]</code>). At render time, Typst resolves the variable and <code>simple-badge</code> passes it to <code>get-badge-colour</code> to select the right colour palette.</p>
<div class="highlight">
<p>The Lua filter generates code that references <code>effective-brand-mode</code> <strong>by name</strong>. Typst resolves the variable at render time via Pandoc template syntax, so each badge picks the right colour palette without Lua needing to know the mode.</p>
</div>
<div id="nte-brand-lua" class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note&nbsp;1: Quarto Brand Lua API
</div>
</div>
<div class="callout-body-container callout-body">
<p>Quarto also exposes a <code>quarto.brand</code> Lua module that lets filters query <code>_brand.yml</code> values directly at parse time:</p>
<ul>
<li><code>quarto.brand.has_mode(mode)</code>: returns <code>true</code> if the document has a brand definition for the given mode (<code>"light"</code> or <code>"dark"</code>).</li>
<li><code>quarto.brand.get_color(mode, name)</code>: returns a brand colour by name in the output format (<em>e.g.</em>, <code>quarto.brand.get_color("light", "primary")</code>).</li>
<li><code>quarto.brand.get_color_css(mode, name)</code>: returns the colour in CSS format.</li>
<li><code>quarto.brand.get_typography(mode, name)</code>: returns a table of typography options (family, size, weight, etc.) for the given element.</li>
<li><code>quarto.brand.get_logo(mode, name)</code>: returns logo resource paths and alt text.</li>
</ul>
<p>This approach is useful when you need brand values <em>inside</em> the Lua filter itself, for instance to compute contrast ratios or choose between different rendering strategies before emitting Typst code. In this tutorial, we pass the mode to Typst instead, keeping colour logic in a single layer.</p>
<p>As a rule of thumb: use <code>quarto.brand</code> when the Lua filter itself needs to act on a brand value (computation, branching); use a template variable when the Typst rendering function should resolve the value at render time.</p>
</div>
</div>
</section>
</section>
<section id="block-components-panels-wrapped-content" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="block-components-panels-wrapped-content"><span class="header-section-number">7</span> Block Components: Panels (Wrapped Content)</h2>
<p>Badges are inline elements with simple text content. Panels are block elements that can contain rich, nested content: paragraphs, lists, images, even code blocks. This introduces a new challenge: how do we preserve and pass through complex content whilst still wrapping it in our Typst function?</p>
<section id="understanding-pandoc-div-elements" class="level3" data-number="7.1">
<h3 data-number="7.1" class="anchored" data-anchor-id="understanding-pandoc-div-elements"><span class="header-section-number">7.1</span> Understanding Pandoc Div Elements</h3>
<p>When you write a fenced div in Quarto:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb5-1">::: {.panel style="info" title="Important Note"}</span>
<span id="cb5-2">This panel contains **rich content** including:</span>
<span id="cb5-3"></span>
<span id="cb5-4"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Bullet points.</span>
<span id="cb5-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>_Formatted text_.</span>
<span id="cb5-6"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>And more.</span>
<span id="cb5-7">:::</span></code></pre></div></div>
<p>Pandoc parses this into a Div element with:</p>
<ul>
<li><strong><code>div.content</code></strong>: An array of block elements (paragraphs, lists, etc.).</li>
<li><strong><code>div.classes</code></strong>: A list of classes ([“panel”]).</li>
<li><strong><code>div.attributes</code></strong>: A table of key-value pairs.</li>
</ul>
<p>The critical difference from spans: <code>div.content</code> is not simple text but a list of Pandoc AST nodes that need further processing.</p>
</section>
<section id="the-wrapped-content-pattern" class="level3" data-number="7.2">
<h3 data-number="7.2" class="anchored" data-anchor-id="the-wrapped-content-pattern"><span class="header-section-number">7.2</span> The Wrapped Content Pattern</h3>
<p>Unlike badges where we convert content to plain text, panels need to preserve their content structure. The solution is to wrap the content with opening and closing Typst code:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) wrapped content pattern</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="(conceptual) wrapped content pattern" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Instead of: #panel[plain text]</span></span>
<span id="cb6-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- We generate:</span></span>
<span id="cb6-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- #panel(style: "info")[</span></span>
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--   &lt;original content passes through&gt;</span></span>
<span id="cb6-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- ]</span></span>
<span id="cb6-6"></span>
<span id="cb6-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb6-8">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#my-panel(style: "info")['</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Opening</span></span>
<span id="cb6-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Original div.content elements go here</span></span>
<span id="cb6-10">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">']'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Closing</span></span>
<span id="cb6-11"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
</div>
<p>For example, given this markdown:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb7-1">::: {.panel style="info"}</span>
<span id="cb7-2">This note contains **bold** text.</span>
<span id="cb7-3">:::</span></code></pre></div></div>
<p>The filter builds this structure, with Pandoc converting the inner content to Typst:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb8-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>my-panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"info"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)[</span></span>
<span id="cb8-2">  This note contains *bold* text.</span>
<span id="cb8-3"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span></code></pre></div></div>
<p>This approach lets Pandoc continue processing the inner content (converting markdown to Typst), whilst our wrapper provides the styling context.</p>
</section>
<section id="building-the-wrapped-content-handler" class="level3" data-number="7.3">
<h3 data-number="7.3" class="anchored" data-anchor-id="building-the-wrapped-content-handler"><span class="header-section-number">7.3</span> Building the Wrapped Content Handler</h3>
<p><code>wrapper.lua</code> uses the standard Lua module pattern. A <code>local M = {}</code> table is created at the top of the file, public functions are attached to it as <code>function M.fn_name(...)</code>, and <code>return M</code> at the end exports them. When <code>filter.lua</code> loads the module with <code>require_local("wrapper.lua")</code>, it gets back that table and calls <code>wrapper.build_wrapped_content(...)</code>. Private helper functions (like <code>attributes_to_table</code> and <code>build_attribute_string</code>) remain as plain <code>local function</code> inside the file and are not visible outside it.</p>
<p>The wrapper module provides two public utilities for this pattern:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/wrapper.lua (excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-15" data-filename="_extensions/my-extension/wrapper.lua (excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-15-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-15-2"></span>
<span id="annotated-cell-15-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build Typst block wrappers with optional attributes</span></span>
<span id="annotated-cell-15-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_typst_block_wrappers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-5">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">has_attributes</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">next</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">~=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span></span>
<span id="annotated-cell-15-6"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-15-7" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">has_attributes</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-15-8">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> build_attribute_string<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-15-9" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s(%s)['</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attr_string</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">']'</span></span>
<span id="annotated-cell-15-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span></span>
<span id="annotated-cell-15-11">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#%s['</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">']'</span></span>
<span id="annotated-cell-15-12">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-15-13"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-15-14"></span>
<span id="annotated-cell-15-15"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Build wrapped content for components</span></span>
<span id="annotated-cell-15-16"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extract_title</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-17">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> attributes_to_table<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-18"></span>
<span id="annotated-cell-15-19">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Optionally extract first heading as title attribute</span></span>
<span id="annotated-cell-15-20">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extract_title</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-15-21" class="code-annotation-target">    <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>extract_first_heading_as_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-22">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-15-23"></span>
<span id="annotated-cell-15-24">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_typst_block_wrappers<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-25"></span>
<span id="annotated-cell-15-26">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Build result: opening wrapper, content, closing wrapper</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-15-27" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">opening</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-15-28" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">_</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ipairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-15-29">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">item</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-15-30">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-15" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-15-31" class="code-annotation-target">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawBlock<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">closing</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-15-32"></span>
<span id="annotated-cell-15-33">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">result</span></span>
<span id="annotated-cell-15-34"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-15" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="7" data-code-annotation="1">Generate parameter list when attributes exist <em>or</em> when the config mandates it (<code>arguments = true</code>); without the <code>or config.arguments</code> branch, an empty attribute list would never be generated even when the Typst function expects it.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="9" data-code-annotation="2">Returns two values: opening bracket with function call, closing bracket.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="21" data-code-annotation="3">Public functions within the module call each other via <code>M.fn_name</code>; this ensures the call always hits the module’s own implementation.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="27" data-code-annotation="4">Opening wrapper becomes first element in result list.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="28" data-code-annotation="5">Original content passes through; Pandoc continues processing nested markdown.</span>
</dd>
<dt data-target-cell="annotated-cell-15" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-15" data-code-lines="31" data-code-annotation="6">Closing bracket completes the Typst function call.</span>
</dd>
</dl>
</section>
<section id="title-extraction-a-useful-pattern" class="level3" data-number="7.4">
<h3 data-number="7.4" class="anchored" data-anchor-id="title-extraction-a-useful-pattern"><span class="header-section-number">7.4</span> Title Extraction: A Useful Pattern</h3>
<p>Many block components benefit from extracting the first heading as a title. Instead of requiring users to write:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb9-1">::: {.panel title="My Title"}</span>
<span id="cb9-2">Content here.</span>
<span id="cb9-3">:::</span></code></pre></div></div>
<p>You can let them write:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb10-1">::: {.panel}</span>
<span id="cb10-2"></span>
<span id="cb10-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># My Title</span></span>
<span id="cb10-4"></span>
<span id="cb10-5">Content here.</span>
<span id="cb10-6">:::</span></code></pre></div></div>
<p>The <code>extract_first_heading_as_title</code> function handles this:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/wrapper.lua (excerpt)</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-18" data-filename="_extensions/my-extension/wrapper.lua (excerpt)" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-18-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>extract_first_heading_as_title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-18" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-18-2" class="code-annotation-target">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'title'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">and</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-18-3">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-18" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-18-4" class="code-annotation-target">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">t</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'Header'</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-18-5">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Extract header text as title attribute</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-18" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-18-6" class="code-annotation-target">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attrs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'title'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">first_elem</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-18-7">      <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Remove header from content</span></span>
<span id="annotated-cell-18-8">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-18" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-18-9" class="code-annotation-target">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-18-10">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">table.insert</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="annotated-cell-18-11">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-18-12">      <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">el</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">new_content</span></span>
<span id="annotated-cell-18-13">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-18-14">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-18-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-18-16"></span>
<span id="annotated-cell-18-17"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">M</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-18" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-18" data-code-lines="2" data-code-annotation="1">Skip if title already provided via attributes or div is empty.</span>
</dd>
<dt data-target-cell="annotated-cell-18" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-18" data-code-lines="4" data-code-annotation="2"><code>.t</code> is the element type; see <a href="https://pandoc.org/lua-filters.html#element-types">Pandoc AST types</a>.</span>
</dd>
<dt data-target-cell="annotated-cell-18" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-18" data-code-lines="6" data-code-annotation="3">Convert header inline content to plain string for the title attribute.</span>
</dd>
<dt data-target-cell="annotated-cell-18" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-18" data-code-lines="9" data-code-annotation="4">Rebuild content list without the first element (the extracted header).</span>
</dd>
</dl>
</section>
<section id="the-typst-panel-function" class="level3" data-number="7.5">
<h3 data-number="7.5" class="anchored" data-anchor-id="the-typst-panel-function"><span class="header-section-number">7.5</span> The Typst Panel Function</h3>
<p>On the Typst side, the panel function receives the attributes and content:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>(conceptual) panels.typ excerpt</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-19" data-filename="(conceptual) panels.typ excerpt" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-19-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">render-panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-19-2" class="code-annotation-target">  content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-3">  title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-4">  style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"subtle"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-5">  icon<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-19-6" class="code-annotation-target">  colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>:<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="annotated-cell-19-7">  breakable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">false</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-8"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-19-9" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">panel-colours</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> get-panel-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>style<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-19-10"></span>
<span id="annotated-cell-19-11">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Build optional title display</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-19-12" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">title-display</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> has-content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-19-13">    block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-19-14">      below<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-15">      text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-19-16">        size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.1em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-17">        weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"semibold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-18">        fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> panel-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-19">        title</span>
<span id="annotated-cell-19-20">      <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-19-21">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-19-22">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-19-23"></span>
<span id="annotated-cell-19-24">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Render the panel</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-19-25" class="code-annotation-target">  block<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-19-26">    width<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-27">    fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> panel-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>background<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-28">    stroke<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1pt</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> panel-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>border<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-29">    radius<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">8pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-30">    inset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2em</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-19" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-19-31" class="code-annotation-target">    breakable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> breakable<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-19-32">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-19-33">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> title-display <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> title-display <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-19-34">      text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>fill<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> panel-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-19-35">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-19-36">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-19-37"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-19" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="2" data-code-annotation="1">First positional parameter receives the wrapped content from the Lua filter.</span>
</dd>
<dt data-target-cell="annotated-cell-19" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="6" data-code-annotation="2">Empty dictionary <code>(:)</code> as default; the parameter is declared now to keep the function signature stable, so <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> can add colour-aware logic without changing any call sites. The wrapper function in <code>typst-show.typ</code> injects the actual colour scheme, but this simplified Part 1 version of <code>get-panel-colours</code> ignores it.</span>
</dd>
<dt data-target-cell="annotated-cell-19" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="9" data-code-annotation="3">Returns a <a href="https://typst.app/docs/reference/foundations/dictionary/">dictionary</a> with background, border, title, and content colours based purely on the <code>style</code> name; <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> upgrades this to also use the injected <code>colours</code> for theme-aware text.</span>
</dd>
<dt data-target-cell="annotated-cell-19" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="12" data-code-annotation="4">Conditional returns <code>none</code> if title is empty; Typst’s <code>if</code> is an expression.</span>
</dd>
<dt data-target-cell="annotated-cell-19" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="25" data-code-annotation="5"><a href="https://typst.app/docs/reference/layout/block/"><code>block</code></a> creates a block-level container with styling.</span>
</dd>
<dt data-target-cell="annotated-cell-19" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-19" data-code-lines="31" data-code-annotation="6"><code>breakable: false</code> prevents the panel from splitting across page breaks.</span>
</dd>
</dl>
<p>The <code>breakable: false</code> default prevents panels from splitting across pages, avoiding awkward orphaned titles or content fragments.</p>
</section>
<section id="the-panel-wrapper-injecting-the-colour-scheme" class="level3" data-number="7.6">
<h3 data-number="7.6" class="anchored" data-anchor-id="the-panel-wrapper-injecting-the-colour-scheme"><span class="header-section-number">7.6</span> The Panel Wrapper: Injecting the Colour Scheme</h3>
<p>Unlike badges, where the Lua handler passes <code>effective-brand-mode</code> directly to <code>simple-badge</code>, panels need a richer colour dictionary. The <code>render-panel</code> function accepts a <code>colours</code> parameter that the Lua filter cannot populate at parse time. A wrapper function in <code>typst-show.typ</code> bridges this gap:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/typst-show.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-20" data-filename="_extensions/my-extension/typst-show.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-20-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">my-panel</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-20-2">  render-panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-20-3">    content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-20" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-20-4" class="code-annotation-target">    colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> get-colours<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> effective-brand-mode<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-20" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-20-5" class="code-annotation-target">    ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-20-6">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-20-7"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-20" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-20" data-code-lines="4" data-code-annotation="1">Injects the full colour dictionary resolved from <code>effective-brand-mode</code> at render time.</span>
</dd>
<dt data-target-cell="annotated-cell-20" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-20" data-code-lines="5" data-code-annotation="2">Forwards all other named arguments (<code>title</code>, <code>style</code>, etc.) unchanged.</span>
</dd>
</dl>
<p>The Lua handler generates <code>#my-panel(style: "info")[...]</code>, and at render time <code>my-panel</code> resolves the colour scheme before delegating to <code>render-panel</code>.</p>
<div class="highlight">
<p>For badges, passing <code>mode</code> directly to the Typst function is sufficient. For panels, a <strong>wrapper function</strong> injects a full colour dictionary that depends on runtime context. Choose the simpler approach when it works; use a wrapper when the rendering function needs derived values the Lua filter cannot provide.</p>
</div>
</section>
<section id="the-filter-entrypoint" class="level3" data-number="7.7">
<h3 data-number="7.7" class="anchored" data-anchor-id="the-filter-entrypoint"><span class="header-section-number">7.7</span> The Filter Entrypoint</h3>
<p>The conceptual snippets from earlier sections are real code from <code>filter.lua</code>, the Pandoc entrypoint that registers the <code>Span</code> and <code>Div</code> filter functions and loads <code>wrapper.lua</code>.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/my-extension/filter.lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-21" data-filename="_extensions/my-extension/filter.lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-21-1" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">path</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">path</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'%.lua$'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">''</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="annotated-cell-21-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-4"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-21-5" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> require_local<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"wrapper.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-6"></span>
<span id="annotated-cell-21-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Badge handler for inline .badge spans</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-21-8" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> Span<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-9">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-21-10">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span></span>
<span id="annotated-cell-21-11">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-12"></span>
<span id="annotated-cell-21-13">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>includes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'badge'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-21-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span></span>
<span id="annotated-cell-21-15">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-16"></span>
<span id="annotated-cell-21-17">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-18">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">span</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">attributes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">or</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'neutral'</span></span>
<span id="annotated-cell-21-19"></span>
<span id="annotated-cell-21-20">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">string.format</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-21-21">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'#simple-badge(colour: "%s", mode: effective-brand-mode)[%s]'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-21-22">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">colour</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-21-23">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">content</span></span>
<span id="annotated-cell-21-24">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-25"></span>
<span id="annotated-cell-21-26">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">typst_code</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-27"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-28"></span>
<span id="annotated-cell-21-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Panel handler for block .panel divs</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-21-30" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> Div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-31">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'typst'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-21-32">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span></span>
<span id="annotated-cell-21-33">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-34"></span>
<span id="annotated-cell-21-35">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">classes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span>includes<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-21-36">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span></span>
<span id="annotated-cell-21-37">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-21-38"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-21" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-21-39" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'my-panel'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">arguments</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-21-40">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">wrapper</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>build_wrapped_content<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-21-41"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-21" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="1" data-code-annotation="1"><code>require_local</code> wraps <code>quarto.utils.resolve_path</code> so paths resolve relative to the extension root regardless of where Quarto is run from.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="5" data-code-annotation="2"><code>wrapper.lua</code> is required at the top level; Lua caches the module, so every filter function shares the same instance.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="8" data-code-annotation="3"><code>Span</code> is the Pandoc filter hook; Pandoc calls it for every span in the document.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="30" data-code-annotation="4"><code>Div</code> is the Pandoc filter hook for block-level divs; both hooks are defined in the same file.</span>
</dd>
<dt data-target-cell="annotated-cell-21" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-21" data-code-lines="39" data-code-annotation="5">The <code>config</code> table matches the shape used in <a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a>’s configuration system: <code>wrapper</code> is the Typst function name, <code>arguments = true</code> ensures the parameter list is always generated.</span>
</dd>
</dl>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>example.qmd</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" data-filename="example.qmd" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb11-2"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">title:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "My Extension Demo"</span></span>
<span id="cb11-3"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">format:</span></span>
<span id="cb11-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  my-extension-typst: default</span></span>
<span id="cb11-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb11-6"></span>
<span id="cb11-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Badges</span></span>
<span id="cb11-8"></span>
<span id="cb11-9">Here are badges in different colours:</span>
<span id="cb11-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Completed</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="success"},</span>
<span id="cb11-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Pending</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="warning"},</span>
<span id="cb11-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Failed</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="danger"},</span>
<span id="cb11-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Information</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="info"},</span>
<span id="cb11-14">and <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Default</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge}.</span>
<span id="cb11-15"></span>
<span id="cb11-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Panels</span></span>
<span id="cb11-17"></span>
<span id="cb11-18">::: {.panel style="info" title="Important Note"}</span>
<span id="cb11-19">This panel uses the **info** style with an explicit title attribute.</span>
<span id="cb11-20"></span>
<span id="cb11-21"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>First point.</span>
<span id="cb11-22"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>Second point.</span>
<span id="cb11-23">:::</span>
<span id="cb11-24"></span>
<span id="cb11-25">::: {.panel style="success"}</span>
<span id="cb11-26"></span>
<span id="cb11-27"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"># Heading as Title</span></span>
<span id="cb11-28"></span>
<span id="cb11-29">This panel extracts the first heading as its title automatically.</span>
<span id="cb11-30">:::</span>
<span id="cb11-31"></span>
<span id="cb11-32">::: {.panel style="warning" title="Caution"}</span>
<span id="cb11-33">Be careful with this content.</span>
<span id="cb11-34">It uses the **warning** style.</span>
<span id="cb11-35">:::</span>
<span id="cb11-36"></span>
<span id="cb11-37">::: {.panel style="danger" title="Error"}</span>
<span id="cb11-38">Something went wrong.</span>
<span id="cb11-39">This panel uses the **danger** style.</span>
<span id="cb11-40">:::</span>
<span id="cb11-41"></span>
<span id="cb11-42">::: {.panel}</span>
<span id="cb11-43">A subtle panel with default styling and no explicit title.</span>
<span id="cb11-44">:::</span>
<span id="cb11-45"></span>
<span id="cb11-46"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Mixed Content</span></span>
<span id="cb11-47"></span>
<span id="cb11-48">::: {.panel style="info" title="Status Overview"}</span>
<span id="cb11-49">The build is <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Passing</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="success"} and deployment is <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Pending</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="warning"}.</span>
<span id="cb11-50"></span>
<span id="cb11-51">Review the <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Critical</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span>{.badge colour="danger"} items before release.</span>
<span id="cb11-52">:::</span></code></pre></div></div>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/example.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3"><img src="https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/assets/example.png" class="img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="PDF render showing three sections. Badges: five inline pill-shaped badges labelled Completed in green, Pending in orange, Failed in red, Information in blue, and Default in grey. Panels: five rounded bordered panels — a blue info panel titled Important Note with a two-item bullet list, a green panel titled Heading as Title, an orange panel titled Caution, a red panel titled Error, and a plain grey panel with no title. Mixed Content: a blue info panel titled Status Overview containing inline Passing (green), Pending (orange), and Critical (red) badges embedded in sentences."></a></p>
</figure>
</div>
</div>
</div>
</div>
</section>
</section>
<section id="conclusion" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">8</span> Conclusion</h2>
<p>This tutorial introduced the dual-layer architecture for building Quarto Typst templates. You have learned how Lua filters and Typst functions work together to transform markdown elements into styled PDF components.</p>
<p>You can download the complete code for this part of the tutorial as a zip file containing a minimal extension with badges and panels.</p>
<div style="text-align: center;">
<p><a href="./tutorial-part1.zip" class="btn btn-outline-light" target="_blank" rel="noopener noreferrer">Part 1 (ZIP)</a></p>
</div>
<section id="key-concepts-recap" class="level3" data-number="8.1">
<h3 data-number="8.1" class="anchored" data-anchor-id="key-concepts-recap"><span class="header-section-number">8.1</span> Key Concepts Recap</h3>
<p>The dual-layer architecture separates concerns effectively:</p>
<ol type="1">
<li><strong>Lua filters</strong> intercept markdown elements and generate Typst function calls using <code>pandoc.RawBlock</code> and <code>pandoc.RawInline</code>.</li>
<li><strong>Typst functions</strong> receive that generated code and render styled output with access to document context.</li>
<li><strong>Pandoc template variables</strong> (like <code>effective-brand-mode</code>) bridge parse-time (Lua) and render-time (Typst). Lua references the variable by name in the generated code; Typst resolves its value when rendering.</li>
</ol>
<p>For inline elements like badges, Lua extracts content and attributes, then generates a Typst function call. For block elements like panels, Lua wraps the content with opening and closing Typst code, preserving nested structure.</p>
</section>
<section id="what-comes-next" class="level3" data-number="8.2">
<h3 data-number="8.2" class="anchored" data-anchor-id="what-comes-next"><span class="header-section-number">8.2</span> What Comes Next</h3>
<p><a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2</a> builds on these foundations with advanced patterns:</p>
<ul>
<li>Handler factories for reducing repetitive code.</li>
<li>Configuration systems for user-extensible mappings.</li>
<li>Value conversion and type safety between Lua and Typst.</li>
<li>WCAG accessibility compliance for colour contrast.</li>
<li>Shortcodes for components without natural markdown syntax.</li>
<li>A complete quote card example tying all patterns together.</li>
</ul>
</section>
<section id="further-resources" class="level3" data-number="8.3">
<h3 data-number="8.3" class="anchored" data-anchor-id="further-resources"><span class="header-section-number">8.3</span> Further Resources</h3>
<ul>
<li><a href="https://quarto.org/docs/extensions/creating.html">Quarto Documentation: Creating Extensions</a>.</li>
<li><a href="https://pandoc.org/lua-filters.html">Pandoc Lua Filters</a>.</li>
<li><a href="https://typst.app/docs/">Typst Documentation</a>.</li>
<li><a href="https://github.com/mcanouil/quarto-mcanouil"><code>mcanouil</code> Extension</a>.</li>
<li><a href="https://rfortherestofus.com/2025/11/quarto-typst-pdf">R for the Rest of Us: Typst + Quarto</a>.</li>
<li><a href="../../posts/2026-03-05-typst-template-tutorial-part2/">Part 2: Advanced Patterns</a></li>
</ul>


</section>
</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Building {Quarto} {Typst} {Templates:} {The} {Lua-Typst}
    {Bridge} {(Part} 1)},
  date = {2026-02-27},
  url = {https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-02-27). Building Quarto Typst Templates: The Lua-Typst
Bridge (Part 1). <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/">https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>extensions</category>
  <category>typst</category>
  <category>lua</category>
  <category>pdf</category>
  <guid>https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/</guid>
  <pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-02-27-typst-template-tutorial-part1/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Document-Type Dispatching in Quarto Typst Extensions</title>
  <link>https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Feature image for &quot;Quarto + Typst Document Dispatcher&quot; blog post. Dark
background with the official Quarto logo (four blue quadrants with
wordmark) and Typst logo (teal wordmark) centered at the top. Below,
&quot;Document Dispatcher&quot; in serif font with subtitle &quot;Modern Scientific
Publishing&quot;. File format pipeline shows .qmd → .typ → .pdf. Decorative
elements include floating code snippets and gradient orbs in blue and
teal.
" width="600"></a></p>
</figure>
</div>
<section id="the-problem-multiple-document-types" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="the-problem-multiple-document-types"><span class="header-section-number">1</span> The Problem: Multiple Document Types</h2>
<p>When building a Quarto Typst extension, you may want to support multiple document types: reports, letters, CVs. Each type has distinct layouts, headers, and styling requirements.</p>
<p>While Quarto extensions can contribute multiple variants of the same base format<sup>1</sup>, this approach requires either sharing metadata across all contributed elements or duplicating it for each variant.</p>
<p>The dispatcher pattern offers an alternative: a single format entry point with document-type routing handled entirely in Typst code. This centralises shared logic and avoids metadata duplication.</p>
<p>Without a clear architecture, you end up with:</p>
<ul>
<li>Monolithic templates cluttered with conditional logic.</li>
<li>Duplicated code across separate template files.</li>
<li>Difficult maintenance as document types multiply.</li>
</ul>
<p>The solution is a <strong>dispatcher pattern</strong>: a central function that routes to the appropriate template based on document type.</p>
</section>
<section id="quartos-default-typst-partials" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="quartos-default-typst-partials"><span class="header-section-number">2</span> Quarto’s Default Typst Partials</h2>
<p>Before building a dispatcher, it helps to understand how Quarto generates Typst documents.</p>
<p>Quarto provides a set of <a href="https://quarto.org/docs/journals/templates.html#typst-partials">default Typst partials</a> that handle different aspects of document generation:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 25%">
<col style="width: 74%">
</colgroup>
<thead>
<tr class="header">
<th>Partial</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>definitions.typ</code></td>
<td>Pandoc/Quarto features (callouts, block quotes, subfloats).</td>
</tr>
<tr class="even">
<td><code>typst-template.typ</code></td>
<td>The main template function applied to document content.</td>
</tr>
<tr class="odd">
<td><code>typst-show.typ</code></td>
<td>Show rules that capture metadata and call the template.</td>
</tr>
<tr class="even">
<td><code>page.typ</code></td>
<td>Page properties (size, margins, numbering, background).</td>
</tr>
<tr class="odd">
<td><code>notes.typ</code></td>
<td>Footnote rendering.</td>
</tr>
<tr class="even">
<td><code>biblio.typ</code></td>
<td>Bibliography formatting.</td>
</tr>
</tbody>
</table>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>definitions.typ</code> partial is essential and must always be included in custom templates. It provides the foundation for Quarto features like callouts.</p>
</div>
</div>
<section id="overriding-partials" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="overriding-partials"><span class="header-section-number">2.1</span> Overriding Partials</h3>
<p>Extensions override default partials by providing replacement files via <code>template-partials</code> in <code>_extension.yml</code> (or document YAML front matter). When you list a partial, Quarto uses your version instead of the built-in one.</p>
<p>The dispatcher pattern requires customising two partials:</p>
<ul>
<li><strong><code>template.typ</code></strong>: Load document-type templates and the dispatcher function.</li>
<li><strong><code>typst-show.typ</code></strong>: Route document content through the dispatcher based on configuration.</li>
</ul>
<p>See the <a href="https://github.com/quarto-dev/quarto-cli/tree/main/src/resources/formats/typst/pandoc/quarto">Quarto source</a> for the default partial implementations.</p>
</section>
</section>
<section id="the-dispatcher-pattern" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="the-dispatcher-pattern"><span class="header-section-number">3</span> The Dispatcher Pattern</h2>
<p>The dispatcher acts as a routing hub. It receives a document type parameter, validates it, and calls the corresponding template function.</p>
<section id="core-implementation" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="core-implementation"><span class="header-section-number">3.1</span> Core Implementation</h3>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>document-dispatcher.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-1" data-filename="document-dispatcher.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/// Supported document types.</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-2" class="code-annotation-target"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">DOCUMENT_TYPES</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"letter"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cv"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-3"></span>
<span id="annotated-cell-1-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/// Main document dispatcher function.</span></span>
<span id="annotated-cell-1-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/// Routes to the appropriate template based on document-type.</span></span>
<span id="annotated-cell-1-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">document-dispatcher</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-1-7" class="code-annotation-target">  document-type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-1-8" class="code-annotation-target">  ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-1-9"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-10">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Validate and normalise document type</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-1-11" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc-type</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> document-type <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">in</span> DOCUMENT_TYPES <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-12">    document-type</span>
<span id="annotated-cell-1-13">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-14">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Fall back to report with warning</span></span>
<span id="annotated-cell-1-15">    <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span></span>
<span id="annotated-cell-1-16">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-1-17"></span>
<span id="annotated-cell-1-18">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Dispatch to appropriate template</span></span>
<span id="annotated-cell-1-19">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> doc-type <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"letter"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-20">    render-letter<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-21">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> doc-type <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cv"</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-22">    render-cv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-23">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-24">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Default: report</span></span>
<span id="annotated-cell-1-25">    render-report<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-1-26">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-1-27"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="2" data-code-annotation="1">Define supported types as an <a href="https://typst.app/docs/reference/foundations/array/">array</a>.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="7" data-code-annotation="2">Default document type; used when not specified.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="8" data-code-annotation="3"><a href="https://typst.app/docs/reference/foundations/arguments/">Variadic arguments</a> capture all other parameters.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="11" data-code-annotation="4"><code>in</code> operator checks array membership.</span>
</dd>
</dl>
</section>
<section id="key-patterns" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="key-patterns"><span class="header-section-number">3.2</span> Key Patterns</h3>
<p><strong>Pattern 1: Type Validation</strong></p>
<p>The dispatcher validates the document type against a known list before routing. Invalid types fall back gracefully to a default (report) rather than failing.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb3-1">let doc-type = if document-type in DOCUMENT_TYPES {</span>
<span id="cb3-2">  document-type</span>
<span id="cb3-3">} else {</span>
<span id="cb3-4">  "report"  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Safe fallback</span></span>
<span id="cb3-5">}</span></code></pre></div></div>
<p><strong>Pattern 2: Variadic Argument Forwarding</strong></p>
<p>Each document type may accept different parameters. Rather than listing every possible parameter, the dispatcher captures them all with <code>..args</code> and forwards them to the target function.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb4-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">document-dispatcher</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>document-type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> ..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb4-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// ...</span></span>
<span id="cb4-3">  render-report<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>..args<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span>  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Forward all arguments</span></span>
<span id="cb4-4"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>This means:</p>
<ul>
<li>The dispatcher does not need to know about each template’s specific parameters.</li>
<li>Adding new parameters to a template requires no changes to the dispatcher.</li>
<li>Each template function handles only its own parameters.</li>
</ul>
<p><strong>Pattern 3: Conditional Routing</strong></p>
<p>Typst’s <code>if-else</code> statements handle the routing logic. For more document types, this could be extended with a dictionary lookup:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">TEMPLATE_FUNCTIONS</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb5-2">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span>: render-report<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-3">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"letter"</span>: render-letter<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-4">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cv"</span>: render-cv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb5-5"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-6"></span>
<span id="cb5-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Then dispatch with:</span></span>
<span id="cb5-8">let template-fn = TEMPLATE_FUNCTIONS.at(doc-type, default: render-report)</span>
<span id="cb5-9">template-fn(..args)</span></code></pre></div></div>
</section>
</section>
<section id="configuration-via-yaml" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="configuration-via-yaml"><span class="header-section-number">4</span> Configuration via YAML</h2>
<p>Users specify the document type in their Quarto document’s YAML front matter.</p>
<section id="user-facing-configuration" class="level3" data-number="4.1">
<h3 data-number="4.1" class="anchored" data-anchor-id="user-facing-configuration"><span class="header-section-number">4.1</span> User-Facing Configuration</h3>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>document.qmd</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="document.qmd" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb6-1"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb6-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Report"</span></span>
<span id="cb6-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">my-extension-typst</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb6-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">document-type</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> report</span></span>
<span id="cb6-6"><span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">---</span></span></code></pre></div></div>
</div>
<p>The extension’s format name (<code>my-extension-typst</code>) comes from <code>_extension.yml</code>. The <code>document-type</code> key is passed through Quarto’s template processing.</p>
</section>
<section id="bridging-quarto-and-typst" class="level3" data-number="4.2">
<h3 data-number="4.2" class="anchored" data-anchor-id="bridging-quarto-and-typst"><span class="header-section-number">4.2</span> Bridging Quarto and Typst</h3>
<p>The <code>typst-show.typ</code> partial connects YAML metadata to the Typst dispatcher:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>typst-show.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename="typst-show.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-annotation-code code-with-copy code-annotated"><code class="sourceCode typst"><span id="annotated-cell-6-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#show</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> document-dispatcher<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>with<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="annotated-cell-6-2">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Document type selection</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-3" class="code-annotation-target">  document-type<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$if(document-type)$</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"$document-type$"</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$else$</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$endif$</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-6-4"></span>
<span id="annotated-cell-6-5">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Pass through other parameters</span></span>
<span id="annotated-cell-6-6">  title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$title$</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span></span>
<span id="annotated-cell-6-7">  author<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$for(author)$$author$$sep$</span>, <span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$endfor$</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span></span>
<span id="annotated-cell-6-8">  date<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$date$</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span></span>
<span id="annotated-cell-6-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// ... additional parameters</span></span>
<span id="annotated-cell-6-10"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="3" data-code-annotation="1">Pandoc template syntax (<code>$if()$</code>) checks if the metadata key exists and provides a default.</span>
</dd>
</dl>
<p>The <code>$if(document-type)$...$else$...$endif$</code> pattern:</p>
<ul>
<li>Uses the user-provided value if present.</li>
<li>Falls back to “report” if not specified.</li>
<li>Ensures the dispatcher always receives a valid string.</li>
</ul>
</section>
</section>
<section id="file-organisation" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="file-organisation"><span class="header-section-number">5</span> File Organisation</h2>
<p>A well-organised extension separates document types into distinct files.</p>
<section id="directory-structure" class="level3" data-number="5.1">
<h3 data-number="5.1" class="anchored" data-anchor-id="directory-structure"><span class="header-section-number">5.1</span> Directory Structure</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="txt" style="background: #f1f3f5;"><pre class="sourceCode txt cw-auto code-with-copy"><code class="sourceCode default"><span id="cb7-1">_extensions/my-extension/</span>
<span id="cb7-2">├── _extension.yml</span>
<span id="cb7-3">├── template.typ              # Main entry point</span>
<span id="cb7-4">├── typst-show.typ            # Quarto-Typst bridge</span>
<span id="cb7-5">└── partials/</span>
<span id="cb7-6">    ├── document-dispatcher.typ</span>
<span id="cb7-7">    └── document-types/</span>
<span id="cb7-8">        ├── report.typ</span>
<span id="cb7-9">        ├── letter.typ</span>
<span id="cb7-10">        └── cv.typ</span></code></pre></div></div>
</section>
<section id="extension-manifest" class="level3" data-number="5.2">
<h3 data-number="5.2" class="anchored" data-anchor-id="extension-manifest"><span class="header-section-number">5.2</span> Extension Manifest</h3>
<p>The <code>_extension.yml</code> declares template partials in loading order:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> My Extension</span></span>
<span id="cb8-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.0.0</span></span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contributes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formats</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">my-extension-typst</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> template.typ</span></span>
<span id="cb8-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template-partials</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb8-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> partials/document-types/report.typ</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    # Load document types first</span></span>
<span id="cb8-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> partials/document-types/letter.typ</span></span>
<span id="cb8-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> partials/document-types/cv.typ</span></span>
<span id="cb8-11"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> partials/document-dispatcher.typ</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      # Dispatcher last (uses the above)</span></span></code></pre></div></div>
</div>
</section>
<section id="main-template" class="level3" data-number="5.3">
<h3 data-number="5.3" class="anchored" data-anchor-id="main-template"><span class="header-section-number">5.3</span> Main Template</h3>
<p>The <code>template.typ</code> file serves as the entry point that assembles all components. It must include Quarto’s essential definitions and load your custom partials in the correct order.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>template.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-filename="template.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-with-copy"><code class="sourceCode typst"><span id="cb9-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Essential: Quarto/Pandoc feature definitions (callouts, quotes, etc.)</span></span>
<span id="cb9-2"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$definitions.typ()$</span></span>
<span id="cb9-3"></span>
<span id="cb9-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Load document type templates</span></span>
<span id="cb9-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$report.typ()$</span></span>
<span id="cb9-6"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$letter.typ()$</span></span>
<span id="cb9-7"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$cv.typ()$</span></span>
<span id="cb9-8"></span>
<span id="cb9-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Load dispatcher (must come after document types)</span></span>
<span id="cb9-10"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$document-dispatcher.typ()$</span></span></code></pre></div></div>
</div>
<p>The loading order matters:</p>
<ol type="1">
<li><strong><code>definitions.typ</code></strong> must come first; it provides Quarto features like callouts.</li>
<li><strong>Document type templates</strong> define the rendering functions (<code>render-report</code>, etc.).</li>
<li><strong>Dispatcher</strong> comes last because it references the document type functions.</li>
</ol>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>$partial-name.typ()$</code> syntax is Quarto’s template interpolation. It inserts the contents of the corresponding partial file at that location.</p>
</div>
</div>
</section>
</section>
<section id="adding-new-document-types" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="adding-new-document-types"><span class="header-section-number">6</span> Adding New Document Types</h2>
<p>To add a new document type (e.g., “memo”):</p>
<p><strong>Step 1: Create the template function.</strong></p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>partials/document-types/memo.typ</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" data-filename="partials/document-types/memo.typ" style="background: #f1f3f5;"><pre class="sourceCode typst code-with-copy"><code class="sourceCode typst"><span id="cb10-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">render-memo</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb10-2">  title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-3">  to<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-4">  from<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-5">  date<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-6">  body<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb10-7"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-8">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Memo-specific layout</span></span>
<span id="cb10-9">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">set</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">page</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>paper<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"a4"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> margin<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">2cm</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-10"></span>
<span id="cb10-11">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Header</span></span>
<span id="cb10-12">  grid<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span></span>
<span id="cb10-13">    columns<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1fr</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb10-14">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>*TO:* <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>to<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>*DATE:* <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>date<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span></span>
<span id="cb10-15">    <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span>*FROM:* <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#</span>from<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">],</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[],</span></span>
<span id="cb10-16">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-17"></span>
<span id="cb10-18">  line<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>length<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">100%</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-19"></span>
<span id="cb10-20">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Subject</span></span>
<span id="cb10-21">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> title <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">none</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb10-22">    align<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>center<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> text<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span>weight<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bold"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> size<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">14pt</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> title<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb10-23">  <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb10-24"></span>
<span id="cb10-25">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Body</span></span>
<span id="cb10-26">  body</span>
<span id="cb10-27"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
</div>
<p><strong>Step 2: Register in the dispatcher.</strong></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb11-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">#let</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">DOCUMENT_TYPES</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"report"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"letter"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cv"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"memo"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb11-2"></span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Add routing case</span></span>
<span id="cb11-4">if doc-type == "memo" {</span>
<span id="cb11-5">  render-memo(..args)</span>
<span id="cb11-6">}</span></code></pre></div></div>
<p><strong>Step 3: Add to extension manifest.</strong></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml"><span id="cb12-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">template-partials</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb12-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> partials/document-types/memo.typ</span></span>
<span id="cb12-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  # ... other partials</span></span></code></pre></div></div>
<p><strong>Step 4: Load in template.</strong></p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" data-filename="typst" style="background: #f1f3f5;"><pre class="sourceCode typst cw-auto code-with-copy"><code class="sourceCode typst"><span id="cb13-1"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">$memo.typ()$</span></span></code></pre></div></div>
</section>
<section id="benefits-of-this-pattern" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="benefits-of-this-pattern"><span class="header-section-number">7</span> Benefits of This Pattern</h2>
<p><strong>Clean Separation</strong></p>
<p>Each document type lives in its own file with its own logic. Changes to one type do not affect others.</p>
<p><strong>Extensibility</strong></p>
<p>Adding new document types requires minimal changes to existing code. The dispatcher’s validation list and routing logic are the only modifications needed.</p>
<p><strong>Graceful Fallbacks</strong></p>
<p>Invalid document types fall back to a sensible default rather than failing. Users receive working output even with typos in their configuration.</p>
<p><strong>Parameter Isolation</strong></p>
<p>Each template function defines only its own parameters. The dispatcher does not need to understand every possible parameter combination.</p>
</section>
<section id="conclusion" class="level2" data-number="8">
<h2 data-number="8" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">8</span> Conclusion</h2>
<p>The document-type dispatcher pattern provides a clean architecture for Quarto Typst extensions that support multiple document types.</p>
<p>Key takeaways:</p>
<ol type="1">
<li><strong>Central routing</strong>: A single dispatcher function routes to appropriate templates.</li>
<li><strong>Validation with fallbacks</strong>: Check document types against a known list; default gracefully.</li>
<li><strong>Variadic forwarding</strong>: Use <code>..args</code> to pass parameters without coupling.</li>
<li><strong>YAML integration</strong>: Bridge Quarto metadata to Typst via <code>typst-show.typ</code>.</li>
<li><strong>Modular organisation</strong>: Separate files for each document type.</li>
</ol>
<div class="highlight">
<p>The dispatcher pattern scales well as your extension grows. Start with one or two document types, and the architecture supports adding more without refactoring.</p>
</div>
<section id="further-resources" class="level3" data-number="8.1">
<h3 data-number="8.1" class="anchored" data-anchor-id="further-resources"><span class="header-section-number">8.1</span> Further Resources</h3>
<ul>
<li><a href="https://typst.app/docs/reference/foundations/function/">Typst Documentation: Functions</a>.</li>
<li><a href="https://quarto.org/docs/journals/templates.html#typst-partials">Quarto Documentation: Typst Partials</a>.</li>
<li><a href="https://github.com/mcanouil/quarto-mcanouil">quarto-mcanouil Extension</a> - demonstrates this pattern in production.</li>
</ul>


</section>
</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Format variants use the <code>+</code> syntax in <code>_extension.yml</code>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contributes</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">formats</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst+report</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Typst Report"</span></span>
<span id="cb1-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">typst+article</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Typst Article"</span></span></code></pre></div></div>
</div>
<p>Then in a document:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>YAML</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="YAML" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb2-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">myformat-typst+report</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> default</span></span></code></pre></div></div>
</div>
↩︎</li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Document-Type {Dispatching} in {Quarto} {Typst} {Extensions}},
  date = {2026-01-19},
  url = {https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-01-19). Document-Type Dispatching in Quarto Typst
Extensions. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/">https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>extensions</category>
  <category>typst</category>
  <category>pdf</category>
  <guid>https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/</guid>
  <pubDate>Mon, 19 Jan 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-01-19-typst-document-dispatcher/featured.png" medium="image" type="image/png" height="76" width="144"/>
</item>
<item>
  <title>Quarto Wizard 2.0.0: Native Extension Management and Batch Operations</title>
  <link>https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Cartoon dog wizard wearing blue hat with red band holding magic wand creating HTML and CSS code scrolls in starry night scene." width="600"></a></p>
</figure>
</div>
<p><strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard 2.0.0</strong> is here with a major architectural overhaul and powerful new features for managing your Quarto extensions.</p>
<div class="highlight">
<p><strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard 2.0.0</strong> is now available!</p>
<p>Upgrade today from the <a href="https://marketplace.visualstudio.com/items?itemName=mcanouil.quarto-wizard">VS Code marketplace</a> or <a href="https://open-vsx.org/extension/mcanouil/quarto-wizard">Open VSX Registry</a>.</p>
</div>
<section id="from-1.0.0-to-2.0.0" class="level2">
<h2 class="anchored" data-anchor-id="from-1.0.0-to-2.0.0">From 1.0.0 to 2.0.0</h2>
<p>Version 1.0.0 was not around for long. As I worked on improving Quarto Wizard and enabling features beyond what the Quarto CLI offers, what started as small enhancements quickly grew into a major internal refactoring. The result is a more powerful and self-contained extension.</p>
<div class="highlight">
<p>No more Quarto CLI dependency.</p>
</div>
<p>The biggest change in 2.0.0: <strong>Quarto Wizard no longer requires the Quarto CLI</strong> for extension management. The extension now handles everything natively, including:</p>
<ul>
<li>Downloading and extracting extension archives directly.</li>
<li>GitHub API integration for repository access.</li>
<li>Proxy support via environment variables (<code>HTTP_PROXY</code>, <code>HTTPS_PROXY</code>, <code>NO_PROXY</code>).</li>
</ul>
<p>This means faster installations and fewer external dependencies.</p>
</section>
<section id="whats-new" class="level2">
<h2 class="anchored" data-anchor-id="whats-new">What’s New</h2>
<section id="extension-type-filtering" class="level3">
<h3 class="anchored" data-anchor-id="extension-type-filtering">Extension Type Filtering</h3>
<p>Quickly filter extensions by type when browsing the registry.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/extension-browser-1-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/extension-browser-1-dark.png" title="Extension Type Filtering" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The extension browser interface showing extension type filters with
options for Shortcodes, Filters, Formats, Reveal.js Plugins, and
Projects.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/extension-browser-1-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/extension-browser-1-light.png" title="Extension Type Filtering" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The extension browser interface showing extension type filters with
options for Shortcodes, Filters, Formats, Reveal.js Plugins, and
Projects.
" width="500"></a></p>
</figure>
</div>
</section>
<section id="multi-extension-selection" class="level3">
<h3 class="anchored" data-anchor-id="multi-extension-selection">Multi-Extension Selection</h3>
<p>When installing from repositories containing multiple extensions, select exactly which ones you need.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/extension-selection-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/extension-selection-dark.png" title="Extension Selection Dialog" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The QuickPick dialog showing a list of discovered extensions with
checkboxes for multi-selection when installing from a repository
containing multiple extensions.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/extension-selection-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/extension-selection-light.png" title="Extension Selection Dialog" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The QuickPick dialog showing a list of discovered extensions with
checkboxes for multi-selection when installing from a repository
containing multiple extensions.
" width="500"></a></p>
</figure>
</div>
</section>
<section id="template-file-selection" class="level3">
<h3 class="anchored" data-anchor-id="template-file-selection">Template File Selection</h3>
<p>Choose which files to copy when using templates, with support for target subdirectories.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/template-files-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/template-files-dark.png" title="Template Files Selection" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The QuickPick dialog showing available template files to copy with
checkboxes for selective file installation.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/template-files-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/template-files-light.png" title="Template Files Selection" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The QuickPick dialog showing available template files to copy with
checkboxes for selective file installation.
" width="500"></a></p>
</figure>
</div>
</section>
<section id="private-repositories" class="level3">
<h3 class="anchored" data-anchor-id="private-repositories">Private Repositories</h3>
<p>Access private GitHub repositories with improved authentication support.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/github-token-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/github-token-dark.png" title="GitHub Token Setup" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The input dialog for entering a GitHub personal access token for private
repository access.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/github-token-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/github-token-light.png" title="GitHub Token Setup" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The input dialog for entering a GitHub personal access token for private
repository access.
" width="500"></a></p>
</figure>
</div>
<p>Authentication priority: Manual token &gt; VS Code GitHub session &gt; Environment variables.</p>
</section>
<section id="quarto-version-validation" class="level3">
<h3 class="anchored" data-anchor-id="quarto-version-validation">Quarto Version Validation</h3>
<p>Get warned before installing extensions that require a newer Quarto version.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/version-validation-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/version-validation-dark.png" title="Version Validation Warning" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The warning dialog indicating that the extension version requirement is
not met, showing the required and current Quarto versions with options
to Install Anyway or Cancel.
" width="250"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/version-validation-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/assets/media/version-validation-light.png" title="Version Validation Warning" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="The warning dialog indicating that the extension version requirement is
not met, showing the required and current Quarto versions with options
to Install Anyway or Cancel.
" width="250"></a></p>
</figure>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Requires to have Quarto VSCode/Positron extension and Quarto CLI installed for version detection.</p>
</div>
</div>
</section>
<section id="install-from-specific-references" class="level3">
<h3 class="anchored" data-anchor-id="install-from-specific-references">Install from Specific References</h3>
<p>Install extensions from specific commits, tags, or branches using the format <code>owner/repo@reference</code>.</p>
</section>
</section>
<section id="new-commands" class="level2">
<h2 class="anchored" data-anchor-id="new-commands">New Commands</h2>
<ul>
<li><strong>Install Extension from Registry</strong> - Direct registry installation.</li>
<li><strong>Install Extension from URL</strong> - Install from archive URLs.</li>
<li><strong>Install Extension from Local</strong> - Install from local directories or archives.</li>
<li><strong>Update All Extensions</strong> - Batch update all outdated extensions.</li>
<li><strong>Remove Multiple Extensions</strong> - Batch removal of extensions.</li>
<li><strong>Clear Extension Cache</strong> - Force registry refresh.</li>
<li><strong>Set GitHub Token (Manual)</strong> - Configure authentication.</li>
<li><strong>Clear GitHub Token</strong> - Remove stored token.</li>
</ul>
</section>
<section id="new-configuration-options" class="level2">
<h2 class="anchored" data-anchor-id="new-configuration-options">New Configuration Options</h2>
<ul>
<li><code>quartoWizard.cache.ttlMinutes</code> - Configure cache duration.</li>
<li><code>quartoWizard.registry.url</code> - Use a custom registry URL.</li>
</ul>
</section>
<section id="documentation-website" class="level2">
<h2 class="anchored" data-anchor-id="documentation-website">Documentation Website</h2>
<p>A new comprehensive documentation website is now available at <a href="https://m.canouil.dev/quarto-wizard">m.canouil.dev/quarto-wizard</a>, featuring:</p>
<ul>
<li>User guide with step-by-step instructions.</li>
<li>Command and configuration reference.</li>
<li>API documentation for developers.</li>
</ul>
</section>
<section id="breaking-changes" class="level2">
<h2 class="anchored" data-anchor-id="breaking-changes">Breaking Changes</h2>
<ul>
<li>Removed <code>quartoWizard.quarto.path</code> setting (no longer needed).</li>
<li>Quarto CLI is no longer required for extension operations.</li>
</ul>
<div class="highlight">
<p>Ready to upgrade? Install <strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard 2.0.0</strong> from the <a href="https://marketplace.visualstudio.com/items?itemName=mcanouil.quarto-wizard">VS Code Marketplace</a> or <a href="https://open-vsx.org/extension/mcanouil/quarto-wizard">Open VSX Registry</a>.</p>
</div>
</section>
<section id="whats-next" class="level2">
<h2 class="anchored" data-anchor-id="whats-next">What’s Next</h2>
<p>This major refactoring introduced <code>@quarto-wizard/core</code><sup>1</sup>, a platform-agnostic package for extension management.</p>
<p>Next on the roadmap: a new version of the <a href="https://github.com/mcanouil/quarto-extensions-updater">Quarto Extensions Updater GitHub Action</a> that will leverage this core package, bringing the same native installation capabilities to your CI/CD workflows.</p>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Not published on any registry.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2026,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Wizard} 2.0.0: {Native} {Extension} {Management} and
    {Batch} {Operations}},
  date = {2026-01-12},
  url = {https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2026" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2026-01-12). Quarto Wizard 2.0.0: Native Extension
Management and Batch Operations. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/">https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>vscode</category>
  <category>positron</category>
  <category>extensions</category>
  <category>productivity</category>
  <category>quarto-wizard</category>
  <guid>https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/</guid>
  <pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2026-01-12-quarto-wizard-2-0-0/featured.png" medium="image" type="image/png" height="72" width="144"/>
</item>
<item>
  <title>Quarto Extensions Updater: Automating Extension Maintenance with GitHub Actions</title>
  <link>https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard puppy wearing pointed hat and holding magic wand within
glowing orange circle decorated with mystical symbols next to Quarto Cli
logo and GitHub Actions text.
" width="600"></a></p>
</figure>
</div>
<p>Managing dependencies is a cornerstone of modern software development, and Dependabot has long been the gold standard for automating dependency updates in GitHub repositories. But what about Quarto extensions?</p>
<p>I’m thrilled to introduce <strong><a href="https://github.com/mcanouil/quarto-extensions-updater">Quarto Extensions Updater</a> 1.0.0</strong>, a GitHub Action that brings Dependabot-style automation to Quarto extension management. If you maintain Quarto projects with multiple extensions, this tool will transform your maintenance workflow from tedious manual checks into an effortless, automated process.</p>
<div class="highlight">
<p><strong>Quarto Extensions Updater 1.0.0</strong> brings <strong>Dependabot-style automation</strong> to Quarto extension management. <strong>Set it and forget it</strong>.</p>
</div>
<section id="key-features" class="level2">
<h2 class="anchored" data-anchor-id="key-features">Key Features</h2>
<p><strong>Quarto Extensions Updater</strong> automates the entire extension update workflow:</p>
<ul>
<li><strong>🔍 Automatic Detection</strong>: Scans your <code>_extensions/</code> directory to discover all installed extensions.</li>
<li><strong>📦 Smart Updates</strong>: Uses the Quarto CLI (<code>quarto add</code>) to update extensions with proper dependency resolution.</li>
<li><strong>🔄 Source Tracking</strong>: Maintains the <code>source</code> field in extension manifests for reliable version management<sup>1</sup>.</li>
<li><strong>📝 Detailed Pull Requests</strong>: Creates PRs with comprehensive release notes, categorised by update type (major, minor, patch).</li>
<li><strong>🔀 One PR Per Extension</strong>: Each extension gets its own pull request that updates when new versions are available.</li>
<li><strong>🏷️ Semantic Versioning</strong>: Automatically categorises updates and adds appropriate labels.</li>
<li><strong>⚡ Flexible Scheduling</strong>: Run daily, weekly, monthly, or manually via workflow dispatch.</li>
<li><strong>⚙️ Highly Customisable</strong>: Configure branch names, commit messages, PR titles, and labels to match your workflow.</li>
</ul>
<div class="highlight">
<p>Each extension gets its <strong>own dedicated pull request</strong> that <strong>automatically updates</strong> when new versions are available. No more blocked PRs!</p>
</div>
</section>
<section id="getting-started" class="level2">
<h2 class="anchored" data-anchor-id="getting-started">Getting Started</h2>
<p>Create a workflow file in your repository:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/workflows/update-extensions.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-1" data-filename=".github/workflows/update-extensions.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Update Quarto Extensions</span></span>
<span id="annotated-cell-1-2"></span>
<span id="annotated-cell-1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">schedule</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-5" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cron</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0 0 * * *"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-1-6" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-7"></span>
<span id="annotated-cell-1-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-1-9" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-1-10" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pull-requests</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="annotated-cell-1-11"></span>
<span id="annotated-cell-1-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">update</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="annotated-cell-1-15"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="annotated-cell-1-17"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-1-18" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Setup Quarto</span></span>
<span id="annotated-cell-1-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="annotated-cell-1-20"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-1-21" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mcanouil/quarto-extensions-updater@v1</span></span>
<span id="annotated-cell-1-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-1-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">github-token</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ secrets.GITHUB_TOKEN }}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="5" data-code-annotation="1">Runs daily at midnight UTC. Adjust the cron schedule to suit your needs (weekly: <code>0 0 * * 0</code>, monthly: <code>0 0 1 * *</code>).</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="6" data-code-annotation="2">Allows manual triggering from the Actions tab.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="9" data-code-annotation="3">Required to create branches and commit changes.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="10" data-code-annotation="4">Required to create and update pull requests.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="18" data-code-annotation="5">Installs Quarto CLI, which is required for the <code>quarto add</code> command.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="21" data-code-annotation="6">Runs the updater with default settings.</span>
</dd>
</dl>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Tip</span>First Time Setup
</div>
</div>
<div class="callout-body-container callout-body">
<p>The action works best with extensions that have proper <code>source</code> metadata in their <code>_extension.yml</code> files. If you installed extensions using <a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a>, this metadata is already present. Otherwise, you can manually add it:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extensions/owner/name/_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="_extensions/owner/name/_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Extension Name"</span></span>
<span id="annotated-cell-3-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.0.0"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-3" class="code-annotation-target"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">source</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"owner/repository@v1.0.0"</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="3" data-code-annotation="1">Add or update this line to enable tracking.</span>
</dd>
</dl>
</div>
</div>
</section>
<section id="configuration-options" class="level2">
<h2 class="anchored" data-anchor-id="configuration-options">Configuration Options</h2>
<p>The action supports extensive customisation through input parameters:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 11%">
<col style="width: 28%">
<col style="width: 4%">
<col style="width: 55%">
</colgroup>
<thead>
<tr class="header">
<th>Input</th>
<th>Description</th>
<th>Required</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>github-token</code></td>
<td>GitHub token for creating pull requests</td>
<td>Yes</td>
<td><code>${{ github.token }}</code></td>
</tr>
<tr class="even">
<td><code>workspace-path</code></td>
<td>Path to the workspace containing <code>_extensions</code> directory</td>
<td>No</td>
<td><code>.</code></td>
</tr>
<tr class="odd">
<td><code>registry-url</code></td>
<td>URL to the Quarto extensions registry JSON file</td>
<td>No</td>
<td><code>https://raw.githubusercontent.com/mcanouil/quarto-extensions/refs/heads/quarto-wizard/quarto-extensions.json</code></td>
</tr>
<tr class="even">
<td><code>create-pr</code></td>
<td>Whether to create pull requests</td>
<td>No</td>
<td><code>true</code></td>
</tr>
<tr class="odd">
<td><code>branch-prefix</code></td>
<td>Prefix for update branches</td>
<td>No</td>
<td><code>chore/quarto-extensions</code></td>
</tr>
<tr class="even">
<td><code>base-branch</code></td>
<td>Target branch for pull requests</td>
<td>No</td>
<td><code>main</code></td>
</tr>
<tr class="odd">
<td><code>pr-title-prefix</code></td>
<td>Prefix for PR titles</td>
<td>No</td>
<td><code>chore(deps):</code></td>
</tr>
<tr class="even">
<td><code>commit-message-prefix</code></td>
<td>Prefix for commit messages</td>
<td>No</td>
<td><code>chore(deps):</code></td>
</tr>
<tr class="odd">
<td><code>pr-labels</code></td>
<td>Comma-separated labels to add to PRs</td>
<td>No</td>
<td><code>dependencies,quarto-extensions</code></td>
</tr>
</tbody>
</table>
<p>The action also provides output values:</p>
<table class="caption-top table">
<colgroup>
<col style="width: 24%">
<col style="width: 75%">
</colgroup>
<thead>
<tr class="header">
<th>Output</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>updates-available</code></td>
<td>Whether updates are available (<code>true</code>/<code>false</code>)</td>
</tr>
<tr class="even">
<td><code>update-count</code></td>
<td>Number of updates available</td>
</tr>
<tr class="odd">
<td><code>updates</code></td>
<td>JSON array of updates (name, currentVersion, latestVersion)</td>
</tr>
<tr class="even">
<td><code>pr-number</code></td>
<td>Pull request number (if created)</td>
</tr>
<tr class="odd">
<td><code>pr-url</code></td>
<td>Pull request URL (if created)</td>
</tr>
</tbody>
</table>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Conventional Commits Integration
</div>
</div>
<div class="callout-body-container callout-body">
<p>Using conventional commits? Customise the prefixes to match your workflow:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> mcanouil/quarto-extensions-updater@v1</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">github-token</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ secrets.GITHUB_TOKEN }}</span></span>
<span id="cb1-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branch-prefix</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"deps/quarto"</span></span>
<span id="cb1-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr-title-prefix</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"build(deps):"</span></span>
<span id="cb1-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">commit-message-prefix</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"build(deps):"</span></span>
<span id="cb1-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pr-labels</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dependencies,quarto,automated"</span></span></code></pre></div></div>
<p>This creates PRs with titles like <code>build(deps): update owner/name extension to 1.2.3</code>.</p>
</div>
</div>
</section>
<section id="how-it-works" class="level2">
<h2 class="anchored" data-anchor-id="how-it-works">How It Works</h2>
<p>The action follows a systematic workflow:</p>
<ol type="1">
<li><strong>Fetch Registry</strong>: Downloads the <a href="https://m.canouil.dev/quarto-extensions/">Quarto Extensions registry</a> from GitHub.</li>
<li><strong>Scan Extensions</strong>: Discovers all installed extensions in your <code>_extensions/</code> directory.</li>
<li><strong>Check Versions</strong>: Compares installed versions with the registry using semantic versioning.</li>
<li><strong>Process Each Extension</strong>: Processes extensions individually to create separate pull requests.</li>
<li><strong>Apply Updates</strong>: Uses the Quarto CLI (<code>quarto add owner/repo@version --no-prompt</code>) to update extensions.</li>
<li><strong>Track Updates</strong>: Maintains or adds the <code>source</code> field in extension manifests.</li>
<li><strong>Create/Update PR</strong>:
<ul>
<li>Creates a new pull request if none exists for the extension.</li>
<li>Updates the existing PR if one already exists (same branch name).</li>
<li>Ensures at most one PR per extension.</li>
</ul></li>
</ol>
</section>
<section id="understanding-pull-requests" class="level2">
<h2 class="anchored" data-anchor-id="understanding-pull-requests">Understanding Pull Requests</h2>
<p>The action creates detailed, informative pull requests that follow Dependabot’s style:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb2-1">Updates the following Quarto extension(s):</span>
<span id="cb2-2"></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## ✨ Minor Updates</span></span>
<span id="cb2-4"></span>
<span id="cb2-5"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span>**[mcanouil/quarto-iconify](https://github.com/mcanouil/quarto-iconify)**: <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`1.0.0`</span> → <span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">`3.0.1`</span></span>
<span id="cb2-6"></span>
<span id="cb2-7">---</span>
<span id="cb2-8"></span>
<span id="cb2-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Release Notes</span></span>
<span id="cb2-10"></span>
<span id="cb2-11">**mcanouil/quarto-iconify** (1.0.0 → 3.0.1)</span>
<span id="cb2-12"></span>
<span id="cb2-13"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Release notes</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](https://github.com/mcanouil/quarto-iconify/releases/tag/v3.0.1)</span></span>
<span id="cb2-14"><span class="ss" style="color: #20794D;
background-color: null;
font-style: inherit;">- </span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">Repository</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](https://github.com/mcanouil/quarto-iconify)</span></span>
<span id="cb2-15"></span>
<span id="cb2-16"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">&gt; A shortcode extension to use Iconify icons in HTML-based Quarto documents.</span></span>
<span id="cb2-17"></span>
<span id="cb2-18">---</span>
<span id="cb2-19"></span>
<span id="cb2-20">🤖 This PR was automatically generated by <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">quarto-extensions-updater</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](https://github.com/mcanouil/quarto-extensions-updater)</span></span></code></pre></div></div>
<p>Each PR includes:</p>
<ul>
<li><strong>Clear Title</strong>: Indicates the extension and version change.</li>
<li><strong>Categorised Updates</strong>: Groups by major, minor, or patch changes.</li>
<li><strong>Release Links</strong>: Direct links to release notes and repositories.</li>
<li><strong>Extension Descriptions</strong>: Context about what the extension does.</li>
<li><strong>Appropriate Labels</strong>: Automatically added based on update type.</li>
</ul>
<div class="highlight">
<p><strong>Automated scanning</strong>, <strong>semantic versioning comparison</strong>, and <strong>automatic pull requests</strong> mean you’ll <strong>never miss an extension update</strong> again.</p>
</div>
</section>
<section id="requirements" class="level2">
<h2 class="anchored" data-anchor-id="requirements">Requirements</h2>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Important</span>Essential Requirements
</div>
</div>
<div class="callout-body-container callout-body">
<p>Before using the action, ensure:</p>
<ol type="1">
<li><strong>Quarto CLI</strong>: Must be installed in the workflow environment. Use <code>quarto-dev/quarto-actions/setup@v2</code> to install Quarto in GitHub Actions.</li>
<li><strong>Extension Structure</strong>: Extensions should be in <code>_extensions/owner/name/</code> structure with <code>_extension.yml</code> or <code>_extension.yaml</code> files.</li>
<li><strong>Semantic Versioning</strong>: Extension versions should follow semantic versioning (X.Y.Z).</li>
<li><strong>Registry Listing</strong>: Extensions should be listed in the <a href="https://m.canouil.dev/quarto-extensions/">Quarto Extensions registry</a>.</li>
<li><strong>Permissions</strong>: Workflow needs <code>contents: write</code> and <code>pull-requests: write</code> permissions.</li>
<li><strong>Source Metadata</strong>: Extensions should have <code>source</code> fields in their manifests for reliable version tracking.</li>
</ol>
</div>
</div>
</section>
<section id="troubleshooting" class="level2">
<h2 class="anchored" data-anchor-id="troubleshooting">Troubleshooting</h2>
<p>Common issues and solutions:</p>
<ul>
<li><strong>No Updates Detected</strong>: Verify extensions are in <a href="https://m.canouil.dev/quarto-extensions/">m.canouil.dev/quarto-extensions/</a> and have version and source fields in their manifests.</li>
<li><strong>Permission Errors</strong>: Check that workflow has <code>contents: write</code> and <code>pull-requests: write</code> permissions.</li>
<li><strong>Workflow Not Running</strong>: Verify cron syntax and ensure GitHub Actions are enabled. GitHub disables scheduled workflows in inactive repositories after 60 days.</li>
<li><strong>Duplicate PRs</strong>: Ensure <code>branch-prefix</code> is consistent across workflow runs.</li>
</ul>
<p>Check the <a href="https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/using-workflow-run-logs">GitHub Actions logs</a> for detailed error messages.</p>
</section>
<section id="ecosystem-integration" class="level2">
<h2 class="anchored" data-anchor-id="ecosystem-integration">Ecosystem Integration</h2>
<p><strong>Quarto Extensions Updater</strong> complements existing tools in the Quarto ecosystem:</p>
<ul>
<li><strong><a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a></strong>: A VS Code extension for installing and managing Quarto extensions with a GUI. Extensions installed via Quarto Wizard include the <code>source</code> metadata needed for automated updates.</li>
<li><strong><a href="https://github.com/mcanouil/quarto-extensions">Quarto Extensions</a></strong>: The community registry that powers both Quarto Wizard and Quarto Extensions Updater.</li>
<li><strong><a href="https://quarto.org/">Quarto CLI</a></strong>: The action uses <code>quarto add</code> commands to perform updates, ensuring compatibility with Quarto’s extension system.</li>
</ul>
<div class="highlight">
<p>Install extensions with <strong>Quarto Wizard</strong>, automate updates with <strong>Quarto Extensions Updater</strong>, and enjoy <strong>seamless maintenance</strong> across your entire Quarto project portfolio.</p>
</div>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p><strong>Quarto Extensions Updater</strong> brings the automation and reliability of Dependabot to Quarto extension management. By automating the update workflow, you can focus on creating compelling content rather than tracking extension releases.</p>
<p>The action handles checking for updates, creating pull requests, and maintaining version metadata, all whilst providing clear, detailed information about each change. Whether you’re managing a personal blog, maintaining a collaborative research project, or overseeing a documentation website, this tool ensures your extensions stay current without demanding your constant attention.</p>
<div class="highlight">
<p>Transform your extension maintenance from a <strong>tedious manual process</strong> into an <strong>effortless automated workflow</strong>.</p>
</div>
<p>Ready to automate your Quarto extension updates? Add the workflow to your repository today and experience the freedom of automated maintenance.</p>
</section>
<section id="resources" class="level2">
<h2 class="anchored" data-anchor-id="resources">Resources</h2>
<ul>
<li><strong><a href="https://github.com/mcanouil/quarto-extensions-updater">Quarto Extensions Updater</a></strong>: GitHub repository and documentation.</li>
<li><strong><a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a></strong>: VS Code / Positron extension for managing Quarto extensions.</li>
<li><strong><a href="https://m.canouil.dev/quarto-extensions/">Quarto Extensions Registry</a></strong>: Browse and install Quarto extensions.</li>
<li><strong><a href="https://quarto.org/docs/extensions/">Quarto Extensions Documentation</a></strong>: Official guide to Quarto extensions.</li>
<li><strong><a href="https://docs.github.com/en/actions">GitHub Actions Documentation</a></strong>: Learn more about GitHub Actions workflows.</li>
</ul>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Quarto CLI does not natively track installation sources as of version 1.8.24. See <a href="https://github.com/quarto-dev/quarto-cli/issues/11468">quarto-dev/quarto-cli#11468</a>.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Extensions} {Updater:} {Automating} {Extension}
    {Maintenance} with {GitHub} {Actions}},
  date = {2025-12-12},
  url = {https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-12-12). Quarto Extensions Updater: Automating
Extension Maintenance with GitHub Actions. <em>Mickael.canouil.fr</em>.
<a href="https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/">https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>github actions</category>
  <category>extensions</category>
  <category>automation</category>
  <category>devops</category>
  <guid>https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/</guid>
  <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-12-12-quarto-extensions-updater/featured.png" medium="image" type="image/png" height="77" width="144"/>
</item>
<item>
  <title>Optimising VS Code and Positron for Quarto: Essential Settings for Better Editing</title>
  <link>https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Visual Studio Code logo on the top left and Positron logo on the top right with Quarto logo in the bottom centre, connected by configuration gear icon." width="600"></a></p>
</figure>
</div>
<p>After years of working with Quarto in both VS Code and Positron, I’ve refined a set of editor settings that significantly improve my document editing experience. These configurations address common pain points like div folding, Git diff readability, and workspace performance.</p>
<div class="highlight">
<p>The path to <strong>effortless editing</strong> is paved with <strong>thoughtful settings</strong>.</p>
</div>
<p>Whether you’re just starting with Quarto or looking to optimise your existing setup, these settings can help streamline your workflow and reduce friction in your daily editing tasks.</p>
<section id="core-quarto-settings" class="level2">
<h2 class="anchored" data-anchor-id="core-quarto-settings">Core Quarto Settings</h2>
<p>The foundation of my configuration lies in language-specific settings for Quarto documents. These settings ensure consistent formatting and provide visual aids for working with Quarto.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>settings.json</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-1" data-filename="settings.json" style="background: #f1f3f5;"><pre class="sourceCode json code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-1-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"[quarto]"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-1-3" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.defaultFormatter"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto.quarto"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-1-4" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.wordWrap"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"off"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-1-5" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.guides.bracketPairs"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"active"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-1-6" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.language.brackets"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">":::"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">":::"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]]</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-1" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-1-7" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.language.colorizedBracketPairs"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<span id="annotated-cell-1-8">      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">":::"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">":::"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-1-9">    <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-1-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-1-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-1" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="3" data-code-annotation="1">Uses the official Quarto extension as the default formatter for Quarto files, ensuring consistent formatting of code blocks and code cells.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="4" data-code-annotation="2">Disables word wrapping to prevent markdown pipe tables from wrapping awkwardly.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="5" data-code-annotation="3">Shows bracket pair guides only for the active bracket pair, reducing visual clutter whilst maintaining helpful structural indicators.</span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="6" data-code-annotation="4">Defines Quarto’s div syntax (<code>:::</code> for opening, <code>:::</code> for closing) as bracket pairs, enabling folding and navigation.<sup>1</sup></span>
</dd>
<dt data-target-cell="annotated-cell-1" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-1" data-code-lines="7" data-code-annotation="5">Adds colourisation to div brackets, making it easier to match opening and closing divs in deeply nested structures.</span>
</dd>
</dl>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Disabling word wrap setting might seem counter-intuitive, but by keeping one sentence per line, Git diffs show exactly which sentences changed rather than displaying entire paragraph modifications. This also prevents markdown pipe tables from wrapping mid-cell, which can break table rendering.</p>
</div>
</div>
</section>
<section id="visual-line-length-guides" class="level2">
<h2 class="anchored" data-anchor-id="visual-line-length-guides">Visual Line Length Guides</h2>
<p>To maintain readable line lengths whilst avoiding word wrap, I use vertical rulers as visual guides.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>settings.json</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-2" data-filename="settings.json" style="background: #f1f3f5;"><pre class="sourceCode json code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.rulers"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-3" class="code-annotation-target">    <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">80</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-5" class="code-annotation-target">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"color"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#b22222"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-2-6" class="code-annotation-target">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"column"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">120</span></span>
<span id="annotated-cell-2-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-2-8">  <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-2-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="3" data-code-annotation="1">Displays a subtle vertical line at 80 characters, serving as a soft guideline for line length.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="5" data-code-annotation="2">Sets a firebrick red colour for the second ruler to draw attention to excessively long lines.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="6" data-code-annotation="3">Places the warning ruler at 120 characters, indicating lines that should ideally be broken up.</span>
</dd>
</dl>
<p>This two-ruler approach provides progressive feedback: the first ruler suggests an ideal/standard line length, whilst the second warns when lines become problematically long.</p>
</section>
<section id="workspace-performance-optimisation" class="level2">
<h2 class="anchored" data-anchor-id="workspace-performance-optimisation">Workspace Performance Optimisation</h2>
<p>Quarto projects generate substantial build artefacts in the <code>.quarto</code> directory. Excluding this directory from file watching and search operations improves editor performance, especially in large projects.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>settings.json</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="settings.json" style="background: #f1f3f5;"><pre class="sourceCode json code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"files.watcherExclude"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-3" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"**/.quarto/**"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span></span>
<span id="annotated-cell-3-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-3-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"search.exclude"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-3-6" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"*/.quarto"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span></span>
<span id="annotated-cell-3-7">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="3" data-code-annotation="1">Prevents the editor from monitoring changes in <code>.quarto</code> directories, reducing unnecessary file system operations and improving responsiveness.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="6" data-code-annotation="2">Excludes <code>.quarto</code> directories from search results, eliminating irrelevant build artefacts from workspace searches.</span>
</dd>
</dl>
<p>These exclusions are particularly beneficial in projects with frequent renders or when working with large documents that generate extensive intermediate files.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Another directory to consider excluding is the <code>_freeze</code> directory if you’re using the freeze feature extensively, as this can also contain a large number of files. You can also exclude other render output directories specific to your workflow, such as <code>_site</code>, <code>_book</code>, and <code>_manuscript</code>.</p>
</div>
</div>
</section>
<section id="github-copilot-integration" class="level2">
<h2 class="anchored" data-anchor-id="github-copilot-integration">GitHub Copilot Integration</h2>
<p>The Quarto extension disables GitHub Copilot by default for Quarto files.<sup>2</sup> If you want to use GitHub Copilot’s AI assistance whilst editing Quarto documents, you must explicitly enable it in your settings.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>settings.json</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-4" data-filename="settings.json" style="background: #f1f3f5;"><pre class="sourceCode json code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-4-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"github.copilot.enable"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-3" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"quarto"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span></span>
<span id="annotated-cell-4-4">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-4-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="3" data-code-annotation="1">Overrides the Quarto extension’s default behaviour to enable GitHub Copilot suggestions in Quarto documents.</span>
</dd>
</dl>
</section>
<section id="current-limitations-and-workarounds" class="level2">
<h2 class="anchored" data-anchor-id="current-limitations-and-workarounds">Current Limitations and Workarounds</h2>
<section id="div-folding-support" class="level3">
<h3 class="anchored" data-anchor-id="div-folding-support">Div Folding Support</h3>
<p>Whilst the bracket pair configuration enables basic folding for divs, native folding support in the Quarto extension remains an ongoing feature request.<sup>3</sup> The current workaround using <code>editor.language.brackets</code> provides functional folding, though it may not be as sophisticated as built-in support would be.</p>
</section>
<section id="applying-these-settings" class="level3">
<h3 class="anchored" data-anchor-id="applying-these-settings">Applying These Settings</h3>
<p>To use these settings in your environment:</p>
<ol type="1">
<li>Open your VS Code or Positron settings (<span class="visually-hidden"></span>).</li>
<li>Click the “<strong>Open Settings (JSON)</strong>” icon in the top right corner.</li>
<li>Add these configurations to your <code>settings.json</code> file.</li>
<li>Save the file to apply the changes immediately.</li>
</ol>
<p>Alternatively, you can add these to a workspace settings file (<code>.vscode/settings.json</code>) to apply them only to specific projects.</p>
</section>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>These settings represent opinionated refinement in my Quarto editing workflow. They address practical challenges like navigating complex document structures and optimising editor performance.</p>
<div class="highlight">
<p>The combination of <strong>disabled word wrap</strong>, <strong>visual rulers</strong>, and <strong>div bracket pairing</strong> creates a <strong>more maintainable</strong> and <strong>visually navigable</strong> editing experience for Quarto documents.</p>
</div>
<p>I encourage you to try these settings and adjust them to match your preferences. What works for my workflow might need tweaking for yours, and that’s perfectly fine. The goal is to reduce friction and let you focus on creating excellent content rather than fighting with your editor.</p>
<p>Do you have custom settings that enhance your Quarto editing experience? I’d love to hear about them in the discussion below.</p>
<p><em>Happy editing!</em></p>
</section>
<section id="resources-and-further-reading" class="level2">
<h2 class="anchored" data-anchor-id="resources-and-further-reading">Resources and Further Reading</h2>
<ul>
<li><strong><a href="https://github.com/quarto-dev/quarto">Quarto VS Code Extension</a></strong> - Official extension for VS Code and Positron.</li>
<li><strong><a href="https://code.visualstudio.com/docs/getstarted/settings">VS Code User and Workspace Settings</a></strong> - Comprehensive guide to VS Code configuration.</li>
<li><strong><a href="https://pandoc.org/MANUAL.html#extension-fenced_divs">Pandoc Fenced Divs Extension</a></strong> - Documentation for Quarto’s div syntax.</li>
<li><strong><a href="https://code.visualstudio.com/docs/editor/artificial-intelligence">GitHub Copilot in VS Code</a></strong> - Guide to using AI assistance in your editor.</li>
</ul>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Whilst Pandoc’s div syntax doesn’t require matching numbers of colons for opening and closing divs (see <a href="https://pandoc.org/MANUAL.html#extension-fenced_divs">Pandoc Manual: Extension fenced_divs</a>), using four colons for opening and three for closing provides clear visual distinction and enables better editor support.↩︎</p></li>
<li id="fn2"><p>The Quarto extension disables Copilot by default in its configuration (see <a href="https://github.com/quarto-dev/quarto/issues/621">quarto-dev/quarto#621</a> and <a href="https://github.com/quarto-dev/quarto/blob/eac7ac2f4676908b05e746e085c1cbf23d000563/apps/vscode/package.json#L960-L962">package.json</a>). This explicit enablement is required if you want to use Copilot in your Quarto workflow.↩︎</p></li>
<li id="fn3"><p>See <a href="https://github.com/quarto-dev/quarto/issues/670">quarto-dev/quarto#670</a> for discussion about native div folding support.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {Optimising {VS} {Code} and {Positron} for {Quarto:}
    {Essential} {Settings} for {Better} {Editing}},
  date = {2025-11-20},
  url = {https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-11-20). Optimising VS Code and Positron for Quarto:
Essential Settings for Better Editing. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/">https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>vscode</category>
  <category>positron</category>
  <category>productivity</category>
  <guid>https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/</guid>
  <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-11-20-quarto-editor-settings/featured.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>Lua Best Practices for Quarto Extensions: Building Maintainable and Robust Extensions</title>
  <link>https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Quarto logo and Lua programming language logo separated by diagonal line with a gradient." width="600"></a></p>
</figure>
</div>
<p>Quarto extensions represent one of the most powerful aspects of the publishing platform, allowing developers to extend core functionality far beyond what’s possible with configuration alone. Through extensions, you can add custom shortcodes for embedding interactive content, create filters that transform document structure, develop entirely new output formats, and integrate third-party libraries seamlessly into the rendering pipeline.</p>
<p>Whether you’re building a simple shortcode to embed tweets, creating a sophisticated filter for academic citations, or developing a complete custom format for journal submissions, Lua serves as the extensibility layer that makes it all possible. The flexibility is remarkable: extensions can process the document’s abstract syntax tree (AST), inject CSS and JavaScript dependencies, generate format-specific output, and even interact with external systems during rendering.</p>
<div class="highlight">
<p>With <strong>great power</strong> comes <strong>great responsibility</strong>.</p>
</div>
<p>As extensions become more complex and widely adopted, following established best practices becomes valuable for creating code that’s maintainable, reliable, and accessible to the broader community. Drawing from patterns established in Quarto’s extensions and emerging community standards, this guide explores the essential practices that help transform initial working extensions into well-crafted, robust solutions.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>This guide presents opinionated recommendations. Whilst these approaches have proven effective in practice, they represent one way of thinking about extension development rather than strict requirements. Quarto’s flexibility means there are often multiple valid approaches to solving the same problem. The key is choosing patterns that work for your specific context and maintaining consistency within your projects.</p>
</div>
</div>
<p>The difference between a working solution and a polished extension lies not just in functionality, but in attention to structure, error handling, documentation, and user experience. Well-crafted extensions anticipate edge cases, provide helpful error messages, support multiple output formats gracefully, and maintain backward compatibility as they evolve. They follow consistent patterns that make them easier to understand, modify, and debug.</p>
<section id="the-foundation-structure-and-organisation" class="level2">
<h2 class="anchored" data-anchor-id="the-foundation-structure-and-organisation">The Foundation: Structure and Organisation</h2>
<section id="consistent-project-structure" class="level3">
<h3 class="anchored" data-anchor-id="consistent-project-structure">Consistent Project Structure</h3>
<p>Every well-built Quarto extension benefits from a predictable structure. This consistency isn’t just aesthetic.<br>
It reduces cognitive load for contributors and makes debugging substantially easier:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="txt" style="background: #f1f3f5;"><pre class="sourceCode txt cw-auto code-with-copy"><code class="sourceCode default"><span id="cb1-1">my-extension/</span>
<span id="cb1-2">├── README.md</span>
<span id="cb1-3">├── example.qmd</span>
<span id="cb1-4">└── _extensions/</span>
<span id="cb1-5">    └── my-extension/</span>
<span id="cb1-6">        ├── _extension.yml</span>
<span id="cb1-7">        ├── my-extension.lua</span>
<span id="cb1-8">        └── _modules/          # Shared utilities (personal convention)</span>
<span id="cb1-9">            ├── utils.lua</span>
<span id="cb1-10">            └── validation.lua</span></code></pre></div></div>
<div class="highlight">
<p>Use a dedicated <code>_modules/</code> directory for shared utility functions, following <strong>DRY</strong> principles whilst maintaining extension portability across different environments.</p>
</div>
<p>The <code>_modules</code> directory shown here represents a personal organisational convention rather than a Quarto requirement. This approach involves creating a dedicated folder for shared utility modules within each extension. Rather than copying utility functions between extensions or writing everything in a single file, this modular architecture follows the DRY (Don’t Repeat Yourself) principle whilst maintaining portability. You might prefer a different structure (such as <code>lib/</code>, <code>utils/</code>, or keeping everything in the main file), but the key principle remains: consistent organisation makes maintenance easier.</p>
</section>
<section id="module-loading" class="level3">
<h3 class="anchored" data-anchor-id="module-loading">Module Loading</h3>
<p>When requiring modules in your extensions, consider using this robust path resolution pattern:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb2-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Load required modules</span></span>
<span id="cb2-2"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/utils.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%.lua$"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span>
<span id="cb2-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">validation</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">require</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>resolve_path<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_modules/validation.lua"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">):</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">gsub</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"%.lua$"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">""</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">))</span></span></code></pre></div></div>
</div>
<p>This pattern ensures modules load correctly regardless of the execution context and handles path resolution gracefully across different operating systems.<br>
If you choose a different directory structure, simply adjust the path accordingly.</p>
</section>
</section>
<section id="configuration-and-validation" class="level2">
<h2 class="anchored" data-anchor-id="configuration-and-validation">Configuration and Validation</h2>
<section id="configuration-hierarchy" class="level3">
<h3 class="anchored" data-anchor-id="configuration-hierarchy">Configuration Hierarchy</h3>
<p>Implementing a consistent configuration hierarchy can provide flexibility whilst maintaining predictable behaviour.<br>
One effective pattern is: arguments, then metadata, then defaults:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb3-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---Get configuration options with proper fallback hierarchy</span></span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">args</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table Command line arguments</span></span>
<span id="cb3-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">meta</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table Document metadata</span></span>
<span id="cb3-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">defaults</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table Default configuration</span></span>
<span id="cb3-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@return</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table Resolved configuration</span></span>
<span id="cb3-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> get_options<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">defaults</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb3-7">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="cb3-8">  </span>
<span id="cb3-9">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Start with defaults</span></span>
<span id="cb3-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">defaults</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="cb3-11">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span></span>
<span id="cb3-12">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb3-13">  </span>
<span id="cb3-14">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Override with metadata if present</span></span>
<span id="cb3-15">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb3-16">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="cb3-17">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb3-18">  </span>
<span id="cb3-19">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Override with arguments if present (highest priority)</span></span>
<span id="cb3-20">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb3-21">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">args</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="cb3-22">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb3-23">  </span>
<span id="cb3-24">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span></span>
<span id="cb3-25"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
</section>
<section id="parameter-scoping-to-avoid-conflicts" class="level3">
<h3 class="anchored" data-anchor-id="parameter-scoping-to-avoid-conflicts">Parameter Scoping to Avoid Conflicts</h3>
<p>To prevent conflicts with Quarto CLI options and other extensions, consider scoping your extension’s configuration parameters under <code>extensions</code> and your extension’s name in the document metadata or in your project configuration (<em>i.e.</em>, <code>quarto.yml</code>).<br>
This creates a clear namespace and prevents accidental collisions:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>document.qmd</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename="document.qmd" style="background: #f1f3f5;"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb4-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb4-2"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">title:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "My Document"</span></span>
<span id="cb4-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Recommended: Scoped under extensions namespace</span></span>
<span id="cb4-4"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">extensions:</span></span>
<span id="cb4-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  my-extension:</span></span>
<span id="cb4-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    theme: "modern"</span></span>
<span id="cb4-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    colour: "#ff0000"</span></span>
<span id="cb4-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    position: "top"</span></span>
<span id="cb4-9"></span>
<span id="cb4-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"># Consider avoiding: Top-level parameters that might conflict</span></span>
<span id="cb4-11"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">theme:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "modern"      # Could conflict with Quarto's theme system</span></span>
<span id="cb4-12"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">colour:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "#ff0000"    # Could conflict with other extensions</span></span>
<span id="cb4-13"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">position:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "top"      # Too generic, high chance of conflicts</span></span>
<span id="cb4-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span></code></pre></div></div>
</div>
<div class="highlight">
<p>Always <strong>scope extension configuration</strong> under the <code>extensions</code> namespace to <strong>prevent conflicts</strong> with Quarto CLI options and other extensions in the ecosystem.</p>
</div>
<p>When accessing these scoped parameters in your Lua code:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-annotation-code code-with-copy code-annotated"><code class="sourceCode lua"><span id="annotated-cell-5-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> get_extension_options<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-2">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extensions_config</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extensions</span></span>
<span id="annotated-cell-5-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extensions_config</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-5-4">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-5-5">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-5-6">  </span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-7" class="code-annotation-target">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extension_config</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extensions_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">EXTENSION_NAME</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-5-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extension_config</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="annotated-cell-5-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-5-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-5-11">  </span>
<span id="annotated-cell-5-12">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{}</span></span>
<span id="annotated-cell-5-13">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">in</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pairs</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extension_config</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="annotated-cell-5-14">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">key</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">value</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="annotated-cell-5-15">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="annotated-cell-5-16">  </span>
<span id="annotated-cell-5-17">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span></span>
<span id="annotated-cell-5-18"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="7" data-code-annotation="1"><code>EXTENSION_NAME</code> should be a constant defined in your extension representing its name.</span>
</dd>
</dl>
<p>This approach helps ensure your extension plays nicely with Quarto’s built-in options and other extensions whilst providing clear documentation for users about which parameters belong to your extension.</p>
</section>
<section id="input-validation" class="level3">
<h3 class="anchored" data-anchor-id="input-validation">Input Validation</h3>
<p>Consider validating user inputs and providing helpful error messages:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb5-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---Validate and process a theme option</span></span>
<span id="cb5-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">theme_input</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> string Theme name provided by user</span></span>
<span id="cb5-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@return</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> string Valid theme name</span></span>
<span id="cb5-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> process_theme_option<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">theme_input</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-5">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">valid_themes</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"dark"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"modern"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"classic"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span>
<span id="cb5-6">  </span>
<span id="cb5-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">theme_input</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb5-8">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>warning<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"No theme specified, using default"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-9">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span></span>
<span id="cb5-10">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb5-11">  </span>
<span id="cb5-12">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Check if theme is in our list of valid themes</span></span>
<span id="cb5-13">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">valid_themes</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">do</span></span>
<span id="cb5-14">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">valid_themes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">i</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">==</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">theme_input</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb5-15">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">theme_input</span></span>
<span id="cb5-16">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb5-17">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb5-18">  </span>
<span id="cb5-19">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>error<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Invalid theme: "</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">theme_input</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">". Valid options: light, dark, modern, classic"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb5-20">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"light"</span></span>
<span id="cb5-21"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
</section>
</section>
<section id="documentation-as-code" class="level2">
<h2 class="anchored" data-anchor-id="documentation-as-code">Documentation as Code</h2>
<section id="luadoc-annotations" class="level3">
<h3 class="anchored" data-anchor-id="luadoc-annotations">LuaDoc Annotations</h3>
<p>Well-crafted extensions benefit from comprehensive <a href="https://keplerproject.github.io/luadoc/">LuaDoc</a> annotations.<br>
These aren’t just comments. They enable IDE features, catch type errors early, and serve as living documentation:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---Processes a div element and adds custom styling</span></span>
<span id="cb6-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">div</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Div The Pandoc div element to process</span></span>
<span id="cb6-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">options</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table Configuration options</span></span>
<span id="cb6-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@return</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> Div|Null Modified div or null if processing failed</span></span>
<span id="cb6-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> process_div<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">div</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">options</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb6-6">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Implementation here</span></span>
<span id="cb6-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb6-8"></span>
<span id="cb6-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---Configuration options for the extension</span></span>
<span id="cb6-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@type</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> table</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">&lt;string</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">, any</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">&gt;</span></span>
<span id="cb6-11"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">default_options</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb6-12">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">style</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'default'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb6-13">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">position</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'top'</span></span>
<span id="cb6-14"><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
</div>
<div class="highlight">
<p>Comprehensive <strong>LuaDoc annotations</strong> with <code>@param</code>, <code>@return</code>, and <code>@type</code> enable IDE features, type checking, and serve as <strong>living documentation</strong> that evolves with your code.</p>
</div>
<p>Modern development environments like VS Code with the Lua Language Server extension use these annotations to provide intelligent autocomplete, type checking, and hover documentation.</p>
</section>
<section id="structured-code-organisation" class="level3">
<h3 class="anchored" data-anchor-id="structured-code-organisation">Structured Code Organisation</h3>
<p>Organising your code with clear section dividers and consistent ordering can improve maintainability:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--[[</span></span>
<span id="cb7-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">License header</span></span>
<span id="cb7-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]]</span></span>
<span id="cb7-4"></span>
<span id="cb7-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Extension name constant</span></span>
<span id="cb7-6"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">EXTENSION_NAME</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-extension"</span></span>
<span id="cb7-7"></span>
<span id="cb7-8"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Load required modules</span></span>
<span id="cb7-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Module loading here</span></span>
<span id="cb7-10"></span>
<span id="cb7-11"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Filter/Shortcode constants</span></span>
<span id="cb7-12"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">DEFAULT_THEME</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'light'</span></span>
<span id="cb7-13"></span>
<span id="cb7-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Filter/Shortcode variables</span></span>
<span id="cb7-15"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">dependency_added</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">false</span></span>
<span id="cb7-16"></span>
<span id="cb7-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Private helper functions</span></span>
<span id="cb7-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Internal functions here</span></span>
<span id="cb7-19"></span>
<span id="cb7-20"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Public API functions</span></span>
<span id="cb7-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Main extension logic here</span></span>
<span id="cb7-22"></span>
<span id="cb7-23"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">--- Extension registration</span></span>
<span id="cb7-24"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Return statement here</span></span></code></pre></div></div>
</div>
<p>This structure makes it easier to navigate large extension files and understand the code’s organisation at a glance.</p>
</section>
</section>
<section id="error-handling-and-logging" class="level2">
<h2 class="anchored" data-anchor-id="error-handling-and-logging">Error Handling and Logging</h2>
<section id="graceful-degradation" class="level3">
<h3 class="anchored" data-anchor-id="graceful-degradation">Graceful Degradation</h3>
<p>Robust extensions handle unexpected situations elegantly.<br>
Consider checking for unsupported formats and returning <code>pandoc.Null()</code> rather than throwing errors:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Check format compatibility first</span></span>
<span id="cb8-2"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">not</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html:js"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb8-3">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>Null<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="cb8-4"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
<p>For extensions that support multiple formats, provide appropriate implementations for each:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb9-1"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"html"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb9-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'html'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">html_output</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb9-3"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">elseif</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">doc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>is_format<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"latex"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb9-4">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>RawInline<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'latex'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">latex_output</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb9-5"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span></span>
<span id="cb9-6">  <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Unsupported format - fail gracefully</span></span>
<span id="cb9-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>Null<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">()</span></span>
<span id="cb9-8"><span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
<div class="highlight">
<p><strong>Check format compatibility</strong> with <code>quarto.doc.is_format()</code> and return <code>pandoc.Null()</code> for unsupported formats <strong>rather than throwing errors</strong> that break the rendering process.</p>
</div>
</section>
<section id="informative-logging" class="level3">
<h3 class="anchored" data-anchor-id="informative-logging">Informative Logging</h3>
<p>Using Quarto’s logging facilities can provide helpful feedback.<br>
The <code>quarto.log</code> module supports different log levels and can be activated with the <code>--trace</code> flag:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb10-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- For debugging during development</span></span>
<span id="cb10-2"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>output<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Processing element:"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">element</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-3"></span>
<span id="cb10-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- For user-facing warnings</span></span>
<span id="cb10-5"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>warning<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Deprecated configuration detected. Please update your settings."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb10-6"></span>
<span id="cb10-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- For error conditions</span></span>
<span id="cb10-8"><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>error<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Required dependency not found. Extension cannot proceed."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span></code></pre></div></div>
</div>
<p>When using shared utility modules, establishing consistent logging patterns can be helpful:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb11-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---Log an error message with extension context</span></span>
<span id="cb11-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">extension_name</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> string Name of the extension</span></span>
<span id="cb11-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">@param</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span><span class="cv" style="color: #5E5E5E;
background-color: null;
font-style: italic;">message</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> string Error message to log</span></span>
<span id="cb11-4"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> log_error<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extension_name</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">message</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb11-5">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">full_message</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"["</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">extension_name</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"] "</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">..</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">message</span></span>
<span id="cb11-6">  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>error<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">full_message</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb11-7"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
</section>
</section>
<section id="distribution-and-maintenance" class="level2">
<h2 class="anchored" data-anchor-id="distribution-and-maintenance">Distribution and Maintenance</h2>
<section id="semantic-versioning" class="level3">
<h3 class="anchored" data-anchor-id="semantic-versioning">Semantic Versioning</h3>
<p>Using semantic versioning for your extensions helps maintain backward compatibility:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>_extension.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb12" data-filename="_extension.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb12-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> My Extension</span></span>
<span id="cb12-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1.2.0</span></span>
<span id="cb12-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">quarto-required</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"&gt;=1.2.0"</span></span></code></pre></div></div>
</div>
</section>
<section id="deprecation-handling" class="level3">
<h3 class="anchored" data-anchor-id="deprecation-handling">Deprecation Handling</h3>
<p>When changing extension interfaces, providing deprecation warnings helps users transition smoothly:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Lua</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb13" data-filename="Lua" style="background: #f1f3f5;"><pre class="sourceCode lua code-with-copy"><code class="sourceCode lua"><span id="cb13-1"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">local</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> handle_deprecated_config<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb13-2">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'old-option'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">]</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">then</span></span>
<span id="cb13-3">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">quarto</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">log</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>warning<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"'old-option' is deprecated. Use 'new-option' instead."</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">)</span></span>
<span id="cb13-4">    <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-- Still process the old option for compatibility</span></span>
<span id="cb13-5">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">pandoc</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">utils</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span>stringify<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">meta</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'old-option'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">])</span></span>
<span id="cb13-6">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span>
<span id="cb13-7">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">nil</span></span>
<span id="cb13-8"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span></span></code></pre></div></div>
</div>
</section>
</section>
<section id="conclusion" class="level2">
<h2 class="anchored" data-anchor-id="conclusion">Conclusion</h2>
<p>Building solid Quarto extensions is a journey of continuous improvement. Start with working code and gradually enhance structure, documentation, error handling, and user experience. The practices outlined in this guide represent one perspective on extension development, drawn from patterns observed in extensions and general software development principles.</p>
<p>Remember that these are recommendations rather than rigid rules. Quarto’s flexibility means there are often multiple valid approaches to solving the same problem. The key is finding patterns that work for your specific context and maintaining consistency within your projects.</p>
<p>Whether you’re enhancing a simple shortcode or expanding a complex multi-format filter, investing in clarity, documentation, and robust error handling pays dividends as your extension grows and finds new users. Good code is written for humans first, computers second.</p>
<div class="highlight">
<p><strong>Proper releases</strong> with semantic versioning enable tools like <strong>Quarto Wizard’s automatic update detection</strong> for seamless extension maintenance.</p>
</div>
<p>Finally, consider tagging and releasing your extensions using semantic versioning. Creating releases not only provides users with stable versions to reference but also enables powerful tooling like <a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a>’s update feature to automatically manage extension lifecycles. With proper releases, users can easily discover when updates are available, understand what’s changed through release notes, and confidently upgrade their extensions knowing they’re moving to a tested, stable version. This thoughtful approach to release management helps transform your extension from a working script into a reliable, maintainable tool that others can depend upon.</p>
<p><em>Happy extending!</em></p>
</section>
<section id="resources-and-further-reading" class="level2">
<h2 class="anchored" data-anchor-id="resources-and-further-reading">Resources and Further Reading</h2>
<ul>
<li><strong><a href="https://quarto.org/docs/extensions/creating.html">Creating Extensions</a></strong> - Official Quarto guide to building extensions.</li>
<li><strong><a href="https://quarto.org/docs/extensions/lua.html">Lua Development</a></strong> - Getting started with Lua for Quarto extensions.</li>
<li><strong><a href="https://pandoc.org/lua-filters.html">Writing Lua Filters</a></strong> - Pandoc’s official Lua filter documentation.</li>
<li><strong><a href="https://keplerproject.github.io/luadoc/">LuaDoc</a></strong> - Documentation generation tool for Lua code.</li>
<li><strong><a href="https://tylerneylon.com/a/learn-lua/">Learn Lua in 15 Minutes</a></strong> - Quick introduction to Lua syntax and concepts.</li>
<li><strong><a href="https://github.com/mcanouil/quarto-wizard">Quarto Wizard</a></strong> - Tool for managing and updating Quarto extensions.</li>
</ul>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {Lua {Best} {Practices} for {Quarto} {Extensions:} {Building}
    {Maintainable} and {Robust} {Extensions}},
  date = {2025-11-06},
  url = {https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-11-06). Lua Best Practices for Quarto Extensions:
Building Maintainable and Robust Extensions.
<em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/">https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>extensions</category>
  <category>productivity</category>
  <category>best practices</category>
  <category>lua</category>
  <guid>https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/</guid>
  <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-11-06-quarto-extensions-lua/featured.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>Quarto Wizard 1.0.0: Democratising Quarto Extension Management</title>
  <link>https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="thanks">
<p>Thank you to the Quarto team for featuring this article on <a href="https://quarto.org/docs/blog/posts/2025-10-20-quarto-wizard-1-0-0/">quarto.org</a>, where it was originally published.</p>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/featured.png" class="img-featured img-fluid quarto-figure quarto-figure-center figure-img" alt="Cartoon dog wizard wearing blue hat with red band holding magic wand creating HTML and CSS code scrolls in starry night scene." width="600"></a></p>
</figure>
</div>
<p>I’m absolutely thrilled to announce <strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard 1.0.0</strong>, a groundbreaking extension for Visual Studio Code and Positron that transforms how you interact with the <iconify-icon inline="" icon="simple-icons:quarto" style="color:#74aadb;" aria-label="Quarto icon" title="Quarto icon"></iconify-icon> Quarto ecosystem. If you’ve ever found yourself wrestling with command-line extension management or struggling to discover the perfect template for your project, this tool is about to become your new best friend.</p>
<div class="highlight">
<p><strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard 1.0.0</strong> is now available!</p>
</div>
<p>Install it today from the <a href="https://marketplace.visualstudio.com/items?itemName=mcanouil.quarto-wizard">VS Code marketplace</a> or <a href="https://open-vsx.org/extension/mcanouil/quarto-wizard">Open VSX Registry</a>:</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<ul>
<li><p>Via VS Code or Positron Extensions view:</p>
<ul>
<li>Search for “Quarto Wizard”.</li>
<li>Click “Install”.</li>
</ul>
<p><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/extensions-marketplace-dark.png" title="Extensions View: Marketplace" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center" data-group="quarto-wizard-dark" alt="Visual Studio Code Extensions Marketplace showing Quarto Wizard search
results with install button.
" width="500"> <img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/extensions-marketplace-light.png" title="Extensions View: Marketplace" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center" data-group="quarto-wizard-light" alt="Visual Studio Code Extensions Marketplace showing Quarto Wizard search
results with install button.
" width="500"></p></li>
</ul>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<ul>
<li><p>Via the command line:</p>
<div class="tabset-margin-container"></div><div class="panel-tabset">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true" href="">Visual Studio Code</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false" href="">Positron</a></li></ul>
<div class="tab-content">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Terminal</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="Terminal" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">code</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--install-extension</span> mcanouil.quarto-wizard</span></code></pre></div></div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Be sure to execute the command <em>Shell Command: Install ‘code’ command in PATH</em> from Visual Studio Code’s Command Palette (<span class="visually-hidden"></span>) if you haven’t done so already.</p>
</div>
</div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>Terminal</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="Terminal" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">positron</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--install-extension</span> mcanouil.quarto-wizard</span></code></pre></div></div>
</div>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>Be sure to execute the command <em>Shell Command: Install ‘positron’ command in PATH</em> from Positron’s Command Palette (<span class="visually-hidden"></span>) if you haven’t done so already.</p>
</div>
</div>
</div>
</div>
</div></li>
</ul>
</div>
</div>
</div>
<p>Quarto has revolutionised scientific and technical publishing by enabling reproducible documents that seamlessly blend code, narrative text, and visualisation. However, one persistent friction point has been managing the rich and ever-growing ecosystem of extensions and templates—until now.</p>
<section id="b58fc729-690b-4000-b19f-365a4093b2ff7b7b3c206d6574612071756172746f2d77697a617264203e7d7d-quarto-wizard-your-gui-for-quarto-extensions" class="level2">
<h2 class="anchored" data-anchor-id="b58fc729-690b-4000-b19f-365a4093b2ff7b7b3c206d6574612071756172746f2d77697a617264203e7d7d-quarto-wizard-your-gui-for-quarto-extensions"><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard: Your GUI for Quarto extensions</h2>
<p>I designed <strong>Quarto Wizard</strong> to address a fundamental challenge I’ve observed in the community: whilst Quarto’s command-line interface is powerful, many users prefer visual interfaces for discovering, installing, and managing extensions.</p>
<div class="highlight">
<p><strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard</strong> makes it simple to <strong>discover</strong>, <strong>install</strong>, and <strong>manage</strong> Quarto extensions and templates directly in your IDE.</p>
</div>
<section id="seamless-ide-integration" class="level3">
<h3 class="anchored" data-anchor-id="seamless-ide-integration">Seamless IDE integration</h3>
<p><strong>Quarto Wizard integrates beautifully with both the VS Code and Positron ecosystems</strong>, appearing as a dedicated icon in the Activity Bar alongside your other development tools. This provides instant access to extension management without disrupting your coding flow, whether you’re in Microsoft’s VS Code or Posit’s new Positron IDE.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-activity-bar-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-activity-bar-dark.png" title="Quarto Wizard Explorer View (Dark)" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard Extensions Installed panel in Visual Studio Code showing
no extensions installed message with green Install Extensions button.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-activity-bar-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-activity-bar-light.png" title="Quarto Wizard Explorer View (Light)" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard Extensions Installed panel in Visual Studio Code showing
no extensions installed message with green Install Extensions button.
" width="500"></a></p>
</figure>
</div>
<p>The solution is <strong>multi-modal installation</strong>: you can now install extensions through multiple pathways that suit your workflow: from the command line, through the web directory, or via the <strong>Quarto Wizard</strong> GUI in your IDE.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<ol type="1">
<li>Open the Command Palette (<span class="visually-hidden"></span>).</li>
<li>Type <code>Quarto Wizard: Install Extensions</code> and select it.</li>
<li>Browse the list of available Quarto extensions.</li>
<li>Select the Quarto extension(s) you want to install.</li>
<li>Answer the prompts to confirm the installation.</li>
</ol>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-install-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-install-dark.png" title="Quarto Wizard: Install Extensions (Dark)" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard extension selection dialog showing list of available
extensions with checkboxes including LIVE, HIGHLIGHT TEXT, GITHUB, and
other Quarto extensions.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-install-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-install-light.png" title="Quarto Wizard: Install Extensions (Light)" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard extension selection dialog showing list of available
extensions with checkboxes including LIVE, HIGHLIGHT TEXT, GITHUB, and
other Quarto extensions.
" width="500"></a></p>
</figure>
</div>
</div>
</div>
</div>
</section>
<section id="intelligent-extension-management" class="level3">
<h3 class="anchored" data-anchor-id="intelligent-extension-management">Intelligent extension management</h3>
<p>The <strong>“Recently Installed Extensions”</strong> feature helps track your workflow and easily reproduce project setups across different environments. This is invaluable for researchers collaborating across multiple machines or teaching workshops where consistent setups are essential, regardless of whether team members use VS Code or Positron.</p>
<p>What makes this particularly powerful is that <strong>Quarto Wizard</strong> tracks which extensions were installed through its interface by adding <code>source</code> metadata to the <code>_extensions.yml</code> file, enabling seamless updates and removals.<sup>1</sup> This source tracking transforms extension maintenance from manual archaeology into an effortless workflow. The extension maintains detailed metadata about installed extensions, enabling batch operations and dependency tracking. The Explorer View provides a comprehensive overview of all installed extensions with visual indicators for updates and management options.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-update-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-update-dark.png" title="Quarto Wizard: Explorer View Update (Dark)" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard Extensions Installed panel showing expanded iconify
extension details with update button and version information.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-update-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-update-light.png" title="Quarto Wizard: Explorer View Update (Light)" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Wizard Extensions Installed panel showing expanded iconify
extension details with update button and version information.
" width="500"></a></p>
</figure>
</div>
</section>
<section id="template-workflow-simplified" class="level3">
<h3 class="anchored" data-anchor-id="template-workflow-simplified">Template workflow simplified</h3>
<p>Beyond extension management, I’ve designed <strong>Quarto Wizard</strong> to ease the process of discovering and using document templates. Once you’ve selected a template, <strong>Quarto Wizard</strong> lets you customise and save the document. The file is not created until you confirm, allowing you to adjust the filename and location.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row quarto-layout-valign-center">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<ol type="1">
<li>Open the Command Palette (<span class="visually-hidden"></span>).</li>
<li>Type <code>Quarto Wizard: Use Template</code> and select it.</li>
<li>Browse the list of available Quarto templates.</li>
<li>Select the Quarto template(s) you want to use.</li>
<li>Answer the prompts to confirm the selection.</li>
</ol>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-template-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-template-dark.png" title="Quarto Wizard: Use Template (Dark)" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Visual Studio Code showing Quarto Wizard with installed extensions list
and document editor displaying invoice template with YAML frontmatter.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/vscode-template-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/vscode-template-light.png" title="Quarto Wizard: Use Template (Light)" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Visual Studio Code showing Quarto Wizard with installed extensions list
and document editor displaying invoice template with YAML frontmatter.
" width="500"></a></p>
</figure>
</div>
</div>
</div>
</div>
</section>
</section>
<section id="powered-by-a-comprehensive-extension-directory" class="level2">
<h2 class="anchored" data-anchor-id="powered-by-a-comprehensive-extension-directory">Powered by a comprehensive extension directory</h2>
<div class="highlight">
<p><strong><span class="quarto-wizard" title="Quarto Wizard Logo" aria-label="Quarto Wizard Logo"></span> Quarto Wizard</strong> transforms tedious extension hunting into <strong>one-click shopping for 250+ powerful document enhancements</strong>.</p>
</div>
<section id="a-curated-catalogue-of-250-extensions" class="level3">
<h3 class="anchored" data-anchor-id="a-curated-catalogue-of-250-extensions">A curated catalogue of 250+ extensions</h3>
<p>At the heart of <strong>Quarto Wizard</strong> lies the <a href="https://m.canouil.dev/quarto-extensions/">Quarto Extensions directory (m.canouil.dev/quarto-extensions/)</a>, a comprehensive listing I maintain that catalogues extensions from across the entire Quarto ecosystem.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-home-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-home-dark.png" title="Mickaël CANOUIL's Quarto Extensions directory" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website displaying grid of extension cards including
webr, Reveal.js Clean theme, and Hikmah Academic templates.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-home-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-home-light.png" title="Mickaël CANOUIL's Quarto Extensions directory" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website displaying grid of extension cards including
webr, Reveal.js Clean theme, and Hikmah Academic templates.
" width="500"></a></p>
</figure>
</div>
<p>To date, it includes over 250 extensions contributed by the community, covering a vast array of functionalities from citation management to interactive visualisations. This directory powers <strong>Quarto Wizard</strong>’s discovery features, providing rich metadata about each extension including descriptions, licensing, version tags, and GitHub stars. The directory is continuously updated through GitHub’s API, ensuring you always have access to the latest extensions from the community.</p>
</section>
<section id="one-click-installation-from-the-web" class="level3">
<h3 class="anchored" data-anchor-id="one-click-installation-from-the-web">One-click installation from the web</h3>
<p>What’s particularly exciting is that you can install extensions or use templates <strong>directly from the website itself</strong>. Each extension listed at <a href="https://m.canouil.dev/quarto-extensions/">m.canouil.dev/quarto-extensions/</a> includes multiple installation options: traditional command-line via terminal, or one-click installation through <strong>Quarto Wizard</strong> in VS Code, Positron, or VSCodium. Simply browse the directory, find the extension you need, and choose your preferred installation method. The website generates the appropriate commands or launches your IDE directly. This flexibility means teams with mixed technical backgrounds can all access the same powerful extensions.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-modal-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-modal-dark.png" title="Mickaël CANOUIL's Quarto Extensions install modal" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website with Install Options popup showing manual
terminal command and Quarto Wizard installation options for Visual
Studio Code, Positron, and VSCodium.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-modal-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-modal-light.png" title="Mickaël CANOUIL's Quarto Extensions install modal" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website with Install Options popup showing manual
terminal command and Quarto Wizard installation options for Visual
Studio Code, Positron, and VSCodium.
" width="500"></a></p>
</figure>
</div>
<p>For example, you might want to add the <a href="https://github.com/mcanouil/quarto-iconify"><strong>Iconify</strong></a> extension to access over 200,000 open source vector icons in your documents, or the <a href="https://github.com/mcanouil/quarto-animate"><strong>Animate</strong></a> extension to bring your presentations to life with CSS animations. Perhaps the <a href="https://github.com/mcanouil/quarto-spotlight"><strong>Spotlight</strong></a> extension for Reveal.js catches your eye for creating dramatic Reveal.js presentations that highlight your mouse position. All of these extensions (and hundreds more) are just a click away.</p>
</section>
<section id="template-discovery-made-easy" class="level3">
<h3 class="anchored" data-anchor-id="template-discovery-made-easy">Template discovery made easy</h3>
<p>Additionally, the Quarto Extensions directory excels at <strong>template discovery and deployment</strong> which is enhanced with powerful filtering options: you can sort by recently updated, filter by popularity, browse by categories (<em>i.e.</em>, Shortcodes, Filters, Formats, Projects, Reveal.js Plugins), or search for specific functionality. Each extension clearly indicates whether it’s a template with <strong>“Use”</strong> buttons alongside <strong>“Install”</strong> options.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-template-dark.png" class="lightbox" data-gallery="quarto-wizard-dark"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-template-dark.png" title="Mickaël CANOUIL's Quarto Extensions list view filtered by formats" class="dark-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website in list view showing Template extensions with
install and use buttons.
" width="500"></a></p>
</figure>
</div>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/media/quarto-extensions-template-light.png" class="lightbox" data-gallery="quarto-wizard-light"><img src="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/assets/media/quarto-extensions-template-light.png" title="Mickaël CANOUIL's Quarto Extensions list view filtered by formats" class="light-content img-fluid img-thumbnail rounded-3 border-light quarto-figure quarto-figure-center figure-img" alt="Quarto Extensions website in list view showing Template extensions with
install and use buttons.
" width="500"></a></p>
</figure>
</div>
<p>Whether you’re crafting an academic paper using journal-specific formats, creating professional invoices with my <a href="https://github.com/mcanouil/quarto-invoice"><strong>Invoice</strong></a> extension template, or building stunning presentations with themed templates like my <a href="https://github.com/mcanouil/quarto-revealjs-coeos"><strong>Reveal.js Coeos</strong></a> extension, browsing available templates becomes as simple as scrolling through a curated gallery.</p>
<p>This directory creates a seamless experience: instead of manually searching GitHub repositories or memorising command-line syntax, you can browse hundreds of extensions with detailed information at your fingertips. This transforms extension discovery from a treasure hunt into a curated shopping experience.</p>
</section>
</section>
<section id="addressing-real-workflow-friction" class="level2">
<h2 class="anchored" data-anchor-id="addressing-real-workflow-friction">Addressing real workflow friction</h2>
<p>I designed <strong>Quarto Wizard</strong> and the extension directory at <a href="https://m.canouil.dev/quarto-extensions/">m.canouil.dev/quarto-extensions</a> to directly tackle several persistent Quarto pain points I’ve encountered:</p>
<ul>
<li><p><strong>Discovery challenges</strong>: Finding relevant extensions in the growing ecosystem becomes intuitive through the visual browser interface powered by the comprehensive extensions directory.</p></li>
<li><p><strong>Command-line intimidation</strong>: Users who prefer graphical interfaces no longer need to memorise terminal commands.</p></li>
<li><p><strong>Document setup complexity</strong>: Template-based document initialisation eliminates manual YAML configuration.</p></li>
<li><p><strong>Extension maintenance</strong>: Updates, removals, and dependency management become point-and-click operations rather than command-line archaeology.</p></li>
<li><p><strong>Source tracking</strong>: <strong>Quarto Wizard</strong> automatically adds source metadata to installed extensions, enabling future updates and proper version management.</p></li>
<li><p><strong>Stable installations</strong>: <strong>Quarto Wizard</strong> installs extensions from GitHub releases/tags by default instead of the potentially unstable default branch, ensuring more reliable installations and more replicable environments.</p></li>
</ul>
</section>
<section id="perfect-for-diverse-use-cases" class="level2">
<h2 class="anchored" data-anchor-id="perfect-for-diverse-use-cases">Perfect for diverse use cases</h2>
<p>The extension shines across multiple scenarios:</p>
<ul>
<li><p><strong>Academic researchers</strong> can quickly install citation management tools, bibliography extensions like <a href="https://github.com/pandoc-ext/multibib"><strong>Multibib</strong></a> or <a href="https://github.com/pandoc-ext/section-bibliographies"><strong>Section Bibliographies</strong></a>, and journal-specific formatting, whether they’re using VS Code or Positron for their analysis work.</p></li>
<li><p><strong>Data scientists</strong> gain easy access to computational extensions like <a href="https://github.com/coatless/quarto-webr"><strong>WebR</strong></a>, visualisation tools, and interactive notebook capabilities. This is particularly powerful in Positron, which is designed specifically for data science workflows.</p></li>
<li><p><strong>Technical writers</strong> can browse and install extensions for enhanced typography with for example the <a href="https://github.com/mcanouil/quarto-highlight-text"><strong>Highlight Text</strong></a> extension for multi-format text highlighting, code highlighting, and advanced formatting options of their Reveal.js presentations with <a href="https://github.com/EmilHvitfeldt/quarto-revealjs-editable"><strong>Reveal.js Editable</strong></a> extension in their preferred IDE.</p></li>
<li><p><strong>Workshop instructors</strong> can ensure all participants have consistent extension setups through guided installation processes, regardless of whether attendees prefer VS Code or Positron.</p></li>
</ul>
</section>
<section id="future-ready-architecture" class="level2">
<h2 class="anchored" data-anchor-id="future-ready-architecture">Future-ready architecture</h2>
<p>I’ve built <strong>Quarto Wizard</strong> on robust foundations that ensure long-term reliability. The extension integrates with GitHub’s API through the <a href="https://m.canouil.dev/quarto-extensions/">Quarto Extensions directory</a> for real-time metadata, includes attestation verification for security, and maintains full compatibility with both VS Code and Positron environments.</p>
<p>The modular architecture allows for future enhancements whilst maintaining backwards compatibility. As the Quarto ecosystem continues expanding and as Positron evolves alongside VS Code, <strong>Quarto Wizard</strong> will support new extension types and project management workflows in both environments.</p>
</section>
<section id="getting-started-today" class="level2">
<h2 class="anchored" data-anchor-id="getting-started-today">Getting started today</h2>
<p>Begin your <strong>Quarto Wizard</strong> journey by installing the extension from the VS Code marketplace, the Open VSX Registry, or directly through your IDE’s Extensions view. Once installed, the <strong>Quarto Wizard</strong> icon appears in your Activity Bar, providing immediate access to extension management and project tools.</p>
<p>You have multiple paths to explore Quarto extensions:</p>
<ol type="1">
<li><strong>Through Quarto Wizard</strong>: Click the <strong>Quarto Wizard</strong> icon in your IDE’s Activity Bar and browse the integrated catalogue.</li>
<li><strong>Via the web directory</strong>: Visit <a href="https://m.canouil.dev/quarto-extensions/">m.canouil.dev/quarto-extensions</a> where you can browse all extensions and install them directly from the website. Each extension offers installation buttons for <strong>Quarto Wizard</strong>, VS Code, Positron, VSCodium, or traditional terminal commands.</li>
<li><strong>Traditional command-line</strong>: Use the familiar <code>quarto add</code> commands if you prefer.</li>
</ol>
<div class="highlight">
<p>Ready to transform your <strong>Quarto workflow</strong>?<br>
Install <strong>Quarto Wizard</strong> today and discover how <strong>effortless extension management</strong> can be. Your future self will thank you for making <strong>document creation</strong> this <strong>intuitive</strong> and <strong>powerful</strong>.</p>
</div>
<p>Try installing your first extension: perhaps <a href="https://github.com/mcanouil/quarto-iconify"><strong>Iconify</strong></a> extension for comprehensive icon support or <a href="https://github.com/mcanouil/quarto-github"><strong>GitHub</strong></a> for seamless GitHub linking. Whether you click “Install” on the website, use <strong>Quarto Wizard</strong>’s interface, or type commands in the terminal, the choice is yours. The difference in experience compared to traditional command-line installation is immediately apparent, especially when browsing the visual catalogue with its filtering options, popularity indicators, and rich metadata.</p>
<p>I believe <strong>Quarto Wizard</strong> represents a significant step forward in making Quarto’s powerful publishing capabilities accessible to users regardless of their comfort level with command-line tools or their choice of IDE. By providing intuitive visual interfaces for complex operations, <strong>Quarto Wizard</strong> democratises access to the rich Quarto ecosystem whilst maintaining the flexibility and power that makes Quarto exceptional.</p>
<p>The future of reproducible publishing is here, and it’s more accessible than ever in whichever modern development environment you prefer.</p>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Quarto CLI does not natively track installation sources as of version 1.8.24 (<a href="https://github.com/quarto-dev/quarto-cli/issues/11468">quarto-dev/quarto-cli#11468</a>).↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Wizard} 1.0.0: {Democratising} {Quarto} {Extension}
    {Management}},
  date = {2025-10-20},
  url = {https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-10-20). Quarto Wizard 1.0.0: Democratising Quarto
Extension Management. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/">https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>vscode</category>
  <category>positron</category>
  <category>extensions</category>
  <category>productivity</category>
  <category>quarto-wizard</category>
  <guid>https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/</guid>
  <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-10-20-quarto-wizard-1-0-0/featured.png" medium="image" type="image/png" height="96" width="144"/>
</item>
<item>
  <title>How to use GitHub Codespaces to simplify your Quarto workshops</title>
  <link>https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<p><span style="font-style: italic; opacity: 0.8;">Originally published on <a href="https://quarto.org/docs/blog/posts/2025-05-19-quarto-codespaces/">quarto.org</a>.</span></p>
<section id="what-is-github-codespaces" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="what-is-github-codespaces"><span class="header-section-number">1</span> What Is GitHub Codespaces?</h2>
<p><a href="https://github.com/features/codespaces">GitHub Codespaces</a> is a cloud-powered, on-demand development environment that runs either in your browser or in Visual Studio Code via the <a href="https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces">GitHub Codespaces extension</a>. It eliminates the need for lengthy local setup by providing a fully configured development container, complete with all necessary dependencies and tools. This means that whether you’re an instructor or a developer, you can start coding immediately with a consistent environment tailored to your specific project, right on your web browser.</p>
<p>More importantly, the participants of your workshops can use GitHub Codespaces just as easily as you can. With Codespaces, you and your participants all work on identical environments, minimising the “it doesn’t work on my machine” problems we are all too aware of.</p>
<p>In this post, I am assuming the case of a workshop instructor and a room full of participants with different laptops and operating systems. Codespaces are also useful for development, though, as you will notice reading on.</p>
</section>
<section id="the-power-of-combining-quarto-cli-with-codespaces" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="the-power-of-combining-quarto-cli-with-codespaces"><span class="header-section-number">2</span> The Power of Combining Quarto CLI with Codespaces</h2>
<p>Imagine a world where your participants are instantly equipped with the same environment with all the tools, libraries, and sample projects ready to go in the cloud. That’s the magic of using Codespaces:</p>
<ol type="1">
<li><p><strong>Immediate Onboarding:</strong> Workshop participants or students can bypass the hassle of local setup. They simply launch a Codespace (running on their web browser of choice, independently of operating system), and the pre-configured environment is available immediately.</p></li>
<li><p><strong>A Consistent Environment:</strong> Ensuring that everyone has the same tools and dependencies can be challenging. Codespaces lets you pre-define your environment with container configurations, reducing the risk of discrepancies in software versions or settings.</p></li>
<li><p><strong>Reproducible Workflows:</strong> Whether you’re teaching a data science workshop or collaborating on a research paper, reproducibility is crucial. Because GitHub Codespaces uses the <a href="https://containers.dev/">Dev Container specification</a>, you can ensure that your code can be run in the same environment. When participants are ready to run their projects locally, they can use Codespace to build an equivalent Docker container.</p></li>
</ol>
<p>GitHub provides <a href="https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/setting-up-your-repository/facilitating-quick-creation-and-resumption-of-codespaces">“deep link”</a> to Codespaces, allowing you to create a link you can share with your students or workshop participants.</p>
<p>For example, the <a href="https://github.com/mcanouil/quarto-codespaces"><code>quarto-codespaces</code> repository</a> provides several Dev Container / Codespaces configurations. The following link will create a new Codespace using the <code>.devcontainer/universal/devcontainer.json</code> configuration file instead of the default <code>.devcontainer/devcontainer.json</code> file.</p>
<div style="text-align: center;">
<p><a href="https://codespaces.new/mcanouil/quarto-codespaces?quickstart=1&amp;devcontainer_path=.devcontainer%2Funiversal%2Fdevcontainer.json"><img src="https://github.com/codespaces/badge.svg" class="img-fluid"></a></p>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">[</span><span class="al" style="color: #AD0000;
background-color: null;
font-style: inherit;">![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">](https://codespaces.new/mcanouil/quarto-codespaces?quickstart=1&amp;devcontainer_path=.devcontainer%2Funiversal%2Fdevcontainer.json)</span></span></code></pre></div></div>
<p>The link can include a specific branch, a particular file, or even a specific line in a file, allowing you to guide participants directly to the relevant content and setup. By doing nothing more than clicking that one link, participants create or resume an existing execution environment.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<p><a href="quarto-codespaces-new-001-dark.png" class="lightbox" data-gallery="codespaces-dark"><img src="https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/quarto-codespaces-new-001-dark.png" class="img-fluid" alt="GitHub Codespaces interface showing the 'Create codespace' page. The page includes a section with the repository 'mcanouil/quarto-codespaces' and a message stating 'No codespace to resume'. There are two buttons: 'Change options' and 'Create new codespace'."></a></p>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<p><a href="quarto-codespaces-new-002-dark.png" class="lightbox" data-gallery="codespaces-dark"><img src="https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/quarto-codespaces-new-002-dark.png" class="img-fluid" alt="image_url"></a></p>
</div>
</div>
</div>
</section>
<section id="setting-up-your-own-quarto-codespaces-environment" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="setting-up-your-own-quarto-codespaces-environment"><span class="header-section-number">3</span> Setting Up Your Own Quarto-Codespaces Environment</h2>
<p>If you’re considering using Codespaces with Quarto CLI for your next workshop or teaching module, here’s how to get started:</p>
<p>Leverage the example provided by the <a href="https://github.com/mcanouil/quarto-codespaces"><code>quarto-codespaces</code> repository</a> or <a href="https://github.com/codespaces/new/">create your own Codespaces using the default</a>.</p>
<section id="use-existing-docker-image" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="use-existing-docker-image"><span class="header-section-number">3.1</span> Use an Existing Docker Image</h3>
<p>The easiest way to get started is to use an existing Docker image that has Quarto CLI and all the dependencies you need. There are several pre-built images available with or without the Quarto CLI:</p>
<ul>
<li>Official Docker images:
<ul>
<li><a href="https://github.com/quarto-dev/quarto-cli/pkgs/container/quarto"><code>ghcr.io/quarto-dev/quarto</code></a></li>
<li><a href="https://github.com/quarto-dev/quarto-cli/pkgs/container/quarto-full"><code>ghcr.io/quarto-dev/quarto-full</code></a></li>
</ul></li>
<li>Community Docker images:
<ul>
<li><a href="https://github.com/mcanouil/quarto-codespaces/pkgs/container/quarto-codespaces"><code>ghcr.io/mcanouil/quarto-codespaces</code></a></li>
</ul></li>
<li>Default Codespaces image:
<ul>
<li><a href="https://github.com/devcontainers/images/tree/main/src/universal"><code>mcr.microsoft.com/devcontainers/universal</code></a></li>
</ul></li>
</ul>
<p>You can use any of these images as a base for your Codespace. Inside the <code>.devcontainer/devcontainer.json</code> file, you can specify the image you want to use. The <code>.devcontainer/devcontainer.json</code> file serves as the blueprint for your Codespace.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-2" data-filename="json" style="background: #f1f3f5;"><pre class="sourceCode json cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"name"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Workshop Setup"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-2-3" class="code-annotation-target">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"image"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ghcr.io/mcanouil/quarto-codespaces:latest"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"remoteUser"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"customizations"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-6">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-2-7">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"extensions"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-2-8" class="code-annotation-target">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto.quarto"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-2-9" class="code-annotation-target">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mcanouil.quarto-wizard"</span></span>
<span id="annotated-cell-2-10">      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-2-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-2-12">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"codespaces"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-2" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-2-13" class="code-annotation-target">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"openFiles"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<span id="annotated-cell-2-14">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"exercises/intro-to-quarto.qmd"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-2-15">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"exercises/computation.qmd"</span></span>
<span id="annotated-cell-2-16">      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span></span>
<span id="annotated-cell-2-17">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-2-18">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-2-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-2" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="3" data-code-annotation="1">The Docker image is specified in the <code>image</code> field. It’s built using a Dev Container specification that you can find in <a href="https://github.com/mcanouil/quarto-codespaces/tree/main/.github/.devcontainer"><code>.github/.devcontainer</code></a>.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="8" data-code-annotation="2">The <a href="https://github.com/quarto-dev/quarto"><code>quarto</code> extension</a> for Visual Studio Code / Positron to provide support for Quarto documents.</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="9" data-code-annotation="3">The <a href="https://github.com/mcanouil/quarto-wizard"><code>quarto-wizard</code> extension</a> for Visual Studio Code / Positron to provide assistance in managing Quarto extensions</span>
</dd>
<dt data-target-cell="annotated-cell-2" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-2" data-code-lines="13" data-code-annotation="4">The <code>openFiles</code> field specifies the files to open when the Codespace is created. This is useful for guiding participants to the right files or folders. See the <a href="https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/configuring-dev-containers/automatically-opening-files-in-the-codespaces-for-a-repository">Codespaces documentation</a> for more information.</span>
</dd>
</dl>
</section>
<section id="configure-the-development-container" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="configure-the-development-container"><span class="header-section-number">3.2</span> Configure the Development Container</h3>
<p>You can fork the <a href="https://github.com/mcanouil/quarto-codespaces"><code>quarto-codespaces</code> repository</a> as a starting point for your own Codespaces. This repository includes the Dev Container configuration file (<em>i.e.</em>, <code>.devcontainer/devcontainer.json</code>) that instruct Codespaces on how to set up an environment complete with Quarto CLI and other essential tools.</p>
<p>The <code>.devcontainer/devcontainer.json</code> configuration file ensures that every instance of your Codespace is identical, capturing everything from the Quarto CLI version to additional libraries or extensions you might need.</p>
<p>The <a href="https://github.com/mcanouil/quarto-codespaces"><code>quarto-codespaces</code> repository</a> is a great starting point as it provides a prebuilt Docker image with the latest Quarto CLI, Python, R, and Julia installed (<em>i.e.</em>, <a href="https://github.com/mcanouil/quarto-codespaces/pkgs/container/quarto-codespaces"><code>ghcr.io/mcanouil/quarto-codespaces</code></a> using <a href="https://github.com/mcanouil/quarto-codespaces/tree/main/.github/.devcontainer"><code>.github/.devcontainer/devcontainer.json</code></a>).</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="json" style="background: #f1f3f5;"><pre class="sourceCode json cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"name"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Quarto"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-3" class="code-annotation-target">  <span class="er" style="color: #AD0000;
background-color: null;
font-style: inherit;">//</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"image"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"buildpack-deps:jammy-curl"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"build"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-5">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"dockerfile"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"./Dockerfile"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-6">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"context"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"."</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-7">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"args"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-8">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"VARIANT"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"jammy"</span></span>
<span id="annotated-cell-3-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-10">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<span id="annotated-cell-3-11">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"remoteUser"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-3-12" class="code-annotation-target">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"features"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-3-13" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"./quarto-computing-dependencies"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-14">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"rDeps"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"rmarkdown,languageserver,nx10/httpgd@v2.0.3,prompt,lintr"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-15">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"pythonDeps"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"jupyter,papermill"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-16">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"juliaDeps"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"IJulia"</span></span>
<span id="annotated-cell-3-17">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-3-18" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"./uv"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-19">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"version"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"latest"</span></span>
<span id="annotated-cell-3-20">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-3-21" class="code-annotation-target">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"ghcr.io/rocker-org/devcontainer-features/quarto-cli:1"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-22">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"version"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"release"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-23">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"installTinyTex"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"true"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-24">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"installChromium"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"false"</span></span>
<span id="annotated-cell-3-25">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-26">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-3-27" class="code-annotation-target">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"customizations"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-28">    <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-29">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"extensions"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<span id="annotated-cell-3-30">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"quarto.quarto"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-31">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mcanouil.quarto-wizard"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-32">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"REditorSupport.r"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-33">        <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Posit.air-vscode"</span></span>
<span id="annotated-cell-3-34">      <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-35">      <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"settings"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-36">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"r.rterm.option"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">[</span></span>
<span id="annotated-cell-3-37">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--no-save"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-38">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--no-restore-data"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-39">          <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"--quiet"</span></span>
<span id="annotated-cell-3-40">        <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">]</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-41">        <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"[r]"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-3-42">          <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.defaultFormatter"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Posit.air-vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-3-43">          <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"editor.formatOnSave"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span></span>
<span id="annotated-cell-3-44">        <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-45">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-46">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-47">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span>
<span id="annotated-cell-3-48"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="3" data-code-annotation="1">The <code>image</code> field specifies the base image for the container. You can customise this to suit your needs.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="12" data-code-annotation="2">The <code>features</code> section allows you to add additional tools or libraries. See the <a href="https://containers.dev/features/">Dev Container Features available</a> for a comprehensive list of available features.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="13" data-code-annotation="3">The <code>quarto-computing-dependencies</code> feature is a “local” custom feature that installs the computing dependencies: R, Python, and Julia. This is a great way to ensure that your Codespace has everything it needs to run Quarto documents.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="18" data-code-annotation="4">The <code>uv</code> feature installs the <a href="https://docs.astral.sh/uv/"><code>uv</code> tool</a> to manage Python packages and project dependencies.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="21" data-code-annotation="5">The <code>quarto-cli</code> feature installs the Quarto CLI. You can specify the version you want to install, and it will be automatically downloaded and installed in your Codespace. You can see the code for this feature in the source repository: <a href="https://github.com/rocker-org/devcontainer-features/tree/main/src/quarto-cli" class="uri">https://github.com/rocker-org/devcontainer-features/tree/main/src/quarto-cli</a>.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="27" data-code-annotation="6">The <code>customizations</code> section allows you to specify settings and extensions for Visual Studio Code.</span>
</dd>
</dl>
<p>You can also add additional features to the <code>devcontainer.json</code> to suit your needs or start directly using the image as is as shown in Use an Existing Docker Image.</p>
<p>For example, you might want to add additional R packages or Python libraries. You can do this by using the <code>quarto-computing-dependencies</code> custom feature and changing the <code>rDeps</code>, <code>pythonDeps</code>, and <code>juliaDeps</code> fields to include the packages you want to install globally, or by using the <code>postStartCommand</code> field to run a script that installs the packages you need.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="json" style="background: #f1f3f5;"><pre class="sourceCode json cw-auto code-with-copy"><code class="sourceCode json"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="cb2-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"name"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Workshop Setup"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"image"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ghcr.io/mcanouil/quarto-codespaces:latest"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"remoteUser"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb2-5">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"postStartCommand"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"uv venv &amp;&amp; source .venv/bin/activate &amp;&amp; uv pip install -r requirements.txt"</span></span>
<span id="cb2-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span></code></pre></div></div>
<p>You can also use the <code>postStartCommand</code> field to run a script that installs the packages you need.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename="json" style="background: #f1f3f5;"><pre class="sourceCode json cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode json"><span id="annotated-cell-5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span></span>
<span id="annotated-cell-5-2">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"name"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"My Workshop Setup"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-3">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"image"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ghcr.io/mcanouil/quarto-codespaces:latest"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="annotated-cell-5-4">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"remoteUser"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"vscode"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-5" class="code-annotation-target">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">"postStartCommand"</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">:</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bash ./init-env.sh --what all --force"</span></span>
<span id="annotated-cell-5-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="5" data-code-annotation="1">The <code>postStartCommand</code> field specifies a command to run after the Codespace is started. In this case, it runs the <a href="https://github.com/mcanouil/quarto-codespaces/blob/main/init-env.sh"><code>init-env.sh</code> script</a> from <a href="https://github.com/mcanouil/quarto-codespaces"><code>quarto-codespaces</code> repository</a> with the <code>--what all</code> and <code>--force</code> options.</span>
</dd>
</dl>
</section>
</section>
<section id="benefits-for-workshops-and-teaching" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="benefits-for-workshops-and-teaching"><span class="header-section-number">4</span> Benefits for Workshops and Teaching</h2>
<p>When it comes to educational sessions, consistency and ease-of-use are paramount. Pairing Codespaces with Quarto CLI brings many direct benefits to a teaching environment:</p>
<ul>
<li><strong>Streamlined Onboarding:</strong> Students and workshop attendees can get right to work without spending time installing and configuring local environments.</li>
<li><strong>Live, Interactive Sessions:</strong> Instructors can demonstrate live edits to Quarto documents. Changes can be rendered instantly and reflect in each participant’s environment: perfect for a hands-on, interactive learning experience.</li>
<li><strong>Collaboration and Version Control:</strong> All changes can be recorded in Git, making it easy to track progress, handle peer reviews, and manage collaborative projects, all within a single hosted environment.</li>
<li><strong>Elimination of “Dependency Hell”:</strong> With containerised development, all attendees work from the same baseline, ensuring that version conflicts or missing libraries don’t derail a session.</li>
</ul>
</section>
<section id="final-thoughts-and-future-directions" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="final-thoughts-and-future-directions"><span class="header-section-number">5</span> Final Thoughts and Future Directions</h2>
<p>Whether you’re running a workshop, teaching a class, or collaborating on research, using Codespaces can reduce setup hassles, foster reproducibility, and encourage interactive learning.</p>
<p>In addition to the benefits mentioned above, other features further enhance your experience with Codespaces and Quarto CLI:</p>
<ul>
<li><strong>Automated Pipelines:</strong> Integrating CI/CD tools to automatically validate Quarto document renders and catch errors using the exact same environment. See <a href="https://docs.github.com/en/actions/writing-workflows/choosing-where-your-workflow-runs/running-jobs-in-a-container">GitHub Actions: Running jobs in a container</a></li>
<li><strong>Real-Time Co-Editing Features:</strong> Enhancing collaborative sessions with simultaneous multi-user editing directly in Codespaces. See <a href="https://docs.github.com/en/codespaces/developing-in-a-codespace/working-collaboratively-in-a-codespace">GitHub Codespaces: Real-time collaboration</a></li>
</ul>
<p>This post covered the basics of using Codespaces and Quarto together, but there’s much more to Codespaces. Learn more by <a href="https://docs.github.com/en/codespaces">consulting their documentation</a>.</p>
<p>Happy teaching!</p>
</section>



<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="disclaimer" class="level2 appendix unnumbered"><h2 class="anchored quarto-appendix-heading">Disclaimer</h2><div class="quarto-appendix-contents">

<p>GitHub Codespaces is a product of GitHub, Inc.&nbsp;and comes with a quota of free usage, including CPU hours and storage. Be sure to check the <a href="https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-codespaces/about-billing-for-github-codespaces">GitHub Codespaces billing documentation</a> and your current GitHub plan to avoid unexpected charges. If you are a student or an educator, you can explore the <a href="https://education.github.com/pack">GitHub Education program</a> and <a href="https://docs.github.com/en/education/manage-coursework-with-github-classroom/integrate-github-classroom-with-an-ide/using-github-codespaces-with-github-classroom">GitHub Classroom</a>.</p>
</div></section><section id="acknowledgements" class="level2 appendix unnumbered"><h2 class="anchored quarto-appendix-heading">Acknowledgements</h2><div class="quarto-appendix-contents">

<p>Thanks to <a href="https://github.com/cscheid">Carlos Scheidegger</a>, <a href="https://github.com/juliasilge">Julia Silge</a>, and <a href="https://github.com/coatless">James J. Balamuta</a> for their feedback and suggestions on this post.</p>


</div></section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {How to Use {GitHub} {Codespaces} to Simplify Your {Quarto}
    Workshops},
  date = {2025-05-19},
  url = {https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-05-19). How to use GitHub Codespaces to simplify your
Quarto workshops. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/">https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>github codespaces</category>
  <category>teaching</category>
  <category>dev container</category>
  <guid>https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/</guid>
  <pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-05-19-quarto-codespaces/featured.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>Quarto: Unleash Dynamic Tabset Navigation &amp; Polished PDF Exports</title>
  <link>https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Update - 2025-11-24
</div>
</div>
<div class="callout-body-container callout-body">
<p>The JavaScript solution presented in this post was updated to a Reveal.js plugin format and packaged as a Quarto extension. You can now easily install and use it in your Quarto presentations by following the instructions on the <a href="https://github.com/mcanouil/quarto-revealjs-tabset">GitHub repository</a>.</p>
</div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
<span class="screen-reader-only">Note</span>Update - 2025-11-15
</div>
</div>
<div class="callout-body-container callout-body">
<p>The JavaScript solution presented in this post was updated on November 15, 2025, to enhance compatibility with nested fragments inside tabsets.</p>
<p>It also now supports the native PDF export functionality to export tabsets correctly without needing to use the <code>decktape</code> tool.<br>
This, however, requires to enable the “PDF separate fragments” option from Reveal.js (<em>i.e.,</em> <code>pdf-separate-fragments: true</code>).</p>
</div>
</div>
<section id="introduction" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="introduction"><span class="header-section-number">1</span> Introduction</h2>
<p>Creating presentations with Quarto’s Reveal.js format comes with great flexibility, but navigating tabsets and exporting polished PDFs can be challenging.</p>
<p>In this post, you’ll learn how to streamline these tasks, making your workflow smoother and ensuring your slides are both interactive and well-formatted for sharing. With the right techniques, you can:</p>
<ol type="1">
<li>Craft slides with tabset navigation that flows as naturally as your ideas.</li>
<li>Transform your interactive presentations into sleek, professionally polished PDFs.</li>
</ol>
<p>Let’s dive in and start elevating your slide game!</p>
</section>
<section id="setting-up-the-presentation" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="setting-up-the-presentation"><span class="header-section-number">2</span> Setting Up the Presentation</h2>
<p>Let’s kick off by creating a basic Reveal.js presentation with the essential YAML header.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-startfrom="2" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 1;"><span id="cb1-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A Title"</span></span>
<span id="cb1-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subtitle</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A Subtitle"</span></span>
<span id="cb1-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">author</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Mickaël Canouil, *Ph.D.*"</span></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">institute</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mickael.canouil.fr"</span></span>
<span id="cb1-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> today</span></span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> revealjs</span></span></code></pre></div></div>
</section>
<section id="structuring-your-content" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="structuring-your-content"><span class="header-section-number">3</span> Structuring Your Content</h2>
<p>Break free from linear storytelling by organising your slides into engaging segments. Use the <code>.panel-tabset</code> class in your markdown content to create a dynamic, tabbed interface that keeps your audience on the edge of their seats:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-shortcodes="false" data-startfrom="10" data-filename="markdown" style="background: #f1f3f5;"><pre class="sourceCode markdown cw-auto code-with-copy"><code class="sourceCode markdown" style="counter-reset: source-line 9;"><span id="cb2-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## New slide {.smaller}</span></span>
<span id="cb2-11"></span>
<span id="cb2-12">{{&lt; lipsum 1 &gt;}}</span>
<span id="cb2-13"></span>
<span id="cb2-14"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Tabset {.smaller}</span></span>
<span id="cb2-15"></span>
<span id="cb2-16">::: {.panel-tabset}</span>
<span id="cb2-17"></span>
<span id="cb2-18"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Lipsum</span></span>
<span id="cb2-19"></span>
<span id="cb2-20">{{&lt; lipsum 1-1 &gt;}}</span>
<span id="cb2-21"></span>
<span id="cb2-22"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Placeholder</span></span>
<span id="cb2-23"></span>
<span id="cb2-24">{{&lt; placeholder 600 400 &gt;}}</span>
<span id="cb2-25"></span>
<span id="cb2-26"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Lipsum 2</span></span>
<span id="cb2-27"></span>
<span id="cb2-28">{{&lt; lipsum 2-2 &gt;}}</span>
<span id="cb2-29"></span>
<span id="cb2-30"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">### Placeholder 2</span></span>
<span id="cb2-31"></span>
<span id="cb2-32">{{&lt; placeholder 600 400 &gt;}}</span>
<span id="cb2-33"></span>
<span id="cb2-34">:::</span>
<span id="cb2-35"></span>
<span id="cb2-36"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">## Another slide {.smaller}</span></span>
<span id="cb2-37"></span>
<span id="cb2-38"><span class="al" style="color: #AD0000;
background-color: null;
font-style: inherit;">![An image]({{&lt; placeholder 900 300&gt;}})</span></span></code></pre></div></div>
</section>
<section id="default-navigation-limitations" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="default-navigation-limitations"><span class="header-section-number">4</span> Default Navigation Limitations</h2>
<p>Out of the box, Quarto’s default configuration requires a manual click on every tab—a small, yet frustrating interruption in the flow of your presentation. Imagine having to constantly click through your content when you could be gliding from idea to idea!</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> render assets/_demo-default.qmd</span></code></pre></div></div>
<div style="text-align: center;">
<p><a href="assets/_demo-default.html" class="lightbox" data-gallery="quarto-lightbox-gallery-1" title=""><embed src="assets/_demo-default.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
<section id="enhanced-tabset-navigation" class="level2" data-number="5">
<h2 data-number="5" class="anchored" data-anchor-id="enhanced-tabset-navigation"><span class="header-section-number">5</span> Enhanced Tabset Navigation</h2>
<p>What if you could navigate through your tabs with the fluid grace of arrow keys? Inspired by <a href="https://github.com/EmilHvitfeldt/">Emil Hvitfeldt</a>’s <a href="https://emilhvitfeldt.com/post/slidecraft-fragment-js/#tabset-advance">Slidecraft 101: Fragments - JS</a>, I’ve taken tabset navigation to the next level with a custom JavaScript solution.</p>
<p>Here’s how you can make your presentation navigation as smooth as silk:</p>
<ol type="1">
<li><p><strong>Create a Custom JavaScript stored as an HTML file.</strong><br>
Save<sup>1</sup> the following code snippet in an HTML file. This script leverages Reveal.js’s fragment feature to enable arrow key navigation, letting you effortlessly glide between your tabs as if they were different slides.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-startfrom="25" data-filename="html" style="background: #f1f3f5;"><pre class="sourceCode html cw-auto code-with-copy"><code class="sourceCode html" style="counter-reset: source-line 24;"><span id="cb4-25"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> type=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"text/javascript"</span><span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;"> id</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"panel-tabset-pdf-export"</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span>
<span id="cb4-26">  (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> () {</span>
<span id="cb4-27">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> TAB_SELECTOR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ul.panel-tabset-tabby &gt; li"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-28">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> TAB_LINK_SELECTOR <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ul.panel-tabset-tabby &gt; li a"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-29"></span>
<span id="cb4-30">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getTabPanes</span>(tabset) {</span>
<span id="cb4-31">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabContent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelector</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".tab-content"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-32">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span> tabContent <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span> tabContent<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">children</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> []<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-33">    }</span>
<span id="cb4-34"></span>
<span id="cb4-35">    Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"ready"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> () {</span>
<span id="cb4-36">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabsetSlides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".reveal .slides section .panel-tabset"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-37">      tabsetSlides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (tabset) {</span>
<span id="cb4-38">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(TAB_SELECTOR)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-39">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (tabCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-40"></span>
<span id="cb4-41">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabPanes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getTabPanes</span>(tabset)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-42">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> currentIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-43"></span>
<span id="cb4-44">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> tabCount<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> i<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++</span>) {</span>
<span id="cb4-45">          <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (tabPanes[i]) {</span>
<span id="cb4-46">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> fragmentsInPane <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabPanes[i]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".fragment"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-47">            fragmentsInPane<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (fragment) {</span>
<span id="cb4-48">              fragment<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data-fragment-index"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> currentIndex)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-49">              currentIndex<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++;</span></span>
<span id="cb4-50">            })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-51">          }</span>
<span id="cb4-52"></span>
<span id="cb4-53">          <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> tabCount <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span>) {</span>
<span id="cb4-54">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> fragmentDiv <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">createElement</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"div"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-55">            fragmentDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">className</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"panel-tabset-fragment fragment"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-56">            fragmentDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dataset</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tabIndex</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-57">            fragmentDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"data-fragment-index"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> currentIndex)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-58">            fragmentDiv<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">display</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-59">            tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">parentNode</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">appendChild</span>(fragmentDiv)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-60">            currentIndex<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++;</span></span>
<span id="cb4-61">          }</span>
<span id="cb4-62">        }</span>
<span id="cb4-63">      })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-64">    })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-65"></span>
<span id="cb4-66">    Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fragmentshown"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span>) {</span>
<span id="cb4-67">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fragment</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contains</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"panel-tabset-fragment"</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-68"></span>
<span id="cb4-69">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">parseInt</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fragment</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dataset</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tabIndex</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-70">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getCurrentSlide</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelector</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".panel-tabset"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-71">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>tabset) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-72"></span>
<span id="cb4-73">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabLinks <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(TAB_LINK_SELECTOR)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-74">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (tabLinks[tabIndex]) {</span>
<span id="cb4-75">        tabLinks[tabIndex]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">click</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-76">      }</span>
<span id="cb4-77">    })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-78"></span>
<span id="cb4-79">    Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"fragmenthidden"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span>) {</span>
<span id="cb4-80">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fragment</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contains</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"panel-tabset-fragment"</span>)) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-81"></span>
<span id="cb4-82">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">parseInt</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">event</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fragment</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dataset</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tabIndex</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-83">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getCurrentSlide</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelector</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".panel-tabset"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-84">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>tabset) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-85"></span>
<span id="cb4-86">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabLinks <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(TAB_LINK_SELECTOR)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-87">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> targetIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">?</span> tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-88">      <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (tabLinks[targetIndex]) {</span>
<span id="cb4-89">        tabLinks[targetIndex]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">click</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-90">      }</span>
<span id="cb4-91">    })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-92"></span>
<span id="cb4-93">    Reveal<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pdf-ready"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> () {</span>
<span id="cb4-94">      <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> slides <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".reveal .slides section"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-95">      slides<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (slide) {</span>
<span id="cb4-96">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabset <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelector</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".panel-tabset"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-97">        <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!</span>tabset) <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">return</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-98"></span>
<span id="cb4-99">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> fragments <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> slide<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">".panel-tabset-fragment"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-100">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">let</span> activeTabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-101"></span>
<span id="cb4-102">        fragments<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (fragment) {</span>
<span id="cb4-103">          <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (fragment<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contains</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"visible"</span>)) {</span>
<span id="cb4-104">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="pp" style="color: #AD0000;
background-color: null;
font-style: inherit;">parseInt</span>(fragment<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dataset</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">tabIndex</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">10</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-105">            <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (tabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> activeTabIndex) {</span>
<span id="cb4-106">              activeTabIndex <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabIndex<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-107">            }</span>
<span id="cb4-108">          }</span>
<span id="cb4-109">        })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-110"></span>
<span id="cb4-111">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabLinks <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> tabset<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">querySelectorAll</span>(TAB_LINK_SELECTOR)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-112">        <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> tabPanes <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getTabPanes</span>(tabset)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-113"></span>
<span id="cb4-114">        tabLinks<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> index) {</span>
<span id="cb4-115">          <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">const</span> li <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">parentElement</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-116">          <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> activeTabIndex) {</span>
<span id="cb4-117">            li<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"active"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-118">            link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"aria-selected"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"true"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-119">            link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tabindex"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"0"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-120">          } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<span id="cb4-121">            li<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">remove</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"active"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-122">            link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"aria-selected"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"false"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-123">            link<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">setAttribute</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"tabindex"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"-1"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-124">          }</span>
<span id="cb4-125">        })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-126"></span>
<span id="cb4-127">        <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">Array</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">from</span>(tabPanes)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> (panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> index) {</span>
<span id="cb4-128">          <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (index <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> activeTabIndex) {</span>
<span id="cb4-129">            panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">add</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"active"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-130">            panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">display</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"block"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-131">          } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> {</span>
<span id="cb4-132">            panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">remove</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"active"</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-133">            panel<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">style</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">display</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"none"</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-134">          }</span>
<span id="cb4-135">        })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-136">      })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-137">    })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-138">  })()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb4-139"><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&lt;/</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">script</span><span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">&gt;</span></span></code></pre></div></div></li>
<li><p><strong>Embed the Script in Your YAML Header.</strong><br>
Integrate this new functionality by including the script in your Quarto presentation’s YAML header:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-startfrom="2" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 1;"><span id="cb5-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">title</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A Title"</span></span>
<span id="cb5-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">subtitle</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"A Subtitle"</span></span>
<span id="cb5-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">author</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Mickaël Canouil, *Ph.D.*"</span></span>
<span id="cb5-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">institute</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mickael.canouil.fr"</span></span>
<span id="cb5-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">date</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> today</span></span>
<span id="cb5-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">format</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb5-8"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">revealjs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb5-9"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">include-after-body</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb5-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">file</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> revealjs-tabset.html</span></span></code></pre></div></div></li>
</ol>
<p>Render your updated slides and experience a newfound freedom in navigation:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> render assets/_demo-tabset.qmd</span></code></pre></div></div>
<p>Now, simply use the left and right arrow keys to seamlessly transition between tabs—it’s like your presentation has its own rhythm!</p>
<div style="text-align: center;">
<p><a href="assets/_demo-tabset.html" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title=""><embed src="assets/_demo-tabset.html" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</div>
</section>
<section id="exporting-to-pdf" class="level2" data-number="6">
<h2 data-number="6" class="anchored" data-anchor-id="exporting-to-pdf"><span class="header-section-number">6</span> Exporting to PDF</h2>
<p>To export your presentation to PDF, you can use the <a href="https://github.com/astefanutti/decktape"><code>decktape</code> tool</a>, which is a headless Chrome utility for capturing web pages as PDFs.</p>
<p>Your interactive masterpiece deserves to be preserved. With the <a href="https://github.com/astefanutti/decktape"><code>decktape</code> tool</a>, you can convert your dynamic Reveal.js slides into a beautifully formatted PDF that retains every engaging detail and nuance of your presentation.</p>
<ol type="1">
<li><p>Install <code>decktape</code> using <a href="https://www.npmjs.com/"><code>npm</code></a>:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>bash</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">npm</span> install <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-g</span> decktape</span></code></pre></div></div>
</div></li>
<li><p>Use the following command to generate a PDF from your Reveal.js presentation:</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>bash</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb8-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">npx</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-y</span> decktape reveal <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb8-2">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--chrome-arg</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>--no-sandbox <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb8-3">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--chrome-arg</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>--disable-setuid-sandbox <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb8-4">  <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--fragments</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb8-5">  <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-slides.html"</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"my-slides.pdf"</span></span></code></pre></div></div>
</div></li>
</ol>
<p>This command ensures every fragment and tab is captured in the PDF, preserving the aesthetic and functionality of your presentation.</p>
<div class="quarto-layout-panel" data-layout-ncol="2">
<div class="quarto-layout-row">
<section id="default-quarto-reveal.js" class="level4 unnumbered unlisted quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<h4 class="unnumbered unlisted anchored" data-anchor-id="default-quarto-reveal.js">Default Quarto Reveal.js</h4>
<p><a href="assets/_demo-default.pdf" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title=""><embed src="assets/_demo-default.pdf" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</section>
<section id="tabset-quarto-reveal.js" class="level4 unnumbered unlisted quarto-layout-cell" style="flex-basis: 50.0%;justify-content: center;">
<h4 class="unnumbered unlisted anchored" data-anchor-id="tabset-quarto-reveal.js">Tabset Quarto Reveal.js</h4>
<p><a href="assets/_demo-tabset.pdf" class="lightbox" data-gallery="quarto-lightbox-gallery-4" title=""><embed src="assets/_demo-tabset.pdf" class="hero-art slide-deck img-fluid" loading="lazy"></a></p>
</section>
</div>
</div>
</section>
<section id="conclusion" class="level2" data-number="7">
<h2 data-number="7" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">7</span> Conclusion</h2>
<p>By blending Quarto’s interactive Reveal.js tabset navigation with the crisp sophistication of PDF exports, you’re not merely creating presentations—you’re crafting immersive experiences. Whether delivered live or shared as a document, your slides will captivate and inspire.</p>
<p>And now, instead of wrestling with LaTeX or Typst and losing your hard-earned theming, you can focus entirely on refining your content and delivering a presentation that truly shines.</p>
<p>Elevate your storytelling and let your slides flow!</p>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>Or download the file directly using the link under the table of contents.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2025,
  author = {CANOUIL, Mickaël},
  title = {Quarto: {Unleash} {Dynamic} {Tabset} {Navigation} \&amp;
    {Polished} {PDF} {Exports}},
  date = {2025-04-21},
  url = {https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2025" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2025-04-21). Quarto: Unleash Dynamic Tabset Navigation
&amp; Polished PDF Exports. <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/">https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>reveal.js</category>
  <category>pdf</category>
  <category>presentations</category>
  <category>decktape</category>
  <guid>https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/</guid>
  <pubDate>Mon, 21 Apr 2025 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2025-04-21-quarto-revealjs-tabset-pdf/featured.gif" medium="image" type="image/gif"/>
</item>
<item>
  <title>Quarto: Publishing to GitHub Pages</title>
  <link>https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<p>Publishing Quarto projects to GitHub Pages can seem daunting, but with the right approach, it’s a breeze.<br>
This blog post will walk you through various methods to deploy your Quarto creations, from simple manual steps to automated workflows using GitHub Actions.<br>
By the end, you’ll have the know-how to share your dynamic documents, books, and websites seamlessly with the world.</p>
<p>Let’s dive into the essentials of making your Quarto project live on GitHub Pages.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/featured.png" class="img-fluid quarto-figure quarto-figure-center figure-img" style="width:50.0%" alt="Quarto icon and text logo above GitHub Pages logo."></a></p>
</figure>
</div>
<section id="prerequisites" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="prerequisites"><span class="header-section-number">1</span> Prerequisites</h2>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>Before proceeding, ensure you have enabled and configured GitHub Pages for your repository (<em>i.e.</em>, <code>https://github.com/&lt;username&gt;/&lt;repository&gt;/settings/pages</code>), see <a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site">GitHub Pages documentation</a> for more details.</p>
</div>
</div>
<p>The following methods mostly assume your GitHub repository is public. If your repository is private, you may need to adjust the settings to allow GitHub Pages to access the repository using a personal access token.</p>
<p>You can use the Quarto project created with the <code>quarto create project website</code> command as a demo project to try out the methods described in this guide. This will provide a practical example to follow along with.</p>
</section>
<section id="manual-approach" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="manual-approach"><span class="header-section-number">2</span> Manual Approach</h2>
<section id="using-quarto-cli" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="using-quarto-cli"><span class="header-section-number">2.1</span> Using Quarto CLI</h3>
<p>The manual method involves using the Quarto Command Line Interface (CLI) to publish your project. This method is straightforward and requires minimal setup.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="sh" style="background: #f1f3f5;"><pre class="sourceCode sh cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> publish gh-pages</span></code></pre></div></div>
<p>Check out the Quarto CLI help and <a href="https://quarto.org/docs/publishing/github-pages.html">Quarto’s documentation</a> for more details on the <code>publish</code> command.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="sh" style="background: #f1f3f5;"><pre class="sourceCode sh cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> publish <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--help</span></span></code></pre></div></div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>gh-pages</code> branch is used to publish to GitHub Pages.</p>
<p><code>quarto publish gh-pages</code> setups the <code>gh-pages</code> branch, renders your project, and pushes the output to the branch.<br>
You can use the <code>--no-render</code> flag to skip rendering and only push a previously rendered project.</p>
</div>
</div>
</section>
</section>
<section id="automated-approach" class="level2" data-number="3">
<h2 data-number="3" class="anchored" data-anchor-id="automated-approach"><span class="header-section-number">3</span> Automated Approach</h2>
<p>Automation can streamline the publishing process and reduce the risk of errors. Here are two automated methods using GitHub Actions.</p>
<div class="callout callout-style-default callout-important callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Important
</div>
</div>
<div class="callout-body-container callout-body">
<p>Ensure you have the necessary permissions to create and manage GitHub Actions workflows in your repository (<em>i.e.</em>, <code>https://github.com/&lt;username&gt;/&lt;repository&gt;/settings/actions</code>), see <a href="https://docs.github.com/en/actions">GitHub Actions documentation</a> for more details.</p>
</div>
</div>
<section id="quarto-publish-github-action" class="level3" data-number="3.1">
<h3 data-number="3.1" class="anchored" data-anchor-id="quarto-publish-github-action"><span class="header-section-number">3.1</span> Quarto Publish GitHub Action</h3>
<p>Quarto provides an official GitHub Action that simplifies the publishing process. This action takes care of the entire workflow but <strong>requires</strong> to use <code>quarto publish gh-pages</code> locally at least once to set up the <code>gh-pages</code> branch.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/workflows/deploy.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename=".github/workflows/deploy.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy</span></span>
<span id="cb3-2"></span>
<span id="cb3-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> main</span></span>
<span id="cb3-8"></span>
<span id="cb3-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> read</span></span>
<span id="cb3-11"></span>
<span id="cb3-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">concurrency</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ github.workflow }}</span></span>
<span id="cb3-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cancel-in-progress</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="cb3-15"></span>
<span id="cb3-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="cb3-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb3-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="cb3-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pages</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="cb3-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Checkout repository</span></span>
<span id="cb3-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="cb3-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Install Quarto</span></span>
<span id="cb3-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="cb3-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-28"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pre-release</span></span>
<span id="cb3-29"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      # Add any additional steps as needed, such as installing dependencies</span></span>
<span id="cb3-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Build and Deploy</span></span>
<span id="cb3-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/publish@v2</span></span>
<span id="cb3-32"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb3-33"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">target</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gh-pages</span></span></code></pre></div></div>
</div>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The <code>quarto-dev/quarto-actions</code> GitHub Actions are maintained by the Quarto team.<br>
You can find more information about these actions in the <a href="https://github.com/quarto-dev/quarto-actions">Quarto Actions repository</a>.</p>
</div>
</div>
</section>
<section id="custom-github-actions-workflow" class="level3" data-number="3.2">
<h3 data-number="3.2" class="anchored" data-anchor-id="custom-github-actions-workflow"><span class="header-section-number">3.2</span> Custom GitHub Actions Workflow</h3>
<p>For more control over the publishing process, you can set up a custom GitHub Actions workflow. I’ll discuss two scenarios:</p>
<ul>
<li><p><strong>Deploy from a branch</strong> (<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-from-a-branch">Deploy from a branch - GitHub</a>)<br>
<em>Classic Pages experience, where the content is published from a specific branch root or <code>/docs</code> folder.</em></p></li>
<li><p><strong>GitHub Actions</strong> (<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#publishing-with-a-custom-github-actions-workflow">GitHub Actions - GitHub</a>)<br>
<em>Best for using frameworks and customizing your build process.</em></p></li>
</ul>
<section id="deploy-from-a-branch" class="level4" data-number="3.2.1">
<h4 data-number="3.2.1" class="anchored" data-anchor-id="deploy-from-a-branch"><span class="header-section-number">3.2.1</span> Deploy From a Branch</h4>
<p>You can configure a workflow to publish your Quarto project whenever changes are pushed to a specific branch (<em>e.g.</em>, <code>main</code>) and deploy the output to GitHub Pages from a specific folder/branch.</p>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The GitHub Action workflow described in Quarto Publish GitHub Action uses <code>gh-pages</code> root as the source for GitHub Pages.</p>
</div>
</div>
<section id="deploying-from-the-docs-folder-e.g.-main-branch" class="level5" data-number="3.2.1.1">
<h5 data-number="3.2.1.1" class="anchored" data-anchor-id="deploying-from-the-docs-folder-e.g.-main-branch"><span class="header-section-number">3.2.1.1</span> Deploying From the <code>docs</code> Folder (<em>e.g.</em>, <code>main</code> Branch)</h5>
<p>Using the <code>docs</code> folder as the source for GitHub Pages. Be sure to use a Quarto project (<em>i.e.</em>, <code>_quarto.yml</code>) to be able to set <code>output-dir</code>.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/workflows/deploy.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-4" data-filename=".github/workflows/deploy.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy</span></span>
<span id="annotated-cell-4-2"></span>
<span id="annotated-cell-4-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> main</span></span>
<span id="annotated-cell-4-8"></span>
<span id="annotated-cell-4-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> read</span></span>
<span id="annotated-cell-4-11"></span>
<span id="annotated-cell-4-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">concurrency</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ github.workflow }}</span></span>
<span id="annotated-cell-4-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cancel-in-progress</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="annotated-cell-4-15"></span>
<span id="annotated-cell-4-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="annotated-cell-4-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="annotated-cell-4-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="annotated-cell-4-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Checkout repository</span></span>
<span id="annotated-cell-4-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="annotated-cell-4-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Install Quarto</span></span>
<span id="annotated-cell-4-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="annotated-cell-4-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-4-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pre-release</span></span>
<span id="annotated-cell-4-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      # Add any additional steps as needed, such as installing dependencies</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-4-29" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Build"</span></span>
<span id="annotated-cell-4-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shell</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="annotated-cell-4-31"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">        run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="annotated-cell-4-32">          [ ! -f _quarto.yml ] &amp;&amp; echo -e "project:\n  output-dir: docs" &gt; _quarto.yml</span>
<span id="annotated-cell-4-33">          if grep -q "output-dir: docs" _quarto.yml; then</span>
<span id="annotated-cell-4-34">            quarto render</span>
<span id="annotated-cell-4-35">          else</span>
<span id="annotated-cell-4-36">            quarto render --output-dir docs</span>
<span id="annotated-cell-4-37">          fi</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-4" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-4-38" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Deploy"</span></span>
<span id="annotated-cell-4-39"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shell</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="annotated-cell-4-40"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">        run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="annotated-cell-4-41">          git config --local user.name github-actions[bot]</span>
<span id="annotated-cell-4-42">          git config --local user.email 41898282+github-actions[bot]@users.noreply.github.com</span>
<span id="annotated-cell-4-43">          git add docs || echo "No changes."</span>
<span id="annotated-cell-4-44">          git commit -m "ci: quarto render" || echo "No changes."</span>
<span id="annotated-cell-4-45">          git push origin || echo "No changes."</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-4" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="29" data-code-annotation="1">This step creates a <code>_quarto.yml</code> project file if it doesn’t exist, allowing to set the output directory to <code>_site</code>. If the <code>_quarto.yml</code> file already specifies the output directory as <code>_site</code>, it simply runs <code>quarto render</code> at the project level. Otherwise, it uses the <code>--output-dir</code> option to specify the output directory.</span>
</dd>
<dt data-target-cell="annotated-cell-4" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-4" data-code-lines="38" data-code-annotation="2">This step configures Git to use the <code>github-actions[bot]</code> user for commits and pushes the changes to the <code>main</code> branch. It adds the contents of the <code>docs</code> folder to Git, commits them, and pushes them to the <code>main</code> branch. If there are no changes to commit, it will output “No changes.”</span>
</dd>
</dl>
</section>
<section id="deploying-from-the-root-e.g.-gh-pages-branch" class="level5" data-number="3.2.1.2">
<h5 data-number="3.2.1.2" class="anchored" data-anchor-id="deploying-from-the-root-e.g.-gh-pages-branch"><span class="header-section-number">3.2.1.2</span> Deploying From the Root (<em>e.g.</em>, <code>gh-pages</code> Branch)</h5>
<p>Using the root of the <code>gh-pages</code> branch as the source for GitHub Pages.</p>
<div class="callout callout-style-default callout-tip callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Tip
</div>
</div>
<div class="callout-body-container callout-body">
<p>This method offers an advantage over <code>quarto publish gh-pages</code> as it does not necessitate a local run to create the <code>gh-pages</code> branch beforehand.<br>
Additionally, it provides some insight into the processes that occur behind the scenes when using <code>quarto publish gh-pages</code>, thus when using the Quarto Publish GitHub Action.</p>
</div>
</div>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/workflows/deploy.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename=".github/workflows/deploy.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy</span></span>
<span id="annotated-cell-5-2"></span>
<span id="annotated-cell-5-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> main</span></span>
<span id="annotated-cell-5-8"></span>
<span id="annotated-cell-5-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> read</span></span>
<span id="annotated-cell-5-11"></span>
<span id="annotated-cell-5-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">concurrency</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ github.workflow }}</span></span>
<span id="annotated-cell-5-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cancel-in-progress</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span></span>
<span id="annotated-cell-5-15"></span>
<span id="annotated-cell-5-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="annotated-cell-5-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="annotated-cell-5-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="annotated-cell-5-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Checkout repository</span></span>
<span id="annotated-cell-5-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="annotated-cell-5-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Install Quarto</span></span>
<span id="annotated-cell-5-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="annotated-cell-5-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pre-release</span></span>
<span id="annotated-cell-5-28"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      # Add any additional steps as needed, such as installing dependencies</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-29" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Build"</span></span>
<span id="annotated-cell-5-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shell</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="annotated-cell-5-31"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">        run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="annotated-cell-5-32">          [ ! -f _quarto.yml ] &amp;&amp; echo -e "project:\n  output-dir: _site" &gt; _quarto.yml</span>
<span id="annotated-cell-5-33">          if grep -q "output-dir: _site" _quarto.yml; then</span>
<span id="annotated-cell-5-34">            quarto render</span>
<span id="annotated-cell-5-35">          else</span>
<span id="annotated-cell-5-36">            quarto render --output-dir _site</span>
<span id="annotated-cell-5-37">          fi</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-5-38" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Deploy"</span></span>
<span id="annotated-cell-5-39"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shell</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="annotated-cell-5-40"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">env</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-5-41"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">GH_PAGES</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> gh-pages</span></span>
<span id="annotated-cell-5-42"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">        run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="annotated-cell-5-43">          git config --local user.name github-actions[bot]</span>
<span id="annotated-cell-5-44">          git config --local user.email 41898282+github-actions[bot]@users.noreply.github.com</span>
<span id="annotated-cell-5-45">          BUILD_DIR=$(mktemp -d)</span>
<span id="annotated-cell-5-46">          mv _site "${BUILD_DIR}/quarto-output"</span>
<span id="annotated-cell-5-47">          if git ls-remote --exit-code origin "${GH_PAGES}"; then</span>
<span id="annotated-cell-5-48">            git fetch origin "${GH_PAGES}"</span>
<span id="annotated-cell-5-49">            git checkout origin/"${GH_PAGES}"</span>
<span id="annotated-cell-5-50">          else</span>
<span id="annotated-cell-5-51">            git checkout --orphan "${GH_PAGES}"</span>
<span id="annotated-cell-5-52">            git rm -rf .</span>
<span id="annotated-cell-5-53">          fi</span>
<span id="annotated-cell-5-54">          mv ${BUILD_DIR}/quarto-output/* .</span>
<span id="annotated-cell-5-55">          git add . || echo "No changes."</span>
<span id="annotated-cell-5-56">          git commit --allow-empty -m "ci: quarto render" || echo "No changes."</span>
<span id="annotated-cell-5-57">          git push origin "${GH_PAGES}" || echo "No changes."</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="29" data-code-annotation="1">This step creates a <code>_quarto.yml</code> project file if it doesn’t exist, allowing to set the output directory to <code>_site</code>. If the <code>_quarto.yml</code> file already specifies the output directory as <code>_site</code>, it simply runs <code>quarto render</code> at the project level. Otherwise, it uses the <code>--output-dir</code> option to specify the output directory.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="38" data-code-annotation="2">This step creates a temporary directory to store the build output and moves the <code>_site</code> folder to that directory. It then checks if the <code>gh-pages</code> branch exists, and if so, it fetches and checks it out. If the branch doesn’t exist, it creates a new orphan branch named <code>gh-pages</code>. Finally, it moves the contents of the temporary directory to the root of the repository, adds them to Git, commits them, and pushes them to the <code>gh-pages</code> branch.</span>
</dd>
</dl>
</section>
</section>
<section id="github-actions" class="level4" data-number="3.2.2">
<h4 data-number="3.2.2" class="anchored" data-anchor-id="github-actions"><span class="header-section-number">3.2.2</span> GitHub Actions</h4>
<p>For more sophisticated workflows, you have the option to customise the build process, thus eliminating the need to set up the <code>gh-pages</code> or <code>docs</code> folder. This approach is particularly advantageous as it ensures that the source repository remains uncluttered, maintaining a clear separation between the source code and the build/deploy process and environment.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/workflows/deploy.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename=".github/workflows/deploy.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-annotation-code code-with-copy code-annotated"><code class="sourceCode yaml"><span id="annotated-cell-6-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Deploy</span></span>
<span id="annotated-cell-6-2"></span>
<span id="annotated-cell-6-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">workflow_dispatch</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">push</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">branches</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-7"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> main</span></span>
<span id="annotated-cell-6-8"></span>
<span id="annotated-cell-6-9"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-10"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contents</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> read</span></span>
<span id="annotated-cell-6-11"></span>
<span id="annotated-cell-6-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">concurrency</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">group</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pages"</span></span>
<span id="annotated-cell-6-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">cancel-in-progress</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span></span>
<span id="annotated-cell-6-15"></span>
<span id="annotated-cell-6-16"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">jobs</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-17"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">deploy</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-18"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">runs-on</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ubuntu-latest</span></span>
<span id="annotated-cell-6-19"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">permissions</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-20"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">pages</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="annotated-cell-6-21"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">id-token</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> write</span></span>
<span id="annotated-cell-6-22"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">environment</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-23"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> github-pages</span></span>
<span id="annotated-cell-6-24"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">url</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> ${{ steps.deployment.outputs.page_url }}</span></span>
<span id="annotated-cell-6-25"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">steps</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-26"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Checkout repository</span></span>
<span id="annotated-cell-6-27"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/checkout@v4</span></span>
<span id="annotated-cell-6-28"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> Install Quarto</span></span>
<span id="annotated-cell-6-29"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> quarto-dev/quarto-actions/setup@v2</span></span>
<span id="annotated-cell-6-30"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-31"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> pre-release</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-32" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Build"</span></span>
<span id="annotated-cell-6-33"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">shell</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> bash</span></span>
<span id="annotated-cell-6-34"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">        run</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">: </span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">|</span></span>
<span id="annotated-cell-6-35">          [ ! -f _quarto.yml ] &amp;&amp; echo -e "project:\n  output-dir: _site" &gt; _quarto.yml</span>
<span id="annotated-cell-6-36">          if grep -q "output-dir: _site" _quarto.yml; then</span>
<span id="annotated-cell-6-37">            quarto render</span>
<span id="annotated-cell-6-38">          else</span>
<span id="annotated-cell-6-39">            quarto render --output-dir _site</span>
<span id="annotated-cell-6-40">          fi</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-6-41" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Configure GitHub Pages"</span></span>
<span id="annotated-cell-6-42"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/configure-pages@v5</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-6-43" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Upload Pages Artifact"</span></span>
<span id="annotated-cell-6-44"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/upload-pages-artifact@v3</span></span>
<span id="annotated-cell-6-45"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">with</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="annotated-cell-6-46"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">          </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">path</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"_site"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-6-47" class="code-annotation-target"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">name</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Deploy"</span></span>
<span id="annotated-cell-6-48"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">id</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> deployment</span></span>
<span id="annotated-cell-6-49"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">        </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">uses</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> actions/deploy-pages@v4</span></span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
</div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="32" data-code-annotation="1">This step creates a <code>_quarto.yml</code> project file if it doesn’t exist, allowing to set the output directory to <code>_site</code>. If the <code>_quarto.yml</code> file already specifies the output directory as <code>_site</code>, it simply runs <code>quarto render</code> at the project level. Otherwise, it uses the <code>--output-dir</code> option to specify the output directory.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="41" data-code-annotation="2">This step configures the GitHub Pages deployment environment using the <code>actions/configure-pages</code> action. This action sets up the environment for deploying to GitHub Pages.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="43" data-code-annotation="3">This step uploads the contents of the <code>_site</code> directory as an artifact using the <code>actions/upload-pages-artifact</code> action. This action allows you to upload files to be used in the deployment process.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="47" data-code-annotation="4">This step deploys the uploaded artifact to GitHub Pages using the <code>actions/deploy-pages</code> action. This action handles the deployment process and makes the files available on GitHub Pages.</span>
</dd>
</dl>
<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>The workflow above utilises <code>_site</code> as the output directory, which is the default output directory for Quarto website projects. You can modify the output directory as necessary, provided it aligns with the <code>Upload Pages Artifact</code> step.</p>
</div>
</div>
</section>
</section>
<section id="keeping-your-github-actions-workflow-up-to-date" class="level3" data-number="3.3">
<h3 data-number="3.3" class="anchored" data-anchor-id="keeping-your-github-actions-workflow-up-to-date"><span class="header-section-number">3.3</span> Keeping Your GitHub Actions Workflow Up-to-Date</h3>
<p>GitHub Actions workflows are versioned, and new versions are released periodically. To ensure your workflows are up-to-date, you can use Dependabot to automatically create pull requests when new versions of your dependencies are available.</p>
<div class="code-with-filename">
<div class="code-with-filename-file">
<pre><strong>.github/dependabot.yml</strong></pre>
</div>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-filename=".github/dependabot.yml" style="background: #f1f3f5;"><pre class="sourceCode yaml code-with-copy"><code class="sourceCode yaml"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">version</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span></span>
<span id="cb4-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">updates</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb4-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">-</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">package-ecosystem</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"github-actions"</span></span>
<span id="cb4-4"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">directory</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"/"</span></span>
<span id="cb4-5"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">schedule</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb4-6"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">      </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">interval</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"weekly"</span></span></code></pre></div></div>
</div>
<p>This configuration file instructs Dependabot to check for updates to GitHub Actions workflows weekly.</p>
<p>For more information on Dependabot, see the <a href="https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuring-dependabot-version-updates">Dependabot documentation</a>.</p>
</section>
</section>
<section id="conclusion" class="level2" data-number="4">
<h2 data-number="4" class="anchored" data-anchor-id="conclusion"><span class="header-section-number">4</span> Conclusion</h2>
<p>By following these methods, you’ll be able to publish your Quarto projects to GitHub Pages efficiently.<br>
Choose the approach that best suits your needs and workflow.</p>
<p>Happy publishing!</p>


</section>

<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2024,
  author = {CANOUIL, Mickaël},
  title = {Quarto: {Publishing} to {GitHub} {Pages}},
  date = {2024-12-30},
  url = {https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2024" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2024-12-30). Quarto: Publishing to GitHub Pages.
<em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/">https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>github pages</category>
  <category>publishing</category>
  <category>deployment</category>
  <category>github actions</category>
  <guid>https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/</guid>
  <pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2024-12-30-quarto-github-pages/featured.png" medium="image" type="image/png" height="81" width="144"/>
</item>
<item>
  <title>Quarto Q&amp;A: How to have images for both light and dark theme?</title>
  <link>https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<div class="callout callout-style-default callout-note callout-titled">
<div class="callout-header d-flex align-content-center">
<div class="callout-icon-container">
<i class="callout-icon"></i>
</div>
<div class="callout-title-container flex-fill">
Note
</div>
</div>
<div class="callout-body-container callout-body">
<p>Quarto (&gt;= 1.7) supports computing light and dark mode images natively using the <code>renderings</code> option, see <a href="https://quarto.org/docs/computations/execution-options.html#cell-renderings" class="uri">https://quarto.org/docs/computations/execution-options.html#cell-renderings</a> and <a href="https://quarto.org/docs/output-formats/html-themes.html#mode-specific-content" class="uri">https://quarto.org/docs/output-formats/html-themes.html#mode-specific-content</a> for more details.</p>
</div>
</div>
<p>A new blog post of the “Quarto Q&amp;A” series.<br>
This time, I will show how to have images both for light and dark theme, when switching between them.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.gif" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/featured.gif" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Animated GIF showing a ggplot2 figure switching from light to dark on theme toggle switch."></a></p>
</figure>
</div>
<!-- gifski --fps 10 --height 1080 --width 1920 -o features.gif assets/light-dark.mov -->
<section id="the-questionproblem" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="the-questionproblem"><span class="header-section-number">1</span> The Question/Problem</h2>
<p>When you build a website with Quarto, you can use the <code>theme</code> option to specify the theme you want to use for <code>light</code> and <code>dark</code> mode<sup>1</sup>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="yml" style="background: #f1f3f5;"><pre class="sourceCode yml cw-auto code-with-copy"><code class="sourceCode yaml"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span></span>
<span id="cb1-2"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">light</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> united</span></span>
<span id="cb1-3"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dark</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> slate</span></span></code></pre></div></div>
<p>However, if you want to have images for both themes, you need to have two versions of the same image, one for <code>light</code> and one for <code>dark</code> mode. But how can you do that and how can you switch between them automatically?</p>
</section>
<section id="the-answersolution" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="the-answersolution"><span class="header-section-number">2</span> The Answer/Solution</h2>
<p>Let’s use <code>knitr</code><sup>2</sup> as the engine to generate the images. For example, you can use <code>svglite</code> to generate SVG images and/or any custom <code>knitr</code> handler.</p>
<ol type="1">
<li><p>Set the <code>dev</code> option to use <code>svglite</code> for <code>light</code> mode and <code>darksvglite</code> for <code>dark</code> mode and <code>fig.ext</code> to set the generated images extensions.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-startfrom="11" data-filename="yaml" style="background: #f1f3f5;"><pre class="sourceCode yaml cw-auto code-with-copy"><code class="sourceCode yaml" style="counter-reset: source-line 10;"><span id="cb2-11"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">knitr</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb2-12"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">  </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">opts_chunk</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb2-13"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">dev</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">svglite</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">,</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> darksvglite</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">]</span></span>
<span id="cb2-14"><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">    </span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">fig.ext</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">:</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> </span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">[</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">.light.svg</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">,</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;"> .dark.svg</span><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">]</span></span></code></pre></div></div></li>
<li><p>Create <code>ggplot2</code> themes for <code>light</code> and <code>dark</code> images.</p>
<div class="tabset-margin-container"></div><div class="panel-tabset">
<ul class="nav nav-tabs"><li class="nav-item"><a class="nav-link active" id="tabset-1-1-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-1" aria-controls="tabset-1-1" aria-selected="true" href="">Light</a></li><li class="nav-item"><a class="nav-link" id="tabset-1-2-tab" data-bs-toggle="tab" data-bs-target="#tabset-1-2" aria-controls="tabset-1-2" aria-selected="false" href="">Dark</a></li></ul>
<div class="tab-content">
<div id="tabset-1-1" class="tab-pane active" aria-labelledby="tabset-1-1-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-startfrom="23" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r" style="counter-reset: source-line 22;"><span id="cb3-23">theme_light <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb3-24">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%+%</span></span>
<span id="cb3-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(</span>
<span id="cb3-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.border =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb3-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb3-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb3-29">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb3-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb3-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb3-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb3-33">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rect =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb3-34">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot.background =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>),</span>
<span id="cb3-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.line =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb3-36">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.ticks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>)</span>
<span id="cb3-37">  )</span>
<span id="cb3-38">}</span></code></pre></div></div>
</div>
<div id="tabset-1-2" class="tab-pane" aria-labelledby="tabset-1-2-tab">
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" data-startfrom="40" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r" style="counter-reset: source-line 39;"><span id="cb4-40">theme_dark <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb4-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%+%</span></span>
<span id="cb4-42">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(</span>
<span id="cb4-43">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.border =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb4-44">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb4-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb4-46">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb4-47">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb4-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb4-49">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb4-50">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rect =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>),</span>
<span id="cb4-51">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot.background =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>),</span>
<span id="cb4-52">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.line =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb4-53">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.ticks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>)</span>
<span id="cb4-54">  )</span>
<span id="cb4-55">}</span></code></pre></div></div>
</div>
</div>
</div></li>
<li><p>Create a new function to save the <code>dark</code> images.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" data-startfrom="57" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r" style="counter-reset: source-line 56;"><span id="cb5-57">darksvglite <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(file, width, height) {</span>
<span id="cb5-58">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on.exit</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reset_theme_settings</span>())</span>
<span id="cb5-59">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_dark</span>())</span>
<span id="cb5-60">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggsave</span>(</span>
<span id="cb5-61">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">filename =</span> file,</span>
<span id="cb5-62">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> width,</span>
<span id="cb5-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> height,</span>
<span id="cb5-64">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dev =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"svg"</span>,</span>
<span id="cb5-65">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bg =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"transparent"</span></span>
<span id="cb5-66">  )</span>
<span id="cb5-67">}</span></code></pre></div></div></li>
<li><p>The <code>ggplot2</code> code to build an image.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" data-startfrom="75" data-filename="r" style="background: #f1f3f5;"><pre class="sourceCode r cw-auto code-with-copy"><code class="sourceCode r" style="counter-reset: source-line 74;"><span id="cb6-75"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb6-76"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(palmerpenguins)</span>
<span id="cb6-77"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_light</span>())</span>
<span id="cb6-78"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> penguins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-79">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(</span>
<span id="cb6-80">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> flipper_length_mm,</span>
<span id="cb6-81">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> body_mass_g <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e3</span>,</span>
<span id="cb6-82">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> species,</span>
<span id="cb6-83">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shape =</span> species</span>
<span id="cb6-84">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-85">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_point</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-86">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_colour_manual</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"darkorange"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"purple"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cyan4"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb6-87">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb6-88">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span>,</span>
<span id="cb6-89">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (kg)"</span>,</span>
<span id="cb6-90">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Penguin Species"</span>,</span>
<span id="cb6-91">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shape =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Penguin Species"</span></span>
<span id="cb6-92">  )</span></code></pre></div></div></li>
<li><p>The JavaScript code to switch between the two images (<em>i.e.</em>, <code>.light.svg</code> and <code>.dark.svg</code>)<sup>3</sup>.</p>
<details>
<summary>
<p>JavaScript code</p>
</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" data-filename="javascript" style="background: #f1f3f5;"><pre class="sourceCode javascript cw-auto code-with-copy"><code class="sourceCode javascript"><span id="cb7-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Author: Mickaël Canouil</span></span>
<span id="cb7-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Version: &lt;1.0.0&gt;</span></span>
<span id="cb7-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// Description: Change image src depending on body class (quarto-light or quarto-dark)</span></span>
<span id="cb7-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">// License: MIT</span></span>
<span id="cb7-5"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">updateImageSrc</span>() {</span>
<span id="cb7-6">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> bodyClass <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">window</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">classList</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-7">  <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> images <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">window</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">getElementsByTagName</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'img'</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-8">  <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">for</span> (<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> i <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> images<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">length</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span> i<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++</span>) {</span>
<span id="cb7-9">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> image <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> images[i]<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-10">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> src <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> image<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">src</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-11">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> newSrc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> src<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-12">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (bodyClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contains</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quarto-light'</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> src<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">includes</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.dark'</span>)) {</span>
<span id="cb7-13">      newSrc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> src<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">replace</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.dark'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.light'</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-14">    } <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">else</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (bodyClass<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">contains</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'quarto-dark'</span>) <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> src<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">includes</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.light'</span>)) {</span>
<span id="cb7-15">      newSrc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> src<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">replace</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.light'</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'.dark'</span>)<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-16">    }</span>
<span id="cb7-17">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (newSrc <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">!==</span> src) {</span>
<span id="cb7-18">      image<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">src</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> newSrc<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-19">    }</span>
<span id="cb7-20">  }</span>
<span id="cb7-21">}</span>
<span id="cb7-22"></span>
<span id="cb7-23"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">var</span> observer <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">new</span> <span class="bu" style="color: null;
background-color: null;
font-style: inherit;">MutationObserver</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(mutations) {</span>
<span id="cb7-24">  mutations<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">forEach</span>(<span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(mutation) {</span>
<span id="cb7-25">    <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span> (mutation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">type</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'attributes'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&amp;&amp;</span> mutation<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">attributeName</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">===</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'class'</span>) {</span>
<span id="cb7-26">      <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">updateImageSrc</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-27">    }</span>
<span id="cb7-28">  })<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-29">})<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-30"></span>
<span id="cb7-31">observer<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">observe</span>(<span class="bu" style="color: null;
background-color: null;
font-style: inherit;">window</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">document</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">.</span><span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">body</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">,</span> {</span>
<span id="cb7-32">  <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">attributes</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">:</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">true</span></span>
<span id="cb7-33">})<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb7-34"></span>
<span id="cb7-35"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">updateImageSrc</span>()<span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
</details></li>
<li><p>Finally, the whole Quarto document code.</p>
<details>
<summary>
<p>Quarto document code</p>
</summary>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" data-filename="md" style="background: #f1f3f5;"><pre class="sourceCode md cw-auto code-with-copy"><code class="sourceCode markdown"><span id="cb8-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb8-2"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">title:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> "Generated images for dark and light mode"</span></span>
<span id="cb8-3"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">format:</span></span>
<span id="cb8-4"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  html:</span></span>
<span id="cb8-5"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    theme:</span></span>
<span id="cb8-6"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      light: united</span></span>
<span id="cb8-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">      dark: slate</span></span>
<span id="cb8-8"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">execute:</span></span>
<span id="cb8-9"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  echo: false</span></span>
<span id="cb8-10"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  warning: false</span></span>
<span id="cb8-11"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">knitr:</span><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;"> </span></span>
<span id="cb8-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  opts_chunk: </span></span>
<span id="cb8-13"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    dev: [svglite, darksvglite]</span></span>
<span id="cb8-14"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    fig.ext: [.light.svg, .dark.svg]</span></span>
<span id="cb8-15"><span class="an" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">include-after-body:</span></span>
<span id="cb8-16"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">  text: |</span></span>
<span id="cb8-17"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">    &lt;script type="application/javascript" src="light-dark.js"&gt;&lt;/script&gt;</span></span>
<span id="cb8-18"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">---</span></span>
<span id="cb8-19"></span>
<span id="cb8-20"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```{r}</span></span>
<span id="cb8-21"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| include: false</span></span>
<span id="cb8-22"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb8-23">theme_light <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb8-24">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%+%</span></span>
<span id="cb8-25">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(</span>
<span id="cb8-26">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.border =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-27">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-28">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-29">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-30">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-31">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb8-32">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb8-33">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rect =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb8-34">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot.background =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>),</span>
<span id="cb8-35">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.line =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>),</span>
<span id="cb8-36">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.ticks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"black"</span>)</span>
<span id="cb8-37">  )</span>
<span id="cb8-38">}</span>
<span id="cb8-39"></span>
<span id="cb8-40">theme_dark <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>() {</span>
<span id="cb8-41">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_minimal</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">base_size =</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">11</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%+%</span></span>
<span id="cb8-42">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme</span>(</span>
<span id="cb8-43">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.border =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-44">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-45">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.y =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-46">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.major.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-47">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">panel.grid.minor.x =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_blank</span>(),</span>
<span id="cb8-48">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb8-49">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.text =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_text</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb8-50">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">rect =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>),</span>
<span id="cb8-51">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">plot.background =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_rect</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">fill =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"#272b30"</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">NA</span>),</span>
<span id="cb8-52">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.line =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>),</span>
<span id="cb8-53">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">axis.ticks =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">element_line</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"white"</span>)</span>
<span id="cb8-54">  )</span>
<span id="cb8-55">}</span>
<span id="cb8-56"></span>
<span id="cb8-57">darksvglite <span class="ot" style="color: #003B4F;
background-color: null;
font-style: inherit;">&lt;-</span> <span class="cf" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span>(file, width, height) {</span>
<span id="cb8-58">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">on.exit</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">reset_theme_settings</span>())</span>
<span id="cb8-59">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_dark</span>())</span>
<span id="cb8-60">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggsave</span>(</span>
<span id="cb8-61">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">filename =</span> file,</span>
<span id="cb8-62">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">width =</span> width,</span>
<span id="cb8-63">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">height =</span> height,</span>
<span id="cb8-64">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">dev =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"svg"</span>,</span>
<span id="cb8-65">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">bg =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"transparent"</span></span>
<span id="cb8-66">  )</span>
<span id="cb8-67">}</span>
<span id="cb8-68"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```</span></span>
<span id="cb8-69"></span>
<span id="cb8-70"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```{r}</span></span>
<span id="cb8-71"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| column: screen-inset</span></span>
<span id="cb8-72"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| fig-width: !expr 16/2</span></span>
<span id="cb8-73"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| fig-height: !expr 9/2</span></span>
<span id="cb8-74"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#| out-width: 80%</span></span>
<span id="cb8-75"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(ggplot2)</span>
<span id="cb8-76"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">library</span>(palmerpenguins)</span>
<span id="cb8-77"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_set</span>(<span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">theme_light</span>())</span>
<span id="cb8-78"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">ggplot</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">data =</span> penguins) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb8-79">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">aes</span>(</span>
<span id="cb8-80">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> flipper_length_mm,</span>
<span id="cb8-81">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> body_mass_g <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">/</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">1e3</span>,</span>
<span id="cb8-82">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">colour =</span> species,</span>
<span id="cb8-83">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shape =</span> species</span>
<span id="cb8-84">  ) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb8-85">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">geom_point</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">alpha =</span> <span class="fl" style="color: #AD0000;
background-color: null;
font-style: inherit;">0.8</span>, <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">na.rm =</span> <span class="cn" style="color: #8f5902;
background-color: null;
font-style: inherit;">TRUE</span>) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb8-86">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">scale_colour_manual</span>(<span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">values =</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">c</span>(<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"darkorange"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"purple"</span>, <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"cyan4"</span>)) <span class="sc" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span></span>
<span id="cb8-87">  <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">labs</span>(</span>
<span id="cb8-88">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">x =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Flipper Length (mm)"</span>,</span>
<span id="cb8-89">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">y =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Body Mass (kg)"</span>,</span>
<span id="cb8-90">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">color =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Penguin Species"</span>,</span>
<span id="cb8-91">    <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">shape =</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"Penguin Species"</span></span>
<span id="cb8-92">  )</span>
<span id="cb8-93"><span class="in" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">```</span></span></code></pre></div></div>
</details>
<div class="quarto-layout-panel" data-layout-ncol="2" style="text-align:center;">
<div class="quarto-layout-row">
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/light.png" class="lightbox" data-gallery="quarto-lightbox-gallery-2" title="Light mode ON"><img src="https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/assets/light.png" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Scatter plot on a light background with light mode switched on."></a></p>
</figure>
</div>
<figcaption><strong>Light</strong> mode <strong>ON</strong></figcaption>
</figure>
</div>
</div>
<div class="quarto-layout-cell" style="flex-basis: 50.0%;justify-content: flex-start;">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="assets/dark.png" class="lightbox" data-gallery="quarto-lightbox-gallery-3" title="Dark mode ON"><img src="https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/assets/dark.png" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Scatter plot on a dark background width dark mode switched on."></a></p>
</figure>
</div>
<figcaption><strong>Dark</strong> mode <strong>ON</strong></figcaption>
</figure>
</div>
</div>
</div>
</div></li>
</ol>


</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>See <a href="https://quarto.org/docs/output-formats/html-themes.html#dark-mode" class="uri">https://quarto.org/docs/output-formats/html-themes.html#dark-mode</a>.↩︎</p></li>
<li id="fn2"><p>What matters is that you have two different images, one for <code>light</code> and one for <code>dark</code> mode, respectively with <code>.light</code> and <code>.dark</code> in their names.↩︎</p></li>
<li id="fn3"><p>The JavaScript code does not actually look for the images extensions, but for the images names.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2023,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Q\&amp;A:} {How} to Have Images for Both Light and Dark
    Theme?},
  date = {2023-05-30},
  url = {https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2023" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2023-05-30). Quarto Q&amp;A: How to have images for both
light and dark theme? <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/">https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>q&amp;a</category>
  <category>javascript</category>
  <category>theme</category>
  <category>dark mode</category>
  <category>light mode</category>
  <guid>https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/</guid>
  <pubDate>Tue, 30 May 2023 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2023-05-30-quarto-light-dark/featured.gif" medium="image" type="image/gif"/>
</item>
<item>
  <title>Quarto Q&amp;A: How to publish your Quarto content as a Docker container?</title>
  <link>https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/</link>
  <description><![CDATA[ 

<!--
@license MIT
@copyright 2026 Mickaël Canouil
@author Mickaël Canouil
-->
Skip to main content





<p>A new blog post of the “Quarto Q&amp;A” series.<br>
This time, I will show how to publish your Quarto project as a Docker container as light as possible.</p>
<div class="quarto-figure quarto-figure-center">
<figure class="figure">
<p><a href="featured.png" class="lightbox" data-gallery="quarto-lightbox-gallery-1"><img src="https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/featured.png" class="img-fluid quarto-figure quarto-figure-center figure-img" alt="Docker logo with a whale and containers on top of it, and Quarto logo with text inside the whale."></a></p>
</figure>
</div>
<section id="the-questionproblem" class="level2" data-number="1">
<h2 data-number="1" class="anchored" data-anchor-id="the-questionproblem"><span class="header-section-number">1</span> The Question/Problem</h2>
<p>There are different ways to publish a Quarto project, either if it is a document, a book or a website:</p>
<ul>
<li><a href="https://quarto.org/docs/publishing/quarto-pub.html">Quarto Pub</a>,</li>
<li><a href="https://quarto.org/docs/publishing/github-pages.html">GitHub Pages</a>,</li>
<li><a href="https://quarto.org/docs/publishing/rstudio-connect.html">Posit Connect</a>,</li>
<li><a href="https://quarto.org/docs/publishing/netlify.html">Netlify</a>,</li>
<li><a href="https://quarto.org/docs/publishing/confluence.html">Confluence</a>.</li>
</ul>
<p>But, what if you want to publish your Quarto project as a Docker container, as an app?</p>
</section>
<section id="the-answersolution" class="level2" data-number="2">
<h2 data-number="2" class="anchored" data-anchor-id="the-answersolution"><span class="header-section-number">2</span> The Answer/Solution</h2>
<p>Again, there are several ways to do it. I will show you one way to publish your Quarto project as a Docker container, as light as possible. To do so, I will use a Dockerfile with staged builds and a Quarto website project.</p>
<section id="create-a-project" class="level3" data-number="2.1">
<h3 data-number="2.1" class="anchored" data-anchor-id="create-a-project"><span class="header-section-number">2.1</span> Create a project</h3>
<p>First, I create a website using the Quarto CLI and add a code cell to the <code>about.qmd</code> file that uses the <code>palmerpenguins</code> <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> package.<br>
I will publish this Quarto project as a Docker container.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> create-project <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--type</span> website:blog mywebsite</span>
<span id="cb1-2"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">cd</span> mywebsite</span>
<span id="cb1-3"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'```{r}</span></span>
<span id="cb1-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">library(ggplot2)</span></span>
<span id="cb1-5"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">library(palmerpenguins)</span></span>
<span id="cb1-6"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">ggplot(penguins) +</span></span>
<span id="cb1-7"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  aes(x = bill_length_mm, y = bill_depth_mm) +</span></span>
<span id="cb1-8"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  geom_point(aes(colour = species)) +</span></span>
<span id="cb1-9"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">  geom_smooth(method = "lm", se = FALSE)</span></span>
<span id="cb1-10"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">```'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&gt;</span> about.qmd</span></code></pre></div></div>
</section>
<section id="renv" class="level3" data-number="2.2">
<h3 data-number="2.2" class="anchored" data-anchor-id="renv"><span class="header-section-number">2.2</span> <code>renv</code></h3>
<p><em>See <a href="https://rstudio.github.io/renv/" class="uri">https://rstudio.github.io/renv/</a>.</em></p>
<section id="setup-renv" class="level4" data-number="2.2.1">
<h4 data-number="2.2.1" class="anchored" data-anchor-id="setup-renv"><span class="header-section-number">2.2.1</span> Setup <code>renv</code></h4>
<p>I am using <code>renv</code> to record the dependencies of the project<sup>1</sup>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb2-1"><span class="bu" style="color: null;
background-color: null;
font-style: inherit;">echo</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'library(knitr)</span></span>
<span id="cb2-2"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">library(rmarkdown)</span></span>
<span id="cb2-3"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">library(ggplot2)</span></span>
<span id="cb2-4"><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">library(palmerpenguins)'</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;&gt;</span> _dependencies.R</span>
<span id="cb2-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'install.packages("renv")'</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::init()"</span></span>
<span id="cb2-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::hydrate()"</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::snapshot()"</span></span></code></pre></div></div>
</section>
<section id="the-dockerfile" class="level4" data-number="2.2.2">
<h4 data-number="2.2.2" class="anchored" data-anchor-id="the-dockerfile"><span class="header-section-number">2.2.2</span> The Dockerfile</h4>
<p>Here, I am using “build arguments” (<em>i.e.</em>, <code>ARGS</code>) to specify the <a href="https://quarto.org/">Quarto</a>, the <a href="https://github.com/r-lib/rig">rig</a> and the <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> versions.<br>
This allows to change the versions without having to change the Dockerfile itself.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-3" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode bash"><span id="annotated-cell-3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> QUARTO_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.3.340"</span></span>
<span id="annotated-cell-3-2"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-3-3" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">FROM</span> ghcr.io/quarto-dev/quarto:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${QUARTO_VERSION}</span> AS builder</span>
<span id="annotated-cell-3-4"></span>
<span id="annotated-cell-3-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> RIG_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"latest"</span></span>
<span id="annotated-cell-3-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> R_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"release"</span></span>
<span id="annotated-cell-3-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> install-rig.sh /tmp/install-rig.sh</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-3-8" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> bash /tmp/install-rig.sh <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${RIG_VERSION}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-3-9" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> rig add <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${R_VERSION}</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">&amp;&amp;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pak::pkg_install("renv")'</span></span>
<span id="annotated-cell-3-10"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-3-11" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> mywebsite /app</span>
<span id="annotated-cell-3-12"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">WORKDIR</span> /app</span>
<span id="annotated-cell-3-13"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> Rscript <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"renv::restore()"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-3-14" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> quarto render .</span>
<span id="annotated-cell-3-15"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-3-16" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">FROM</span> httpd:alpine</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-3" data-target-annotation="7" onclick="event.preventDefault();">7</a><span id="annotated-cell-3-17" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--from</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>builder /app/_site/ /usr/local/apache2/htdocs/</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-3" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="3" data-code-annotation="1">The base Docker image used as the “<em>builder</em>”, <em>i.e.</em>, the image with all requirements to build your Quarto project. Here it is the Quarto image which is an Ubuntu based image with Quarto pre-installed.<br>
The “<em>builder</em>” stage is used to install the specified <code>rig</code> and <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> version, and to render the website.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="8" data-code-annotation="2">The <code>install-rig.sh</code> script is used to install the <code>rig</code> software.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="9" data-code-annotation="3">Using <code>rig</code>, we add the specified <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> version and install the <code>renv</code> package.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="11" data-code-annotation="4">Copy the Quarto project into the <code>/app</code> directory of the “<em>builder</em>”.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="14" data-code-annotation="5">Restore the <code>lockfile</code> created with <code>pak</code> and render the website with Quarto CLI.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="16" data-code-annotation="6">The second and last stage is based on the <code>httpd:alpine</code> image which is a light image with Apache pre-installed.</span>
</dd>
<dt data-target-cell="annotated-cell-3" data-target-annotation="7">7</dt>
<dd>
<span data-code-cell="annotated-cell-3" data-code-lines="17" data-code-annotation="7">Copy the rendered website from the “<em>builder</em>” to the <code>/usr/local/apache2/htdocs/</code> directory of the second stage image.</span>
</dd>
</dl>
</section>
</section>
<section id="pak" class="level3" data-number="2.3">
<h3 data-number="2.3" class="anchored" data-anchor-id="pak"><span class="header-section-number">2.3</span> <code>pak</code></h3>
<p><em>See <a href="https://pak.r-lib.org/" class="uri">https://pak.r-lib.org/</a>.</em></p>
<section id="setup-pak" class="level4" data-number="2.3.1">
<h4 data-number="2.3.1" class="anchored" data-anchor-id="setup-pak"><span class="header-section-number">2.3.1</span> Setup <code>pak</code></h4>
<p>Alternatively to <code>renv</code>, it’s also possible de record (and restore) the dependencies of a project<sup>2</sup>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-with-copy"><code class="sourceCode bash"><span id="cb3-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'install.packages("pak", repos = sprintf("https://r-lib.github.io/p/pak/stable/%s/%s/%s", .Platform$pkgType, R.Version()$os, R.Version()$arch))'</span></span>
<span id="cb3-2"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">Rscript</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">'pak::lockfile_create(c("knitr", "rmarkdown", "ggplot2", "palmerpenguins"))'</span></span></code></pre></div></div>
</section>
<section id="the-dockerfile-1" class="level4" data-number="2.3.2">
<h4 data-number="2.3.2" class="anchored" data-anchor-id="the-dockerfile-1"><span class="header-section-number">2.3.2</span> The Dockerfile</h4>
<p>Again, I am using “build arguments” (<em>i.e.</em>, <code>ARGS</code>) to specify the <a href="https://quarto.org/">Quarto</a>, the <a href="https://github.com/r-lib/rig">rig</a> and the <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> versions.<br>
This allows to change the versions without having to change the Dockerfile.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-5" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode bash"><span id="annotated-cell-5-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> QUARTO_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"1.3.340"</span></span>
<span id="annotated-cell-5-2"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-5-3" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">FROM</span> ghcr.io/quarto-dev/quarto:<span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${QUARTO_VERSION}</span> AS builder</span>
<span id="annotated-cell-5-4"></span>
<span id="annotated-cell-5-5"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> RIG_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"latest"</span></span>
<span id="annotated-cell-5-6"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">ARG</span> R_VERSION=<span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"release"</span></span>
<span id="annotated-cell-5-7"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> install-rig.sh /tmp/install-rig.sh</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-5-8" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> bash /tmp/install-rig.sh <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${RIG_VERSION}</span><span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-5-9" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> rig add <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">${R_VERSION}</span></span>
<span id="annotated-cell-5-10"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-5-11" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> mywebsite /app</span>
<span id="annotated-cell-5-12"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">WORKDIR</span> /app</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-5-13" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">RUN</span> Rscript <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">-e</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"pak::lockfile_install()"</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">&amp;&amp;</span> <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">quarto</span> render .</span>
<span id="annotated-cell-5-14"></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="6" onclick="event.preventDefault();">6</a><span id="annotated-cell-5-15" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">FROM</span> httpd:alpine</span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-5" data-target-annotation="7" onclick="event.preventDefault();">7</a><span id="annotated-cell-5-16" class="code-annotation-target"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">COPY</span> <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--from</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span>builder /app/_site/ /usr/local/apache2/htdocs/</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-5" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="3" data-code-annotation="1">The base Docker image used as the “<em>builder</em>”, <em>i.e.</em>, the image with all requirements to build your Quarto project. Here it is the Quarto image which is an Ubuntu based image with Quarto pre-installed.<br>
The “<em>builder</em>” stage is used to install the specified <code>rig</code> and <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> version, and to render the website.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="8" data-code-annotation="2">The <code>install-rig.sh</code> script is used to install the <code>rig</code> software.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="9" data-code-annotation="3">Using <code>rig</code>, we add the specified <iconify-icon inline="" icon="fa7-brands:r-project" aria-label="R" title="R"></iconify-icon> version which already includes <code>pak</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="11" data-code-annotation="4">Copy the Quarto project into the <code>/app</code> directory of the “<em>builder</em>”.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="13" data-code-annotation="5">Restore the <code>lockfile</code> created with <code>pak</code> and render the website with Quarto CLI.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="6">6</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="15" data-code-annotation="6">The second and last stage is based on the <code>httpd:alpine</code> image which is a light image with Apache pre-installed.</span>
</dd>
<dt data-target-cell="annotated-cell-5" data-target-annotation="7">7</dt>
<dd>
<span data-code-cell="annotated-cell-5" data-code-lines="16" data-code-annotation="7">Copy the rendered website from the “<em>builder</em>” to the <code>/usr/local/apache2/htdocs/</code> directory of the second stage image.</span>
</dd>
</dl>
</section>
</section>
<section id="build-the-image" class="level3" data-number="2.4">
<h3 data-number="2.4" class="anchored" data-anchor-id="build-the-image"><span class="header-section-number">2.4</span> Build the image</h3>
<p>It’s time to build the image using the Dockerfile and the <a href="https://docs.docker.com/engine/reference/commandline/buildx_build/"><code>docker buildx build</code></a> command.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-6" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode bash"><span id="annotated-cell-6-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> buildx build <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-6-2" class="code-annotation-target">--platform <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux/amd64"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-6-3" class="code-annotation-target">--build-arg QUARTO_VERSION=1.3.340 <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-6-4" class="code-annotation-target">--tag <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"mywebsite:1.0.0"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-6" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-6-5" class="code-annotation-target">--push <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="annotated-cell-6-6">.</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-6" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="2" data-code-annotation="1">The platform to build the image for.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="3" data-code-annotation="2">The Quarto version to use when building the image.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="4" data-code-annotation="3">The tag to use to label the image, <em>i.e.</em>, the name <code>mywebsite</code> and the version <code>1.0.0</code>. This is useful to be able to identify the image later.</span>
</dd>
<dt data-target-cell="annotated-cell-6" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-6" data-code-lines="5" data-code-annotation="4">(Optional) Assuming you have access to a registry (<code>docker login</code>), push the image to the registry, such as Docker Hub.</span>
</dd>
</dl>
</section>
<section id="deploy-a-container" class="level3" data-number="2.5">
<h3 data-number="2.5" class="anchored" data-anchor-id="deploy-a-container"><span class="header-section-number">2.5</span> Deploy a container</h3>
<p>Finally, deploying locally the image as a Docker container using <a href="https://docs.docker.com/engine/reference/commandline/container_run/"><code>docker container run</code></a> and access the website from your browser at <a href="http://localhost:8080" class="uri">http://localhost:8080</a>.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="annotated-cell-7" data-filename="bash" style="background: #f1f3f5;"><pre class="sourceCode bash cw-auto code-annotation-code code-with-copy code-annotated"><code class="sourceCode bash"><span id="annotated-cell-7-1"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">docker</span> container run <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="1" onclick="event.preventDefault();">1</a><span id="annotated-cell-7-2" class="code-annotation-target">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--detach</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="2" onclick="event.preventDefault();">2</a><span id="annotated-cell-7-3" class="code-annotation-target">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--platform</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"linux/amd64"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="3" onclick="event.preventDefault();">3</a><span id="annotated-cell-7-4" class="code-annotation-target">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--name</span> mywebsite <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="4" onclick="event.preventDefault();">4</a><span id="annotated-cell-7-5" class="code-annotation-target">   <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--publish</span> 8080:80 <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<a class="code-annotation-anchor" data-target-cell="annotated-cell-7" data-target-annotation="5" onclick="event.preventDefault();">5</a><span id="annotated-cell-7-6" class="code-annotation-target">   mywebsite:1.0.0</span><div class="code-annotation-gutter-bg"></div><div class="code-annotation-gutter"></div></code></pre></div></div>
<dl class="code-annotation-container-grid">
<dt data-target-cell="annotated-cell-7" data-target-annotation="1">1</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="2" data-code-annotation="1">The container in detached mode, <em>i.e.</em>, the container runs in the background.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="2">2</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="3" data-code-annotation="2">The platform to run the container on, <em>i.e.</em>, same as the platform used to build the image in this case.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="3">3</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="4" data-code-annotation="3">The name of the container for easy identification, <em>i.e.</em>, <code>mywebsite</code>.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="4">4</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="5" data-code-annotation="4">The port mapping, <em>i.e.</em>, we publish the port <code>8080</code> of the host to the port <code>80</code> of the container. This is useful to be able to access the website from the host.</span>
</dd>
<dt data-target-cell="annotated-cell-7" data-target-annotation="5">5</dt>
<dd>
<span data-code-cell="annotated-cell-7" data-code-lines="6" data-code-annotation="5">The image to use to run the container, <em>i.e.</em>, <code>mywebsite:1.0.0</code>.</span>
</dd>
</dl>


</section>
</section>


<a onclick="window.scrollTo(0, 0); return false;" id="quarto-back-to-top"><i class="bi bi-arrow-up"></i> Back to top</a><div id="quarto-appendix" class="default"><section id="footnotes" class="footnotes footnotes-end-of-document"><h2 class="anchored quarto-appendix-heading">Footnotes</h2>

<ol>
<li id="fn1"><p>See <a href="https://rstudio.github.io/renv/articles/renv.html#snapshotting-dependencies"><code>renv</code> documentation</a> for more information on snapshotting dependencies.↩︎</p></li>
<li id="fn2"><p>See <a href="https://pak.r-lib.org/reference/index.html#lock-files"><code>pak</code> documentation</a> for more information on snapshotting dependencies.↩︎</p></li>
</ol>
</section><section class="quarto-appendix-contents" id="quarto-reuse"><h2 class="anchored quarto-appendix-heading">Reuse</h2><div class="quarto-appendix-contents"><div><a rel="license" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en_gb">CC BY-NC-SA 4.0</a></div></div></section><section class="quarto-appendix-contents" id="quarto-citation"><h2 class="anchored quarto-appendix-heading">Citation</h2><div><div class="quarto-appendix-secondary-label">BibTeX citation:</div><pre class="sourceCode code-with-copy quarto-appendix-bibtex"><code class="sourceCode bibtex">@misc{canouil2023,
  author = {CANOUIL, Mickaël},
  title = {Quarto {Q\&amp;A:} {How} to Publish Your {Quarto} Content as a
    {Docker} Container?},
  date = {2023-05-07},
  url = {https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/},
  langid = {en-GB}
}
</code></pre><div class="quarto-appendix-secondary-label">For attribution, please cite this work as:</div><div id="ref-canouil2023" class="csl-entry quarto-appendix-citeas">
CANOUIL, M. (2023-05-07). Quarto Q&amp;A: How to publish your Quarto
content as a Docker container? <em>Mickael.canouil.fr</em>. <a href="https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/">https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/</a>
</div></div></section></div> ]]></description>
  <category>quarto</category>
  <category>q&amp;a</category>
  <category>docker</category>
  <guid>https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/</guid>
  <pubDate>Sun, 07 May 2023 00:00:00 GMT</pubDate>
  <media:content url="https://mickael.canouil.fr/posts/2023-05-07-quarto-docker/featured.png" medium="image" type="image/png" height="81" width="144"/>
</item>
</channel>
</rss>
