Stalwart conference slides
A Slidev deck used for technical conference talks about Stalwart.
Two output targets:
- Online deploy (
npm run build) writes to../public/slides/, served by the website athttps://stalw.art/slides/. - Self-contained offline bundle (
npm run build:offline) writes todist-offline/, with relative asset paths and zero external requests (fonts, favicon, scripts all bundled). Zip the directory and openindex.htmlfrom any folder, even on a machine with no internet.
Develop
cd slides
npm install
npm run dev # opens http://localhost:3030
Slidev's dev server hot-reloads the deck on file save. Speaker notes, presenter mode and overview navigation are accessible from the toolbar.
Screenshots live in ../public/images/slides/ (Astro's public folder).
A symlink at slides/public/images points at them so the Slidev dev
server serves them under /images/... while developing.
Build for the live website
npm run build # writes ../public/slides/
The build runs with --base /slides/. Screenshot URLs in the deck are
absolute (/images/slides/...) and are served by Astro from the
website's public/images/slides/ directory after deploy. The slides
ship as part of the Cloudflare Pages deployment alongside the rest of
the site.
The prebuild hook empties ../public/slides/ before each build, so
old hashed bundles do not accumulate. Vite's default emptyOutDir
only kicks in for output paths inside the project root; ours is
outside, hence the explicit clean step.
Build a self-contained offline bundle
npm run build:offline # writes ./dist-offline/
zip -r stalwart-slides.zip dist-offline
To view it on the offline machine, unzip and run:
- macOS / Linux:
./serve.sh - Windows: double-click
serve.bat
then open http://localhost:8000 in a browser. Both helpers use
Python's built-in http.server, which ships with recent macOS, Linux
and Windows. A README.txt next to the index file repeats these
instructions for the recipient.
Why a local web server, and not just index.html? Browsers refuse to
load JavaScript modules from file:// URLs for security reasons (it
would otherwise let any HTML file silently read other files on disk),
so the bundle has to be served over HTTP, even locally.
The offline build runs with --base ./ so every Slidev-managed URL is
relative to index.html. A post-build step
(scripts/finalize-offline.mjs) does
three things:
- Rewrites the absolute screenshot URLs (
"/images/slides/...") into relative ones ("./images/slides/..."). The deck source uses absolute paths so the live site can serve them via Astro at the URL root, but absolute paths would break in the offline bundle. The online build at/slidesis unaffected and continues to use the absolute form. - Writes
serve.shandserve.batnext toindex.html. - Writes a short
README.txtfor the recipient.
The bundle includes:
- All Slidev runtime JS/CSS (no CDN imports).
- Inter and JetBrains Mono fonts via
@fontsource-variable(Slidev'sfonts.provider: nonedisables the Google Fonts CDN that would otherwise be inlined into<head>). - The Stalwart favicon at
dist-offline/favicon.svg. - All slide screenshots in
dist-offline/images/slides/, sourced through the narrow symlink atslides/public/images/slides/so only the deck-relevant captures get bundled (about 19 MB), not the entire website image library.
Open dist-offline/index.html directly in a browser. The deck runs
end-to-end with no internet connection.
Export to PDF
npm run export # writes slides-export.pdf
Wipe build artefacts
npm run clean # removes ../public/slides and dist-offline
Layout
slides.md # the deck source
style.css # Slidev-auto-loaded global styles
styles/tokens.css # website token mirror (brand, surfaces, type)
components/ # custom Vue components (diagrams, frames)
public/favicon.svg # bundled favicon (mirrors website)
public/images -> ../../public/images # symlink for dev-time screenshots