zola-quiet

A quiet, two-skin Zola theme. Two stylesheets ship together — a Minima-flavoured typographic skin and a TUI/ncurses skin with an ASCII-drawn article frame. Both ship with matched light and dark modes, and two runtime toggles in the top-right flip between them (skin + light/dark). Both choices persist to localStorage. Posts with headings get a docs-style "on this page" TOC pinned under the sidebar on the TUI skin, with scroll-spy for the active section.

No frameworks, no fonts hot-linked from a CDN, no analytics, no search, no comments. Static HTML out of Zola, two CSS files, two tiny inline scripts.

Screenshots

Each skin is shown with its light mode on the left and dark mode on the right, rendered from the built-in lorem-preview sample post via scripts/screenshots.sh.

Minima skin (the default):

zola-quiet minima — light and dark

TUI/ncurses skin with ASCII article frame and on-page TOC:

zola-quiet TUI — light and dark

What you get

Install

cd your-site
git submodule add https://github.com/johnnybravo-xyz/zola-quiet themes/zola-quiet

Then in your config.toml:

theme = "zola-quiet"

[extra] keys the templates read

All optional. Every block is {%/* if */%}-guarded so the theme degrades cleanly if a key isn't set.

KeyUsed byEffect
extra.author<meta>, footer line"© YEAR "
extra.githubindex.html, footerGitHub icon + connect link
extra.linkedinindex.html, footerLinkedIn icon + connect link
extra.emailindex.html, footerMail icon + connect link
extra.ascii_signaturebase.html sidebarMulti-line ASCII shown as a quiet signature above the content
extra.homepage_post_limitindex.htmlCap the front-page post list to N most recent posts. When more exist, a "see all M posts →" link appears below the list. Unset or 0 shows every post.

Example:

[extra]
author = "Jane Doe"
github = "janedoe"
linkedin = "janedoe"
email = "jane@example.com"
ascii_signature = """
â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
â–‘â–‘ HELLO  â–‘â–‘
â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘â–‘
"""

Shareable heading anchors

The theme ships an anchor-link.html override that renders as a dim > character before the heading text. To enable, set insert_anchor_links = "left" in a page's frontmatter (or the section's _index.md, where it applies as a default to all pages in the section):

insert_anchor_links = "left"

Zola then inserts a <a class="header-anchor" href="#slug">&gt;</a> before every h2/h3/etc. The CSS in static/style.css styles it to look like a subtle prefix — dim by default, coloured on hover.

Embedding a visualiser

If a post embeds an interactive visualiser as an iframe, the theme ships two small helpers under /visualizers/_shared/ so the plumbing stays two <script> lines instead of a copy-pasted block.

On the post side, mark the iframe with data-viz-embed and give it the height-message name the frame will post back:

<iframe src="/visualizers/foo.html"
        title="Foo visualiser"
        loading="lazy"
        style="width:100%;height:800px;border:1px solid var(--rule);
               border-radius:6px;"
        data-viz-embed
        data-height-msg="foo-height"></iframe>
<script src="/visualizers/_shared/viz-embed.js" defer></script>

viz-embed.js finds every iframe[data-viz-embed] on the page, sends {theme, skin} to it on load and whenever the parent's data-theme or data-skin change, and resizes the iframe when the frame posts a height back.

Inside the visualiser HTML, load the frame-side helper (plus the optional TUI palette so the viz tracks the skin toggle):

<link rel="stylesheet" href="/visualizers/_shared/viz-tui.css">
<script src="/visualizers/_shared/viz-frame.js"
        data-height-msg="foo-height"
        data-size-selector=".wrap"
        defer></script>

viz-frame.js mirrors data-theme / data-skin onto the frame's own <html> when it receives them, and posts the .wrap element's height back to the parent (change the selector via data-size-selector). Omit data-height-msg to skip height reporting entirely.

viz-tui.css sets base CSS variables (--bg, --panel, --text, --mark, --match, --danger, etc.) for TUI light + dark. Each visualiser stays free to add its own semantic aliases (e.g. --go: var(--mark);) — the file only overrides when data-skin="tui" is on the frame root.

Syntax highlighting

The theme is wired for Zola 0.22+ class-mode syntax highlighting, but doesn't enable it for you — you opt in from your site's config.toml. When you do, the base template loads two CSS files (/giallo-light.css and /giallo-dark.css) with media-query gating, and the theme toggle flips them in step with the light/dark toggle.

To enable, add this to your site's config.toml:

[markdown.highlighting]
style = "class"
light_theme = "github-light"
dark_theme = "github-dark"

Zola emits the two CSS files automatically on the next build. The filenames (giallo-light.css and giallo-dark.css) are fixed by Zola — don't rename them; the theme's <link> tags expect those exact paths.

The theme names come from shikijs/textmate-grammars-themes (VSCode-flavoured TextMate themes that Zola 0.22+ bundles via the giallo highlighter). Any matched light/dark pair works — common choices are github-light/github-dark, one-light/one-dark-pro, ayu-light/ayu-dark, everforest-light/everforest-dark.

If you'd rather not have syntax highlighting, simply omit the [markdown.highlighting] block; the <link> tags will 404 on those two files but the rest of the site is unaffected. (Or fork the base template and drop the two lines.)

Templates you can override

Drop a file with the same name into your site's templates/ to override the theme's. The theme ships:

CSS conventions

Useful classes the stylesheets style:

Run the demo

git clone https://github.com/johnnybravo-xyz/zola-quiet
cd zola-quiet
zola serve

config.toml at the repo root is a minimal demo configuration that's only used when building this repo in isolation.

License

MIT — see LICENSE.