Zorto #
Content above config above code.
The AI-native static site generator (SSG) with executable code blocks & more.
What is Zorto? #
- Static site generator written in Rust
- Python bindings via PyO3: install with
uvorpip - Markdown content with TOML frontmatter
- Executable code blocks: run Python and Bash at build time
- Built-in search, feeds, and llms.txt
- Data apps: HTML/CSS/JS plus DuckDB data
- MIT licensed, open source forever
Content above config above code #
Content #
- Markdown pages
- Prose, docs, decks
- SQL receipts and notes
- Mostly human-owned
Config #
config.toml- Routes, templates, themes
- Sources, queries, dashboards
- Human or AI-owned
Code handles the machinery: Rust builds, Python orchestrates local data, JavaScript renders browser interfaces.
Small core, wide surface #
Code can grow behind stable boundaries. Content and config stay readable.
AI-native by design #
- Markdown + TOML are easy to diff, review, and edit
- One file per page or slide keeps agent edits scoped
content_dirslets zorto.dev publish repo docs without duplicationzorto checkvalidates links, headings, and structurellms.txtgives agents a compact site map at build time
Core surface #
- Content model: pages, sections, taxonomies, feeds, clean URLs
- Search: built-in client-side index for static hosting
- Code execution: Python and Bash outputs baked into pages
- Data apps: DuckDB-backed HTML/CSS/JS interfaces
- Presentations: one markdown file per slide, native HTML runtime
- AI contracts:
AGENTS.md,llms.txt, andzorto check
Native presentations #
- Markdown source, one file per slide
- TOML frontmatter controls order, layout, theme, and backgrounds
- Shortcodes handle columns, fragments, notes, and media
- Output is static HTML/CSS/JS
- No presentation server required
Executable code blocks #
Write Python in a fenced block: Zorto runs it at build time and embeds the output.
# Stdlib only: this demo runs without extra installs. import statistics revenue = [12, 15, 13, 17, 21, 24] costs = [10, 11, 12, 13, 14, 15] print(f"Mean revenue: {statistics.mean(revenue):.2f}") print(f"Mean costs: {statistics.mean(costs):.2f}") print(f"Margin: {statistics.mean(revenue) - statistics.mean(costs):.2f}")
Mean revenue: 17.00
Mean costs: 12.50
Margin: 4.50
Add plotly, matplotlib, altair, or seaborn to your venv to embed figure output the same way.
Dependencies are explicit #
# website/pyproject.toml [project] dependencies = [ "matplotlib", "plotly", "pandas", "numpy", ] # build_meta.py # /// script # dependencies = [ # "duckdb==1.5.2", ]
- Executable blocks: Zorto activates the site
.venv - Website builds:
uv syncinstalls chart/data deps - Pipelines:
uv run --locked --scriptowns script deps - Distribution:
uv tool install zortoinstalls the Rust-backed CLI
No hidden global Python. No generated dependency soup.
Build-time visualizations #
# Stdlib only: a tiny ASCII sparkline of a damped oscillation. import math x = [i * 0.4 for i in range(40)] y = [math.sin(v) * math.exp(-v * 0.12) for v in x] ramp = " ▁▂▃▄▅▆▇█" lo, hi = min(y), max(y) spark = "".join(ramp[round((v - lo) / (hi - lo) * (len(ramp) - 1))] for v in y) print(f"damped sin(x) * e^(-0.12x), 40 samples") print(spark)
damped sin(x) * e^(-0.12x), 40 samples
▃▅▇██▇▆▅▃▂▁ ▁▁▂▄▅▅▅▅▅▄▄▃▂▂▂▂▂▃▃▄▄▄▄▄▄▄▃
For real charts, install matplotlib, plotly, altair, or seaborn: Zorto captures Figure objects and embeds them as static HTML automatically.
Python deps render artifacts #
import matplotlib.pyplot as plt import pandas as pd df = pd.DataFrame({ "week": range(1, 9), "content": [18, 21, 25, 32, 40, 48, 57, 69], "data apps": [2, 3, 3, 5, 8, 13, 21, 34], }) ax = df.plot(x="week", y=["content", "data apps"], marker="o", linewidth=3, figsize=(7.2, 3.4)) ax.set_title("Generated during the Zorto build") ax.grid(alpha=0.25) ax.legend(frameon=False) print(f"rows: {len(df)}") print("deps: pandas + matplotlib")
rows: 8
deps: pandas + matplotlib
The build runs inside the uv-managed site environment. The resulting figure is baked into static HTML.
Data app shape #
Static-first does not mean static-only. The interface is HTML, CSS, and JavaScript.
The slide can query site.ddb #
Zorto can ship a public DuckDB database beside the site, then let a page query it in the browser.
site.ddb: generated locally- DuckDB-Wasm attaches it read-only
- Plotly renders the result inside this deck
waiting for slide
Dynamic when needed #
Static-first #
- Public data: ship
site.ddb - Local query: DuckDB-Wasm in the browser
- Deploy: any static host
- Cost: no server process
Live data #
- Quack: DuckDB instances talk over HTTP
- Attach: remote catalogs through a
quack:URI - Auth: scoped secret or explicit
TOKENoption - Interface: HTMX or JS updates browser views
- Security: localhost default, reverse proxy/TLS for non-local
Zorto should not hide this. It should make the boundary obvious.
16 built-in themes #
- zorto
- dkdc
- default
- ember
- forest
- ocean
- rose
- slate
- midnight
- sunset
- mint
- plum
- sand
- arctic
- lime
- charcoal
Every theme supports light and dark mode with a single toggle.
Progressive reveal #
Press → or space to reveal one at a time:
- fade-in: the default, gentle entrance
- fade-up: slides in from below
- grow: scales up to emphasize
- highlight-blue: flashes a color highlight
- fade-right: slides in from the left
Config drives the deck #
+++ title = "Data apps" weight = 58 [extra] slide_theme = "ink" layout = "wide" +++
Slide source stays readable. The template owns the runtime and visual system.
Presentations: one file per slide #
- Each slide is a markdown file with its own frontmatter
weightfield controls slide order[extra]controls background, theme, and layout- Shortcodes for columns, fragments, speaker notes, and positioned images
- Native HTML/CSS/JS: keyboard navigation, fullscreen, print
Shortcodes: rich content without HTML #
Built-in #
slide_image: positioned or inline imagesfragment: progressive revealcolumns: side-by-side layoutspeaker_notes: source-level presenter notes
Custom #
- Drop a
.teratemplate inshortcodes/ - Call it from any markdown page
- Keep repeated HTML out of prose
- Validate author-facing inputs at shortcode boundaries
- Test the rendered result with
zorto check
The whole presentation you’re watching is rendered from these shortcodes.
Get started #
# Install uv tool install zorto # Create a new site zorto init mysite cd mysite # Preview with live reload zorto preview --open
Or use as a Python library:
import zorto zorto.build(root=".")