Shortcodes
Math, diagrams, video, presenter
Heavyweight shortcodes that load external libraries — only on pages that use them.
Math, diagrams, and video are expensive — KaTeX is ~150KB, Mermaid is ~700KB. The theme lazy-loads each one only when a page actually uses it, via Page.Store flags read in the footer.
Math (KaTeX) #
{{< math >}}
e^{i\pi} + 1 = 0
{{< /math >}}The shortcode wraps your LaTeX in $$ … $$ (display mode) and flags the page so KaTeX is loaded from CDN in the footer. Pages without a math shortcode pay zero cost.
By default the block is centered; pass align="left" (or right) to change it:
{{< math align="left" >}}
\sum_{i=1}^{n} i = \frac{n(n+1)}{2}
{{< /math >}}For inline math, use raw $ … $ (KaTeX’s auto-render extension picks both up):
The complexity is $O(n \log n)$ — see the proof below.The CDN URL is pinned in layouts/_partials/chrome/footer.html with an SRI hash. Bump the version there when KaTeX releases an update.
Diagrams (Mermaid) #
graph LR A[Forwarder] --> B[Indexer] B --> C[Search Head] C --> D[Dashboard]
Three equivalent ways to author a diagram:
{{< mermaid >}}
graph LR
A[Forwarder] --> B[Indexer]
{{< /mermaid >}}{{% mermaid %}}
graph LR
A[Forwarder] --> B[Indexer]
{{% /mermaid %}}```mermaid
graph LR
A[Forwarder] --> B[Indexer]
```The angle-bracket shortcode ({{< mermaid >}}) is the most predictable form — Hugo passes .Inner through untouched. The percent form ({{% mermaid %}}) lets the inner block participate in markdown so it survives being nested inside other percent-form shortcodes (e.g. {{% notice %}}); the theme strips the blank lines Goldmark would otherwise treat as HTML-block terminators. The fenced ```mermaid block is registered through the theme’s code-block render hook — convenient if your editor highlights mermaid in fenced blocks and you prefer to avoid shortcode syntax altogether.
Mermaid auto-rerenders when you toggle light/dark mode — a MutationObserver on data-theme triggers a re-init. Same lazy-load pattern: only pages using one of the three forms pull the module.
Icons inside diagrams #
Labels can reference any icon in data/icons.toml
using a lucide:NAME token. The theme substitutes the token with inline SVG before mermaid renders, so the diagram ships with vector icons without any external font load.
graph LR A["lucide:download Collector"] --> B["lucide:cpu Processor"] B --> C["lucide:upload Backend"]
{{< mermaid >}}
graph LR
A["lucide:download Collector"] --> B["lucide:cpu Processor"]
B --> C["lucide:upload Backend"]
{{< /mermaid >}}For migrating diagrams that still use Font Awesome syntax, the same substitution accepts fa:fa-NAME and maps a small set of common aliases (fa-download → download, fa-upload → upload, fa-microchip → cpu, fa-route → route). Unknown FA names pass through untouched; the diagram still renders, just without the icon.
Tune the icon size site-wide via:
[params]
mermaidIconSize = 24 # default; pixelsSecurity mode and HTML in labels #
The theme sets mermaid’s securityLevel: "antiscript". This matches relearn’s default and allows <br>, <strong>, <em>, , and similar inline HTML in node labels (useful for line breaks and emphasis inside diagrams) while still blocking <script> tags and click handlers via mermaid’s bundled DOMPurify. If you need a stricter or looser mode, the only place to change it is in layouts/_partials/chrome/footer.html
— there’s no site param for it (mermaid only sets the policy once at init time, so swapping it dynamically per page isn’t useful).
Background and theming #
Mermaid normally paints its own canvas (white in default theme, dark slate in dark) plus a cream-yellow cluster fill (#fff5ad) for subgraphs. The theme overrides all four surface tokens (background, clusterBkg, clusterBorder, secondaryColor, tertiaryColor) to transparent so the diagram sits on the page’s own --color-surface rather than introducing a fifth surface tint. Node and edge colors stay theme-driven by mermaid’s default/dark palette; your classDef overrides in author content still win.
Inline slide decks #
{{< slides >}} embeds a reveal.js
slide deck inline in a workshop page. Authors write markdown inside the shortcode with --- between slides; on click, the deck mounts in a fullscreen overlay over the workshop. Esc closes; arrow keys advance.
{{< slides title="The Splunk data pipeline" >}}
## The pipeline
Splunk takes **raw events**, runs them through a small set of well-named
stages, and gives you back a queryable index.
```text
forwarder → indexer → search head
```
---
## Forwarder
A small agent that watches files or sockets and ships events.
- Runs on the host generating the data
- Tails logs, reads syslog, listens on a TCP port
---
## What you'll build today
Close this deck and read on for the install.
{{< /slides >}}What it renders #
A small brand-coloured “Slide deck · N slides” preview card in normal article flow, with an “Open presentation” button. Click → reveal.js loads from CDN (one-time ~75KB) and the deck mounts fullscreen on top of the workshop. The reader scrolls past the card to reach whatever comes next (an exercise, the next chapter, etc.).
Author conventions #
Separator. A horizontal rule (
---) on its own line splits one slide from the next. The shortcode tolerates blank lines around the separator — both forms work:markdown## Slide one Body… --- ## Slide twoMarkdown inside slides goes through Hugo’s normal markdown pipeline. Code fences with chroma highlighting, images, links, lists, and other shortcodes all work. The theme strips workshop-specific chrome (heading top-borders, code-block file-name bars) inside the slide so the deck reads cleanly on its dark background.
Images.
works straight through — images are auto-sized to fit within 80% width and 60vh height, centred. Hugo’s image render hook handles relative paths for page-bundled images.Alignment. Slide content is left-aligned by default — better for the bullet-list + code-block + paragraph-prose content workshops are made of. Reveal.js’s stock styles centre everything (great for TED talks, wrong for technical decks); the theme overrides that. To centre a specific slide (cover slide, big takeaway, hero quote), use reveal.js’s native class-comment pattern inside the section:
markdown{{< slides >}} ## Default left-aligned slide - Bullet one - Bullet two --- <!-- .slide: class="center" --> ## Big takeaway Centred for impact. {{< /slides >}}The
<!-- .slide: class="..." -->syntax is a reveal.js convention — the comment is parsed off the front of the section and applied as a class on the<section>element. The theme ships a.centerrule; other reveal classes (has-light-background,aligncenter, etc.) work via the standard reveal.js mechanism. Authors can also define their own per-slide classes and target them in site CSS.
Args #
| Arg | Default | Purpose |
|---|---|---|
title | "Presentation" | Heading shown on the preview card. Pipes through markdownify. |
label | i18n slidesOpen (default "Open presentation") | CTA label on the button. |
Presenter-mode gate #
By default the preview card is hidden, so attendees don’t see the deck on their normal scroll-through. It appears only when presenter mode is on — same [data-presenter="true"] toggle as the {{< presenter >}} notes shortcode. Toggle with the floating “Presenter” pill, ?presenter=1 URL param, or P P (press P twice). The intent: the facilitator opens the deck during the workshop; readers consuming the workshop async don’t get distracted by it.
If you want a deck that every reader can launch (e.g. a recorded workshop), you can override the gate with site CSS:
/* In your own site's CSS, after the theme bundle */
.slides-card { display: grid; }Reveal.js + CDN + SRI #
Reveal.js (v6.0.1) is loaded from jsdelivr with Subresource Integrity
hashes pinned in assets/js/slides.js
. The browser refuses to execute the bundle if the CDN ever serves a modified file. Bump REVEAL_VERSION in the same file when upgrading and regenerate the hashes:
curl -sL https://cdn.jsdelivr.net/npm/reveal.js@<v>/dist/reveal.min.js \
| openssl dgst -sha384 -binary | openssl base64 -AThe font (Inter, with the optical-size axis) is loaded from Google Fonts on first open — content there is dynamic, so SRI isn’t practical; the blast-radius of a Google Fonts compromise is limited to glyph rendering.
Why a CDN #
Reveal.js is ~75KB minified plus a theme stylesheet — bundling it into every workshop page (including the 99% that have no deck) would inflate the theme’s JS bundle for no benefit. CDN delivery on first open keeps the theme bundle small AND caches across workshop sites: a reader who’s seen one deck on one Splunk workshop pays nothing on every subsequent deck on every other Splunk workshop.
Embedded video #
{{< youtube id="tBHUyncppng" title="An old internet classic" >}}Uses youtube-nocookie.com for GDPR-friendlier embedding. The title attribute is strongly recommended for accessibility — omit it and the iframe falls back to a generic "YouTube video" label, which screen readers will announce verbatim.
For other video providers (Vimeo, Wistia, etc.), the theme doesn’t ship a shortcode — drop the embed iframe directly into your markdown and Hugo’s Goldmark with unsafe = true will render it.
Presenter notes #
A presenter block is hidden by default and revealed when the user enters presenter mode. A floating “Presenter” pill appears in the bottom-right of any page that has presenter notes; clicking it (or pressing P twice in quick succession, or appending ?presenter=1 to the URL) toggles the mode.
{{< presenter >}}
Start the EC2 instance — boot takes ~3 minutes.
{{< /presenter >}}
{{< presenter title="Timing" >}}
Allow 10 minutes for attendees to finish this section.
{{< /presenter >}}The state persists in localStorage, so once you toggle presenter mode it stays on as you navigate between pages — useful for live workshops where you want notes available throughout.
Don’t put answers in presenter notes
Webex chat simulation #
Simulate a Cisco Webex conversation as the narrative device that opens an exercise — “your manager just pinged you about a customer complaint, here’s what you’d see”. Renders the full Webex chat UI (header, tabs, stacked message stream, “Seen by” indicator, composer footer) as decorative chrome around a sequence of messages you author yourself. Matches the real Webex desktop client’s layout — all messages are left-aligned with avatar + name + time + body; the current-user case (me=true) swaps the initials disc for a chat-bubble glyph and defaults the name to “You”. The chat surface follows the page’s light/dark theme — flip the site theme and the chat flips with it, just like the real Webex client.
{{< webex chat="Bill Grant" date="Today • 28/01/2026" seenby="BG" >}}
{{< webex-msg from="BG" name="Bill Grant" time="09:42" >}}
Hey! We're getting reports of a potential customer satisfaction issue
with the Online Boutique application. Can you do the first triage?
{{< /webex-msg >}}
{{< webex-msg me=true time="09:43" >}}
Sure, I'll start by checking RUM in Splunk Observability. 👍
{{< /webex-msg >}}
{{< /webex >}}Multi-turn conversation with multiple senders #
Each {{< webex-msg >}} is independent, so you can interleave senders freely. Use the color param to give each person their own avatar tint.
Parameters #
{{< webex >}} (parent — chat container):
| Param | Default | Notes |
|---|---|---|
chat | Chat | Name in the header bar (also used as the input placeholder). |
status | Available | Status text under the name. |
date | Today | Centred date divider. Free-form — Today • 28/01/2026, Yesterday, Last Tuesday. |
seenby | — | Initials of the avatar shown under “Seen by”. Omit to hide the indicator. |
seenby-color | Webex green | CSS colour for the seen-by avatar. |
{{< webex-msg >}} (child — one message):
| Param | Default | Notes |
|---|---|---|
from | — | Initials shown in the avatar disc. Ignored when me=true (the chat-bubble glyph replaces it). |
name | — / You | Sender’s full name (above the body). Defaults to You when me=true. |
time | — | Timestamp string (e.g. 09:42, Thursday, 15:35). Free-form, no parsing. |
color | Webex green | Avatar disc background colour. Override per-sender to distinguish multiple voices. Ignored when me=true. |
me | false | When true, the message uses the gray chat-bubble avatar and defaults the name to You — matching how Webex renders the current user’s own messages. |
Message bodies support full markdown — **bold**, *italic*, `code`, links, emoji.
