| name | zensical-setup |
| description | Guide for setting up and configuring Zensical documentation sites. Use when: create documentation site, configure zensical.toml, set up navigation, change theme colors, configure fonts, add analytics, deploy docs site, GitHub Pages, GitLab Pages, mkdocs.yml migration, customize Zensical theme, template overrides, add CSS or JavaScript.
|
Zensical Site Setup & Configuration
Zensical is a modern static site generator for project documentation, built by
the creators of Material for MkDocs. It uses TOML configuration (zensical.toml)
and supports mkdocs.yml for migration. This skill covers installation,
configuration, theming, navigation, deployment, and customization.
Quick Start
python3 -m venv .venv && source .venv/bin/activate && pip install zensical
uv init && uv add --dev zensical
zensical new .
zensical serve
zensical build
Generated structure:
.
├─ .github/ # GitHub Actions workflow
├─ docs/
│ ├─ index.md
│ └─ markdown.md
└─ zensical.toml
Configuration File
Zensical uses zensical.toml (recommended) or mkdocs.yml (for migration).
All settings live under the [project] scope.
Essential Settings
[project]
site_name = "My Site"
site_url = "https://example.com"
site_description = "Project docs"
site_author = "Jane Doe"
copyright = "© 2025 Jane Doe"
docs_dir = "docs"
site_dir = "site"
use_directory_urls = true
dev_addr = "localhost:7860"
Theme Variant
Two variants: modern (default, fresh design) and classic (matches Material for MkDocs).
[project.theme]
variant = "classic"
Navigation
Automatic Navigation
By default, Zensical generates navigation from the folder structure.
Explicit Navigation
[project]
nav = [
{ "Home" = "index.md" },
{ "About" = [
"about/index.md",
"about/vision.md",
"about/team.md"
]},
{ "GitHub Repo" = "https://github.com/org/repo" }
]
Navigation Features
Enable features under [project.theme]:
[project.theme]
features = [
"navigation.instant",
"navigation.instant.prefetch",
"navigation.instant.progress",
"navigation.tabs",
"navigation.tabs.sticky",
"navigation.sections",
"navigation.expand",
"navigation.indexes",
"navigation.path",
"navigation.prune",
"navigation.top",
"navigation.footer",
"navigation.tracking",
"toc.follow",
"toc.integrate",
]
Incompatibilities:
navigation.prune ↔ navigation.expand
navigation.indexes ↔ toc.integrate
Instant Previews
[project.markdown_extensions.zensical.extensions.preview]
configurations = [
{ targets.include = ["setup/extensions/*", "customization.md"] }
]
Hiding Sidebars (per page, via front matter)
---
hide:
- navigation
- toc
- path
- footer
- tags
- feedback
---
Colors
Color Scheme
[project.theme.palette]
scheme = "default"
primary = "indigo"
accent = "indigo"
Available primary colors: red, pink, purple, deep-purple, indigo, blue,
light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange,
deep-orange, brown, grey, blue-grey, black, white, custom.
Available accent colors: red, pink, purple, deep-purple, indigo, blue,
light-blue, cyan, teal, green, light-green, lime, yellow, amber, orange,
deep-orange.
Color Palette Toggle (light/dark switch)
[[project.theme.palette]]
scheme = "default"
toggle.icon = "lucide/sun"
toggle.name = "Switch to dark mode"
[[project.theme.palette]]
scheme = "slate"
toggle.icon = "lucide/moon"
toggle.name = "Switch to light mode"
System Preference + Automatic Mode
[[project.theme.palette]]
media = "(prefers-color-scheme)"
toggle.icon = "lucide/sun-moon"
toggle.name = "Switch to light mode"
[[project.theme.palette]]
media = "(prefers-color-scheme: light)"
scheme = "default"
toggle.icon = "lucide/sun"
toggle.name = "Switch to dark mode"
[[project.theme.palette]]
media = "(prefers-color-scheme: dark)"
scheme = "slate"
toggle.icon = "lucide/moon"
toggle.name = "Switch to system preference"
Custom Colors via CSS
Set primary = "custom" then define CSS variables:
:root > * {
--md-primary-fg-color: #EE0F0F;
--md-primary-fg-color--light: #ECB7B7;
--md-primary-fg-color--dark: #90030C;
}
Custom Color Scheme
[data-md-color-scheme="my-scheme"] {
--md-primary-fg-color: #EE0F0F;
}
[data-md-color-scheme="slate"] {
--md-hue: 210;
}
Fonts
[project.theme]
font.text = "Inter"
font.code = "JetBrains Mono"
Custom fonts via CSS:
:root {
--md-text-font: "My Custom Font";
--md-code-font: "My Mono Font";
}
Logo & Icons
[project.theme]
logo = "images/logo.png"
favicon = "images/favicon.png"
[project.theme.icon]
logo = "lucide/smile"
repo = "fontawesome/brands/github"
edit = "material/pencil"
view = "material/eye"
previous = "fontawesome/solid/angle-left"
next = "fontawesome/solid/angle-right"
[project.extra]
homepage = "https://example.com"
Bundled Icon Sets
- Lucide, Material Design, FontAwesome, Octicons, Simple Icons
- Over 10,000 icons available
Adding Custom Icons
Place SVGs in overrides/.icons/<set-name>/:
[project.theme]
custom_dir = "overrides"
[project.markdown_extensions.pymdownx.emoji]
emoji_index = "zensical.extensions.emoji.twemoji"
emoji_generator = "zensical.extensions.emoji.to_svg"
options.custom_icons = ["overrides/.icons"]
Language
[project.theme]
language = "en"
direction = "ltr"
Language Selector
[project.extra]
alternate = [
{ name = "English", link = "/en/", lang = "en" },
{ name = "Deutsch", link = "/de/", lang = "de" }
]
Repository Integration
[project]
repo_url = "https://github.com/org/repo"
repo_name = "org/repo"
edit_uri = "edit/main/docs/"
[project.theme]
features = [
"content.action.edit",
"content.action.view"
]
Footer
[project]
copyright = "Copyright © 2025 My Company"
[project.theme]
features = ["navigation.footer"]
[[project.extra.social]]
icon = "fontawesome/brands/github"
link = "https://github.com/org"
[[project.extra.social]]
icon = "fontawesome/brands/x-twitter"
link = "https://x.com/handle"
name = "Company on X"
[project.extra]
generator = false
Search
Built-in, enabled by default. English only currently.
[project.theme]
features = ["search.highlight"]
Header
[project.theme]
features = [
"header.autohide",
"announce.dismiss"
]
Announcement bar via template override:
{% extends "base.html" %}
{% block announce %}
Your announcement here!
{% endblock %}
Analytics
[project.extra.analytics]
provider = "google"
property = "G-XXXXXXXXXX"
[project.extra.analytics.feedback]
title = "Was this page helpful?"
[[project.extra.analytics.feedback.ratings]]
icon = "material/emoticon-happy-outline"
name = "This page was helpful"
data = 1
note = "Thanks for your feedback!"
[[project.extra.analytics.feedback.ratings]]
icon = "material/emoticon-sad-outline"
name = "This page could be improved"
data = 0
note = "Thanks for your feedback!"
Data Privacy / Cookie Consent
[project.extra.consent]
title = "Cookie consent"
description = "We use cookies to measure our docs effectiveness."
actions = ["accept", "manage"]
[project.extra.consent.cookies]
analytics = "Google Analytics"
custom = "Custom cookie"
Change cookie settings link in footer:
[project]
copyright = """
Copyright © 2025 –
<a href="#__consent">Change cookie settings</a>
"""
Tags
Tags work by default. Add to pages via front matter:
---
tags:
- Setup
- Getting started
---
Tag icons:
[project.extra.tags]
HTML5 = "html"
[project.theme.icon.tag]
default = "lucide/hash"
html = "fontawesome/brands/html5"
Offline Usage
[project.plugins.offline]
Limitations: disable instant navigation, analytics, repo info, comments.
Custom CSS & JavaScript
[project]
extra_css = ["stylesheets/extra.css"]
extra_javascript = ["javascripts/extra.js"]
JavaScript modules:
[[project.extra_javascript]]
path = "javascripts/extra.js"
type = "module"
Async scripts:
[[project.extra_javascript]]
path = "javascripts/extra.js"
async = true
Important: use document$ observable for instant navigation compatibility:
document$.subscribe(function() {
console.log("Initialize third-party libraries here")
})
Template Overrides
[project.theme]
custom_dir = "overrides"
Override Blocks (recommended)
{% extends "base.html" %}
{% block htmltitle %}
<title>Custom Title</title>
{% endblock %}
Available blocks: analytics, announce, config, container, content,
extrahead, fonts, footer, header, hero, htmltitle, libs,
outdated, scripts, site_meta, site_nav, styles, tabs.
Override Partials
Place files in overrides/partials/ matching the theme's partial structure.
Custom Templates (per page)
---
template: my_template.html
---
Custom 404 Page
Place 404.html in the overrides directory.
Markdown Extensions (Default Configuration)
Zensical has sensible defaults. If you need to customize, here is the full
default expansion you can use as a starting point:
[project.markdown_extensions.abbr]
[project.markdown_extensions.admonition]
[project.markdown_extensions.attr_list]
[project.markdown_extensions.def_list]
[project.markdown_extensions.footnotes]
[project.markdown_extensions.md_in_html]
[project.markdown_extensions.toc]
permalink = true
[project.markdown_extensions.pymdownx.arithmatex]
generic = true
[project.markdown_extensions.pymdownx.betterem]
smart_enable = "all"
[project.markdown_extensions.pymdownx.caret]
[project.markdown_extensions.pymdownx.details]
[project.markdown_extensions.pymdownx.emoji]
emoji_generator = "zensical.extensions.emoji.to_svg"
emoji_index = "zensical.extensions.emoji.twemoji"
[project.markdown_extensions.pymdownx.highlight]
[project.markdown_extensions.pymdownx.inlinehilite]
[project.markdown_extensions.pymdownx.keys]
[project.markdown_extensions.pymdownx.mark]
[project.markdown_extensions.pymdownx.smartsymbols]
[project.markdown_extensions.pymdownx.superfences]
[project.markdown_extensions.pymdownx.tabbed]
alternate_style = true
[project.markdown_extensions.pymdownx.tasklist]
custom_checkbox = true
[project.markdown_extensions.pymdownx.tilde]
To reset to MkDocs defaults (no extensions):
[project]
markdown_extensions = {}
Deployment
GitHub Pages (GitHub Actions)
name: Documentation
on:
push:
branches: [master, main]
permissions:
contents: read
pages: write
id-token: write
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/configure-pages@v5
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: 3.x
- run: pip install zensical
- run: zensical build --clean
- uses: actions/upload-pages-artifact@v4
with:
path: site
- uses: actions/deploy-pages@v4
id: deployment
Site appears at <username>.github.io/<repository>.
GitLab Pages
pages:
stage: deploy
image: python:latest
script:
- pip install zensical
- zensical build --clean
pages:
publish: public
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
Important: Set site_dir = "public" for GitLab Pages.
Site appears at <username>.gitlab.io/<repository>.
Content Area Width
.md-grid {
max-width: 1440px;
}
mkdocstrings (API docs)
pip install mkdocstrings-python
[project.plugins.mkdocstrings.handlers.python]
paths = ["src"]
inventories = ["https://docs.python.org/3/objects.inv"]
[project.plugins.mkdocstrings.handlers.python.options]
docstring_style = "google"
inherited_members = true
show_source = false
Comment System (Giscus example)
Override partials/comments.html with the Giscus script, then enable per page:
---
comments: true
---
CLI Reference
| Command | Purpose |
|---|
zensical new <dir> | Create new project |
zensical serve | Preview with live reload |
zensical serve -o | Preview and open browser |
zensical serve -a localhost:3000 | Custom address |
zensical build | Build static site |
zensical build --clean | Build with cache cleaning |
zensical build -f path/to/config | Use specific config file |