Chart.js (charts), KaTeX (math) and Mermaid (diagrams) are self-hosted and only loaded when the respective shortcode appears on the page - no unnecessary JavaScript on pages without them. The shortcodes post introduces them in brief; here we go into depth. Every example has two tabs: Example shows the result, Code the source.
Charts: the basics #
The chart shortcode renders a canvas with
Chart.js. The inner content is the chart’s
config as JSON (type + data); an optional title sets the canvas'
accessible label. Axis, grid and text colours bind to the theme tokens and
re-theme live when you toggle dark mode; series without a colour of their own
are filled from the site’s accent palette.
Line chart #
{
"type": "line",
"data": {
"labels": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
"datasets": [
{ "label": "Desktop", "data": [120, 132, 101, 134, 90, 60, 70], "tension": 0.35 },
{ "label": "Mobile", "data": [220, 182, 191, 234, 210, 150, 160], "tension": 0.35 }
]
}
}
Bar chart #
Two datasets render as grouped bars; a legend is added automatically.
{
"type": "bar",
"data": {
"labels": ["Q1", "Q2", "Q3", "Q4"],
"datasets": [
{ "label": "2025", "data": [42, 55, 48, 61] },
{ "label": "2026", "data": [51, 63, 59, 74] }
]
}
}
Doughnut chart #
For a single series of proportions, give the dataset an explicit
backgroundColor array so each slice gets its own colour.
{
"type": "doughnut",
"data": {
"labels": ["Organic", "Direct", "Social", "Referral"],
"datasets": [
{ "data": [58, 22, 12, 8], "backgroundColor": ["#ff5d8f", "#f59e0b", "#8b5cf6", "#12a594"] }
]
}
}
Radar chart #
{
"type": "radar",
"data": {
"labels": ["Performance", "Accessibility", "Best Practices", "SEO", "PWA"],
"datasets": [
{ "label": "Before", "data": [72, 88, 80, 90, 40] },
{ "label": "After", "data": [98, 100, 95, 100, 80] }
]
}
}
KaTeX: the basics #
Place the marker shortcode {{< katex >}} on the page once - it loads
the KaTeX renderer. You then write the actual math right in the prose with
$ ... $ (inline) or $$ ... $$ (display); one marker covers the whole page.
Inline: $E = mc^2$ sits in the middle of a sentence without blowing up the line height. In display mode the formula is centred on its own line:
$$ \int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi} $$Inline: $E = mc^2$ in the middle of a sentence.
$$
\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}
$$
Fractions, roots, powers and indices #
$$
\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}
\qquad
\sqrt[3]{x}
\qquad
a_{i,j}^{2}
$$
Sums, products, limits #
$$
\sum_{k=1}^{n} k = \frac{n(n+1)}{2}
\qquad
\prod_{i=1}^{n} i = n!
\qquad
\lim_{x \to 0} \frac{\sin x}{x} = 1
$$
Greek letters and operators #
$$
\alpha,\ \beta,\ \gamma,\ \Delta,\ \Omega \qquad
\nabla \cdot \vec{E} = \frac{\rho}{\varepsilon_0} \qquad
x \in \mathbb{R},\ A \subseteq B
$$
Matrices #
$$
\begin{pmatrix} a & b \\ c & d \end{pmatrix}
\begin{pmatrix} x \\ y \end{pmatrix} =
\begin{pmatrix} ax + by \\ cx + dy \end{pmatrix}
$$
Aligned multi-line and cases #
Use aligned to line up on &, and cases for piecewise definitions:
$$
\begin{aligned}
f(x) &= (x+1)^2 \\
&= x^2 + 2x + 1
\end{aligned}
\qquad
|x| =
\begin{cases}
x, & \text{if } x \ge 0 \\
-x, & \text{otherwise}
\end{cases}
$$
Mermaid: the basics #
You describe the diagram as text between {{< mermaid >}} and
{{< /mermaid >}} - Mermaid renders it to SVG in the browser. The first
line picks the diagram type (graph/flowchart, sequenceDiagram,
classDiagram, …).
graph LR
A[Markdown] --> B{Render hook}
B --> C[WebP]
B --> D[Lightbox]
graph LR
A[Markdown] --> B{Render hook}
B --> C[WebP]
B --> D[Lightbox]
Flow direction and node shapes #
graph/flowchart supports the directions TD (top→down), LR, RL and
BT. The brackets around the node text set its shape: [box], (round),
([stadium]), {diamond}, ((circle)).
flowchart TD
Start([Start]) --> Check{All green?}
Check -->|yes| Deploy[Deploy]
Check -->|no| Fix(Fix errors)
Fix --> Check
Deploy --> Done((Done))
flowchart TD
Start([Start]) --> Check{All green?}
Check -->|yes| Deploy[Deploy]
Check -->|no| Fix(Fix errors)
Fix --> Check
Deploy --> Done((Done))
Sequence diagram #
For interactions between participants - with activation bars and notes.
sequenceDiagram participant V as Visitor participant S as Server V->>S: GET /post/... activate S S-->>V: HTML (pre-rendered) deactivate S Note over V: Apply dark mode (no flash)
sequenceDiagram
participant V as Visitor
participant S as Server
V->>S: GET /post/...
activate S
S-->>V: HTML (pre-rendered)
deactivate S
Note over V: Apply dark mode (no flash)
Class diagram #
classDiagram
class Post {
+string Title
+Date Date
+render() string
}
class Series {
+string Name
}
Series "1" o-- "*" Post : contains
classDiagram
class Post {
+string Title
+Date Date
+render() string
}
class Series {
+string Name
}
Series "1" o-- "*" Post : contains
State diagram #
stateDiagram-v2 [*] --> Draft Draft --> Review: submit Review --> Draft: changes Review --> Published: approve Published --> [*]
stateDiagram-v2
[*] --> Draft
Draft --> Review: submit
Review --> Draft: changes
Review --> Published: approve
Published --> [*]
Gantt chart #
gantt
title Project roadmap
dateFormat YYYY-MM-DD
section Planning
Concept :done, des1, 2026-01-01, 2026-01-07
Draft :active, des2, 2026-01-08, 5d
section Build
Build : des3, after des2, 10d
Test & launch : des4, after des3, 4d
gantt
title Project roadmap
dateFormat YYYY-MM-DD
section Planning
Concept :done, des1, 2026-01-01, 2026-01-07
Draft :active, des2, 2026-01-08, 5d
section Build
Build : des3, after des2, 10d
Test & launch : des4, after des3, 4d
Pie chart #
pie showData title Time per task "Writing" : 45 "Tinkering": 35 "Cleanup" : 20
pie showData
title Time per task
"Writing" : 45
"Tinkering": 35
"Cleanup" : 20
ER diagram #
erDiagram
CATEGORY ||--o{ POST : has
POST }o--o{ TAG : tagged-with
erDiagram
CATEGORY ||--o{ POST : has
POST }o--o{ TAG : tagged-with
Mermaid automatically adopts the light or dark colour scheme and re-renders live when you toggle dark mode - try the switch in the top right while a diagram is visible.
Under the hood #
What both shortcodes take care of in the background - with no extra effort:
- Self-hosted, no CDN. Mermaid, KaTeX (fonts included) and Chart.js are vendored from npm into the theme and served from your own domain - GDPR-friendly, no third-party requests.
- Only when needed. The libraries land only on pages that use the
mermaid,katexorchartshortcode (.HasShortcode). Pages without diagrams, formulas or charts load no extra JavaScript. - CSP-friendly. The init scripts load as regular
<script src>with subresource integrity anddefer, not inline - a strict Content Security Policy withoutunsafe-inlinestays possible. - Live dark mode. A
MutationObserveronhtml.darkre-renders the diagrams and re-themes the charts as soon as the colour scheme changes (Mermaid and Chart.js only theme at render time). - Stable sizes. Mermaid measures text boxes at render time; the init waits
for
document.fonts.readyso diagrams are measured with the loaded font and don’t jump in size between reloads. - Safe. Mermaid runs with
securityLevel: "strict". KaTeX auto-render only processes the prose regions of the page and leaves code blocks untouched - so$signs in examples stay put.



