Skip to content
tbsch Theme
Features

Diagrams and math

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} $$
$$
\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 $$
$$
\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 $$
$$
\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} $$
$$
\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} $$
$$
\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, katex or chart shortcode (.HasShortcode). Pages without diagrams, formulas or charts load no extra JavaScript.
  • CSP-friendly. The init scripts load as regular <script src> with subresource integrity and defer, not inline - a strict Content Security Policy without unsafe-inline stays possible.
  • Live dark mode. A MutationObserver on html.dark re-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.ready so 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.
Join the conversation

Reactions

No reactions yet - be the first.

Read more

Related posts