v0.4.0 is out — data-driven components and integrated graphics. Read the notes
projectious·theme
On this page

Diagrams and Charts

Create responsive diagrams, data-driven charts and generated vector figures.

The theme accepts diagram source and chart data directly in Markdown, from a project file, or from an explicitly supplied build-time URL. Each source is passed to a domain-specific renderer and becomes inline SVG or semantic HTML. That common contract keeps text selectable and lets the page provide responsive, accessible and printable presentation without reducing every domain to one generic drawing language.

Choose an authoring path#

ContentRecommended pathAccepted sourceRendering
Flow, sequence, class or state diagramMermaidFence, shortcode body, file or URLBrowser, only on pages that use it
Small bar, line or dot chartchartCSV fileHugo build
Rich analytical chartplotInline CSV/JSON/TOML/YAML/XML, file or URLBrowser, from build-loaded data
Architecture or infrastructureD2Fence, .d2 file or URLBuild-integrated SVG
Dependencies, trees or directed graphsGraphviz/DOTFence, .dot file or URLBuild-integrated SVG
Mathematical or technical figureTypst graphicsFence, .typ file or URLBuild-integrated SVG
Interactive mathematical constructionJSXGraphInline JSON, file or URLBrowser-generated inline SVG
Digital timing diagramWaveDromInline WaveJSON, file or URLBrowser-generated inline SVG
Chemical structureSMILES DrawerInline SMILES, file or URLBrowser-generated inline SVG
Publication-style algorithmpseudocode.jsInline source, file or URLSemantic HTML and KaTeX
Finished SVG, PNG, WebP or JPEGImagesFileHugo image presentation

Mermaid in Markdown#

A mermaid fence is the fastest route from an idea to a semantic diagram. The theme loads its pinned runtime only when a page uses Mermaid and redraws the diagram after a colour-mode change.

flowchart LR
  M[Markdown] --> H[Hugo]
  H --> S[Search index]
  H --> P[HTML and print]
md
```mermaid
flowchart LR
  M[Markdown] --> H[Hugo]
  H --> P[HTML and print]
```

The paired shortcode {{< mermaid >}}…{{< /mermaid >}} is equivalent. It can also load a page/global asset or fetch a source during the Hugo build. For example, this rendering comes from assets/graphics/request-flow.mmd:

flowchart LR
  B[Browser] --> E[Edge]
  E --> H[Hugo site]
  H --> C[(Content)]
md
{{< mermaid src="graphics/request-flow.mmd" >}}{{< /mermaid >}}

A build-time URL uses the same rendering path:

md
{{< mermaid url="https://example.org/diagrams/request-flow.mmd" >}}{{< /mermaid >}}

Hugo fetches the URL while building; the browser receives the Mermaid source inside the page and does not contact that URL. Prefer a versioned, project-controlled URL. Versions and delivery options are documented under Dependencies, while Mermaid syntax belongs in the Mermaid documentation.

Charts from CSV#

The chart shortcode turns a two-column CSV resource into inline, accessible SVG during the Hugo build. It adds no browser JavaScript and automatically uses the theme’s surfaces, borders, typography and colour-mode tokens.

Projects using generated graphicsBar chart. Q1: 18; Q2: 31; Q3: 46; Q4: 67.017345067Q1: 18Q2: 31Q3: 46Q4: 67Q1Q2Q3Q4QuarterProjects
A bar chart generated from a four-row CSV file.

Place shared data under assets/, or put it beside index.md in a page bundle. The first row is a header; the first column contains labels and the second contains positive numeric values.

csv
Quarter,Projects
Q1,18
Q2,31
Q3,46
Q4,67
md
{{< chart src="data/graphics-adoption.csv" type="bar"
    title="Projects using generated graphics"
    x-label="Quarter" y-label="Projects"
    caption="A bar chart generated from CSV." >}}

The explicit title becomes the SVG’s accessible name, while the caption supplies visible context. Tooltips expose the exact label and value for every mark.

Line charts#

Use a line when ordered values form a meaningful progression.

Growth in projects using generated graphicsLine chart. Q1: 18; Q2: 31; Q3: 46; Q4: 67.017345067Q1: 18Q2: 31Q3: 46Q4: 67Q1Q2Q3Q4QuarterProjects
The same source rendered as a line chart.
md
{{< chart src="data/graphics-adoption.csv" type="line"
    title="Growth in projects using generated graphics"
    x-label="Quarter" y-label="Projects" >}}

Dot charts#

Use dots when the individual measurements matter more than continuity.

Projects using generated graphics by quarterDot chart. Q1: 18; Q2: 31; Q3: 46; Q4: 67.017345067Q1: 18Q2: 31Q3: 46Q4: 67Q1Q2Q3Q4QuarterProjects
The same source rendered as a dot chart.
md
{{< chart src="data/graphics-adoption.csv" type="dot"
    title="Projects using generated graphics by quarter"
    x-label="Quarter" y-label="Projects" >}}

Rich charts with Plot#

The static chart shortcode is ideal for a small, printable series. For broader coverage, plot uses Observable Plot. It adds tooltips, scales, grouping and richer marks while retaining responsive SVG output and the theme’s colour tokens.

Every Plot example accepts the same three build-time data paths:

  • inline CSV, JSON, TOML, YAML or XML in the paired shortcode body;
  • src pointing to assets/ or a page-bundle resource; or
  • url fetched and cached by Hugo, with an explicit format.

Bar and grouped bar#

The bar, line and area examples share assets/data/plot-series.csv:

csv
quarter,projects,target
Q1,18,20
Q2,31,30
Q3,46,44
Q4,67,60
An interactive bar chart loaded from CSV.
md
{{< plot src="data/plot-series.csv" type="bar"
    x="quarter" y="projects" title="Projects by quarter" >}}{{< /plot >}}

Add color="team" when the data contains a grouping column. Plot creates the categorical scale and legend.

Line and area#

Area and line marks emphasize both magnitude and trend.
md
{{< plot src="data/plot-series.csv" type="line"
    x="quarter" y="projects" title="Project adoption" >}}{{< /plot >}}
{{< plot src="data/plot-series.csv" type="area"
    x="quarter" y="projects" title="Project adoption" >}}{{< /plot >}}

Scatter plots#

The scatter, histogram and box-plot examples share assets/data/plot-measurements.csv:

csv
team,latency,throughput
Core,82,510
Core,91,470
Core,76,540
Edge,48,610
Edge,55,590
Edge,43,650
Data,118,390
Data,105,420
Data,127,360
Colour groups the services; position carries both measurements.
md
{{< plot src="data/plot-measurements.csv" type="scatter"
    x="latency" y="throughput" color="team"
    title="Throughput versus latency" >}}{{< /plot >}}

Histograms and distributions#

Plot bins a numeric column and counts observations.
md
{{< plot src="data/plot-measurements.csv" type="histogram"
    x="latency" title="Latency distribution" >}}{{< /plot >}}

Box plots summarize distributions by group:

Median, quartiles and outliers are computed by Plot.
md
{{< plot src="data/plot-measurements.csv" type="box"
    x="team" y="latency" title="Latency range by team" >}}{{< /plot >}}

Heat maps#

The heat map uses assets/data/plot-heatmap.csv:

csv
day,hour,value
Mon,09:00,18
Mon,13:00,32
Mon,17:00,24
Tue,09:00,25
Tue,13:00,41
Tue,17:00,35
Wed,09:00,22
Wed,13:00,37
Wed,17:00,29
Cell colour represents the numeric value column.
md
{{< plot src="data/plot-heatmap.csv" type="heatmap"
    x="hour" y="day" color="value" title="Requests by time slot" >}}{{< /plot >}}

Inline and remote data#

Inline data is useful for a small chart that belongs to one paragraph:

md
{{< plot format="csv" type="bar" x="quarter" y="projects"
    title="Inline project counts" >}}
quarter,projects
Q1,18
Q2,31
Q3,46
{{< /plot >}}

For a build-time URL, Hugo downloads and caches the source before it writes the page; visitors do not make the data request:

md
{{< plot url="https://example.org/metrics.json" format="json"
    type="line" x="date" y="value" title="Daily metric" >}}{{< /plot >}}

CSV and JSON are the most portable choices. YAML, TOML and XML also work.

More Plot charts and configuration#

The theme shortcode currently exposes bar, line, area, scatter, histogram, box and heat-map charts. Plot itself uses composable marks rather than a fixed list of chart types. Its capabilities overview and gallery demonstrate additional possibilities such as stacked and diverging bars, rules, text, links, arrows, vectors, density and contour plots, hexagonal bins, raster plots, maps, trees, clusters and small multiples.

Plot can layer multiple marks and configure axes, grids, legends, colour and opacity scales, projections and facets. It can also derive values with bin, group, stack, normalize, window and map transforms. Consult the upstream marks, scales, transforms, and facets references for the complete option set. The theme keeps its shortcode deliberately smaller so every exposed option has consistent responsive, accessible and colour-mode behavior.

Build-integrated diagram fences#

D2 and Graphviz/DOT are source languages rather than formats understood by Hugo itself. The project build command therefore runs the graphics renderer immediately before Hugo. It discovers fences and referenced source files, hashes their content, invokes only the required external renderer, caches the resulting SVG under _generated/graphics/, and then lets Hugo include that SVG as an ordinary responsive figure.

Authors only write a fence or use diagram with a file or URL; they do not run a separate conversion command or maintain a matching SVG by hand. A cached result also lets builds proceed without the renderer until its source changes. CI should nevertheless pin and install every renderer used by the site so a clean build can reproduce all outputs.

D2 architecture diagrams#

D2 is strongest for architecture, infrastructure, service maps and data flows. It supplies automatic layout, containers, connections, labels and multiple layout engines without manual coordinates.

A compact D2 service architecture.D2 source rendered at build time
md
```d2 {alt="Request processing architecture" caption="Service flow"}
direction: right
client -> edge: HTTPS
edge -> api: authenticated request
api -> db: query
```

Network topology#

This deliberately compact campus example borrows the useful visual grammar of larger operational diagrams: traffic flows top-down, boundaries group the edge, campus zones and branch, device shapes distinguish network equipment from hosts, and labels carry example addresses, VLANs, protocols and link capacity. Redundant paths remain explicit without reproducing every access device. The rendering is loaded from assets/graphics/network-topology.d2 and compiled as part of the Hugo build.

A grouped campus topology with device symbols, example IP addresses, VLANs and annotated links.D2 source rendered at build time
md
{{< diagram renderer="d2" src="graphics/network-topology.d2"
    alt="Internet, campus network zones and a VPN-connected branch"
    caption="A grouped top-down network topology." >}}
graphics/network-topology.d2
direction: down

classes: {
  zone: {
    style: {
      fill: "#f7f9fc"
      stroke: "#7890aa"
      stroke-dash: 4
    }
  }
  network-device: {
    shape: hexagon
    style: {
      fill: "#e7f2ff"
      stroke: "#2f6f9f"
    }
  }
  endpoint: {
    shape: rectangle
    style: {
      fill: "#ffffff"
      stroke: "#567089"
    }
  }
}

internet: Internet {
  shape: cloud
  style.fill: "#dceeff"
}

edge: Internet edge {
  class: zone
  isp: ISP router\n203.0.113.1 {
    class: network-device
  }
  firewall: Perimeter firewall\nWAN 203.0.113.2\nLAN 10.20.0.1 {
    shape: hexagon
    style.fill: "#ffe7dc"
    style.stroke: "#b34f2a"
  }
  isp -> firewall: 1 Gbit/s fibre
}

campus: Main campus · 10.20.0.0/16 {
  class: zone
  core: Core network {
    class: zone
    primary: Core switch A\n10.20.0.2 {
      class: network-device
    }
    standby: Core switch B\n10.20.0.3 {
      class: network-device
    }
    primary <-> standby: redundant 10 Gbit/s trunk
  }
  dmz: DMZ · 10.20.20.0/27 {
    class: zone
    web: Web server\n10.20.20.8 {
      class: endpoint
    }
    mail: Mail server\n10.20.20.9 {
      class: endpoint
    }
  }
  users: User VLANs {
    class: zone
    engineering: Engineering\nVLAN 20 · 10.20.20.0/24 {
      class: network-device
    }
    business: Business\nVLAN 30 · 10.20.30.0/24 {
      class: network-device
    }
    eng-pc: Workstation ENG-01\n10.20.20.41 · DHCP {
      class: endpoint
    }
    bus-pc: Workstation BUS-01\n10.20.30.41 · DHCP {
      class: endpoint
    }
    engineering -> eng-pc: access port
    business -> bus-pc: access port
  }
}

branch: Branch office · 10.40.0.0/16 {
  class: zone
  router: Branch router\n10.40.0.1 {
    class: network-device
  }
  switch: Access switch\n10.40.10.2 {
    class: network-device
  }
  client: Branch workstation\n10.40.10.51 · DHCP {
    class: endpoint
  }
  router -> switch: 1 Gbit/s trunk
  switch -> client: VLAN 10
}

internet -> edge.isp: public uplink
edge.firewall -> campus.core.primary: inside · 10.20.0.0/16
edge.firewall -> campus.core.standby
campus.core.primary -> campus.dmz.web: HTTPS · TCP 443
campus.core.standby -> campus.dmz.mail: SMTP · TCP 25
campus.core.primary -> campus.users.engineering: 802.1Q trunk
campus.core.standby -> campus.users.business: 802.1Q trunk
campus.core.primary -> branch.router: site-to-site VPN\n10.255.0.0/30

Nested provider-style infrastructure#

D2 containers naturally express account, region, VPC, availability-zone and subnet boundaries. Icons from the D2 icon library make services recognizable while the grouping remains ordinary D2. The example deliberately mixes icons with plain labeled resources: an icon supports meaning but does not replace it.

Provider-style AWS grouping with a load balancer, Lambda API and PostgreSQL database.D2 source rendered at build time
md
{{< diagram renderer="d2" src="graphics/aws-nested-infrastructure.d2"
    alt="Nested AWS production infrastructure"
    caption="Provider-style infrastructure grouping." >}}
graphics/aws-nested-infrastructure.d2
direction: right

internet: Internet {
  shape: cloud
}

aws: AWS account {
  icon: https://icons.d2lang.com/aws%2F_Group%20Icons%2FAWS-Cloud-alt_light-bg.svg
  style: {
    stroke: "#d86613"
    font-color: "#8a3b00"
    fill: white
  }

  region: eu-central-1 {
    vpc: Production VPC 10.1.0.0/16 {
      icon: https://icons.d2lang.com/aws%2F_Group%20Icons%2FVirtual-private-cloud-VPC_light-bg.svg
      style: {
        stroke: green
        font-color: green
        fill: white
      }

      az-a: Availability Zone A {
        style: {
          stroke: blue
          font-color: blue
          stroke-dash: 3
          fill: white
        }

        public: Public subnet {
          lb: Application Load Balancer
        }

        private: Private subnet {
          lambda: Lambda API {
            icon: https://icons.d2lang.com/aws/Compute/AWS-Lambda.svg
          }
        }

        data: Database subnet {
          db: Amazon RDS PostgreSQL {
            icon: https://icons.d2lang.com/aws%2FDatabase%2FAmazon-RDS_light-bg.svg
          }
        }
      }
    }
  }
}

internet -> aws.region.vpc.az-a.public.lb: HTTPS
aws.region.vpc.az-a.public.lb -> aws.region.vpc.az-a.private.lambda
aws.region.vpc.az-a.private.lambda -> aws.region.vpc.az-a.data.db

Remote icon URLs are fetched by D2 while rendering. For deterministic offline builds, download the chosen SVG icons into the project, reference their local paths, and commit them with the diagram source. D2 also supports imports and variables, so teams can keep provider styles and reusable infrastructure groups in a small local D2 library rather than copying them into every figure.

Graphviz graphs#

Graphviz is the dependable choice for dependency graphs, trees, finite-state relationships and machine-generated networks. Its DOT language describes nodes and edges; the selected layout engine computes their geometry.

A directed release-state graph rendered by Graphviz.DOT source rendered at build time
md
```dot {alt="Release workflow" caption="Release states"}
digraph release {
  rankdir=LR;
  draft -> review;
  review -> published [label="approve"];
}
```

For a source file instead of a fence, put .d2, .dot or .typ in assets/ or a page bundle and use the same renderer:

md
{{< diagram renderer="d2" src="diagrams/system.d2"
    alt="Request processing architecture" caption="Service flow" >}}

url="https://example.org/system.d2" is also accepted by the shortcode. The pre-renderer fetches it during the build and Hugo fetches the same URL through its resource cache. Prefer a versioned, project-controlled URL; a remote source is deliberately an explicit build dependency and cannot work offline until its SVG is cached.

For precision drawings, mathematical figures and the wider Typst package ecosystem, continue with Typst graphics and CeTZ.

Interactive mathematics with JSXGraph#

JSXGraph renders manipulable geometry, functions, curves, vector fields and other mathematical constructions. The theme accepts a declarative JSON model: named elements can reference elements created earlier, and their attributes are passed to JSXGraph.

Drag A, B or C; JSXGraph recomputes the triangle and circumcircle.
graphics/math-construction.json
{
  "boundingBox": [-1.5, 5, 6, -1.5],
  "axis": true,
  "elements": [
    {"id": "a", "type": "point", "parents": [0, 0], "attributes": {"name": "A", "size": 5, "fillColor": "#ffffff", "strokeColor": "#174a72", "strokeWidth": 3}},
    {"id": "b", "type": "point", "parents": [4, 0], "attributes": {"name": "B", "size": 5, "fillColor": "#ffffff", "strokeColor": "#174a72", "strokeWidth": 3}},
    {"id": "c", "type": "point", "parents": [1.4, 3], "attributes": {"name": "C", "size": 5, "fillColor": "#ffffff", "strokeColor": "#174a72", "strokeWidth": 3}},
    {"type": "polygon", "parents": ["a", "b", "c"], "attributes": {"fillColor": "#70a9d1", "fillOpacity": 0.22, "borders": {"strokeColor": "#174a72", "strokeWidth": 3}}},
    {"type": "circumcircle", "parents": ["a", "b", "c"], "attributes": {"strokeColor": "#d0522a", "strokeWidth": 4, "fillOpacity": 0}}
  ]
}
md
{{< jsxgraph src="graphics/math-construction.json"
    title="Interactive circumcircle construction"
    caption="Drag the triangle vertices." >}}{{< /jsxgraph >}}

The same JSON can be placed directly inside the paired shortcode or loaded with url. Consult the JSXGraph API for element types, parents and attributes; the theme does not duplicate that reference.

Digital timing with WaveDrom#

WaveDrom turns WaveJSON into SVG timing diagrams for clocks, signals, buses and register fields.

Clock, request, payload and acknowledgement signals.
graphics/clock-bus.json5
{ signal: [
  { name: "clk",  wave: "p......." },
  { name: "req",  wave: "01....0." },
  { name: "data", wave: "x.345.x.", data: "header payload crc" },
  { name: "ack",  wave: "0...1.0." }
]}
md
{{< wavedrom src="graphics/clock-bus.json5"
    title="Clock and request bus timing" >}}{{< /wavedrom >}}

Inline WaveJSON and build-time url input use the same shortcode.

Chemical structures from SMILES#

SMILES Drawer converts a compact SMILES description into inline SVG. The textual SMILES value remains in the document as an assistive fallback.

SMILES: CC(=O)OC1=CC=CC=C1C(=O)O
Aspirin rendered from its SMILES representation.
text
CC(=O)OC1=CC=CC=C1C(=O)O
md
{{< smiles value="CC(=O)OC1=CC=CC=C1C(=O)O"
    title="Aspirin structure" >}}

Use src for a checked-in .smi file or url for an explicitly approved build-time source.

Algorithms and pseudocode#

pseudocode.js typesets a LaTeX-like algorithm notation into semantic HTML and delegates formulae to KaTeX. The result remains selectable and responds to the page typography.

\begin{algorithm}
\caption{Binary search}
\begin{algorithmic}
\PROCEDURE{Search}{$A, x$}
  \STATE $low \gets 1$
  \STATE $high \gets \mathrm{length}(A)$
  \WHILE{$low \leq high$}
    \STATE $mid \gets \lfloor(low + high) / 2\rfloor$
    \IF{$A[mid] = x$}
      \RETURN $mid$
    \ELSIF{$A[mid] < x$}
      \STATE $low \gets mid + 1$
    \ELSE
      \STATE $high \gets mid - 1$
    \ENDIF
  \ENDWHILE
  \RETURN not found
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
Binary search typeset from a reusable source file.
graphics/binary-search.pseudo
\begin{algorithm}
\caption{Binary search}
\begin{algorithmic}
\PROCEDURE{Search}{$A, x$}
  \STATE $low \gets 1$
  \STATE $high \gets \mathrm{length}(A)$
  \WHILE{$low \leq high$}
    \STATE $mid \gets \lfloor(low + high) / 2\rfloor$
    \IF{$A[mid] = x$}
      \RETURN $mid$
    \ELSIF{$A[mid] < x$}
      \STATE $low \gets mid + 1$
    \ELSE
      \STATE $high \gets mid - 1$
    \ENDIF
  \ENDWHILE
  \RETURN not found
\ENDPROCEDURE
\end{algorithmic}
\end{algorithm}
md
{{< pseudocode src="graphics/binary-search.pseudo"
    caption="Binary search." >}}{{< /pseudocode >}}

Use an inline paired shortcode for a short algorithm, or src/url for a reusable source. See the upstream grammar for supported algorithm constructs.

Accessibility and visual quality#

  • Describe the conclusion or structure in alt, not merely “chart” or “diagram”. Use an empty alt only for decorative artwork.
  • Use caption for provenance, units, caveats or interpretation that benefits every reader.
  • Do not encode meaning through colour alone. Add labels, shapes or patterns.
  • Prefer a neutral or transparent SVG background. The theme provides the panel, border and dark-mode integration.
  • Keep text large enough to read at the figure’s normal content width and test both screen and print output.
  • Treat renderer source as build input: avoid remote includes and never pass untrusted shortcode strings to shell commands.

Finished SVG and bitmap presentation, including trusted inline SVG and explicit dark variants, is documented under Images.

Edit this page Updated Aug 26, 2026