zensical-glossary
Add hover tooltips and glossary links to a Zensical site from one or more Markdown glossary pages.
Write your terms once in docs/glossary.md — spread them across several
domain-focused pages, or define them inline on any page with a comment marker.
Everywhere else in your docs, matching words are automatically underlined,
show a short definition on hover, and link back to the full definition.
Why Use It
- Keep terminology consistent and easily accessible across docs.
- Give readers quick definitions without interrupting the page.
- Link every term back to a canonical glossary entry.
- Organize large glossaries into sections without turning section headings into terms.
- Spread terms across several glossary pages, organized by domain, and have them merged at build time.
- Define terms inline on any page with a comment marker — no glossary file maintenance.
- Add aliases to a term, so plural forms and alternative wordings also match and link back.
- Use normal Python-Markdown and Zensical configuration.
Origin
This extension has its origin in the necessity of generating richer documentation for the POG Programming Language (still to be released). As the language has too many domain specific terms, it is better to have them at hand instead of jumping back-and-forth between a glossary and the page the reader is currently at.
We've decided to publish it before POG, since it has been proven extremely useful in our internal documentations.
Install
In a Zensical project:
uv add zensical-glossary
Or with pip:
pip install zensical-glossary
For local development in this repository:
uv pip install -e .
Quick Start
Create docs/glossary.md:
# Glossary
## Core concepts
### Zensical
Zensical is a modern static site generator with a Rust core and a Python
Markdown pipeline.
### Markdown
Markdown is a lightweight plain-text formatting syntax that is converted to
HTML.
Enable the extension in zensical.toml:
[project.markdown_extensions.zensical_glossary]
glossary_file = "glossary.md"
heading_level = 3
first_only = true
Now any page can simply mention Zensical or Markdown. The extension wraps those
terms with a tooltip and a link to /glossary/#zensical or /glossary/#markdown.
Run the site:
uv run zensical serve
Build it:
uv run zensical build --clean
Glossary Format
By default, heading_level = 2, so every ## Term in the glossary file is a
term.
For larger glossaries, use shallower headings for sections and set
heading_level to the exact level that contains terms:
# Glossary
## Authoring
### Admonition
An admonition is a callout block such as a note, warning, or tip.
### Front matter
Front matter is metadata at the top of a Markdown file.
[project.markdown_extensions.zensical_glossary]
heading_level = 3
Deeper headings remain part of the current term definition instead of becoming separate glossary entries.
Multiple Glossary Pages
For bigger knowledge bases, spread terms across several pages — for example one
page per domain — and list them with glossary_files:
[project.markdown_extensions.zensical_glossary]
glossary_files = [
"glossary/core.md",
"glossary/api.md",
]
heading_level = 3
Entries may also be glob patterns, expanded against docs_dir and sorted for
deterministic builds:
glossary_files = ["glossary/**/*.md"]
All files are merged into a single term index at build time:
- Files are processed in configuration order. If two pages define the same term, the first file wins.
- Each term links to the page that defines it, e.g.
/glossary/api/#widget. - Every glossary source page is skipped during annotation, so glossary pages never link to themselves.
- Parsed pages are cached by modification time, and the merged index and term regex are built once per build, so watch-mode rebuilds stay fast.
glossary_files takes precedence over glossary_file when both are set, so a
single-page setup keeps working unchanged.
Inline Definitions
For terms that are best explained where they are used, skip glossary files entirely and mark a definition directly on any regular page:
<!-- zensical-glossary: Widget -->
A widget is a reusable UI unit that...
The paragraphs after the marker — up to the next heading or the next marker —
become the definition. The marker renders as an invisible anchor
(#widget), and every other mention of the term on the site links to it.
Enable the mode with:
[project.markdown_extensions.zensical_glossary]
inline_definitions = true
All pages are scanned once per build (cached by modification time) and merged with any glossary files into a single index — a hybrid glossary. When a term is defined both inline and in a glossary file, the glossary file wins. A page never links to the terms it defines itself.
Aliases
Prose rarely uses the exact glossary wording — plural forms, alternative spellings, or a fuller name for an acronym. Add an alias marker inside a term's definition block and every comma-separated alias also matches in prose, showing the canonical definition and linking to the canonical anchor:
### API
<!-- zensical-glossary-aliases: Application Programming Interface -->
A set of rules that lets one program request services from another.
In inline mode, the marker goes right after the definition marker:
<!-- zensical-glossary: Widget -->
<!-- zensical-glossary-aliases: gadget, widgets -->
A widget is a reusable unit that...
The marker renders as an invisible HTML comment and never appears in the tooltip. Aliases follow the same matching rules as terms:
- Case-insensitive by default (
case_sensitiveapplies to both). - The longest surface wins: a multi-word alias matches as one unit instead of a shorter term inside it.
first_onlycounts the term and its aliases as one entry per page.min_lengthfilters out aliases that are too short.- On collision, the first definition wins: a real term always beats an alias, and among aliases the earliest one wins.
Configuration
All options live under [project.markdown_extensions.zensical_glossary].
| Option | Type | Default | Description |
|---|---|---|---|
glossary_file |
str | "glossary.md" |
Glossary source, relative to docs_dir. |
glossary_files |
list | [] |
Multiple glossary sources (paths or globs). Wins over glossary_file. |
inline_definitions |
bool | false |
Scan all pages for <!-- zensical-glossary: Term --> definitions. |
heading_level |
int | 2 |
Exact heading level treated as terms; shallower headings can be sections. |
first_only |
bool | true |
Annotate only the first occurrence of each term per page. |
case_sensitive |
bool | false |
Match terms case-sensitively. |
min_length |
int | 2 |
Ignore terms shorter than this. |
max_definition |
int | 280 |
Truncate tooltip text to this many characters. |
base_url |
str | derived | Single file: override the glossary URL. Multiple files: URL prefix. |
language |
str | site language | UI language for tooltip text: en, fr, es, pt. |
labels |
table | {} |
Override UI strings, e.g. labels = { more = "Read more" }. |
docs_dir |
str | "docs" |
Fallback docs directory if it cannot be read from Zensical config. |
For GitHub Pages project sites, set base_url when your site is served from a
repository subpath. With a single glossary file it is the full glossary URL;
with glossary_files it is a prefix joined with each derived page URL:
[project.markdown_extensions.zensical_glossary]
# Single file:
base_url = "https://OWNER.github.io/REPOSITORY/glossary/"
# Multiple files (terms link to https://OWNER.github.io/REPOSITORY/glossary/api/#term):
glossary_files = ["glossary/*.md"]
base_url = "https://OWNER.github.io/REPOSITORY"
Example Project
This repository includes a complete Zensical example in docs/. It is the same
site that can be published with the included GitHub Pages workflow.
uv run zensical serve
uv run zensical build --clean
The workflow at .github/workflows/docs.yml builds site/ and deploys it to
GitHub Pages on pushes to master.
Local Development
This repository uses the drun automation language for local automation.
Use xdrun --list to see the available tasks.
xdrun test
xdrun build
xdrun rebuild
# xdrun ci is optimized for LLMs and MCPs, it only generates
# verbose output in the case an error is thrown. Saves input tokens.
xdrun ci
# For seeing everything the CI is outputting, use:
xdrun ci --task-mode normal
testruns the Python test suite.buildcreates package artifacts and builds the example site.rebuildremoves generated outputs first, then builds everything again.ciruns the full local verification lifecycle, including package metadata checks.
GitHub Actions runs the same xdrun ci pipeline in
.github/workflows/ci.yml for pull requests and pushes.
Releasing
Releases are prepared with:
xdrun prepare-release version=X.Y.Z
This checks that the version is newer than the latest release, runs the full
CI pipeline, promotes the unreleased changelog entries to the new version,
syncs the version across the drun spec, pyproject.toml, and the package,
and rebuilds the distribution artifacts. After reviewing the changes, commit,
tag vX.Y.Z, and push the tag — the workflow at
.github/workflows/release.yml builds and publishes to PyPI.
Internationalization
The tooltip UI text is localized separately from your glossary content. The language is resolved in this order:
- The extension's
languageoption. - The Zensical site language.
- English.
Supported built-in languages are English (en), French (fr), Spanish (es),
and Portuguese (pt). Region subtags are ignored, so pt-BR resolves to pt.
Override a label directly:
[project.markdown_extensions.zensical_glossary]
language = "fr"
labels = { more = "Voir la definition complete" }
How It Works
The extension parses the glossary files and scans pages for inline definition markers, merges everything into one term index, and uses a treeprocessor to wrap matching text in rendered pages. Each term links to the page that defines it. It skips code, links, abbreviations, headings, the glossary pages themselves, and each term's own defining page. A postprocessor injects the tooltip CSS and JavaScript only on pages where at least one term was found. Source files are cached by modification time, so unchanged content is never re-parsed during a build or watch session.
License
MIT
Metadata
Release files for zensical-glossary 1.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zensical_glossary-1.2.1.tar.gz | 31.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zensical_glossary-1.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.3 kB
Release files / zensical_glossary-1.2.1.tar.gz
| Download URL | zensical_glossary-1.2.1.tar.gz |
|---|---|
| Size | 31.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8e8817d23d35014997070d72dc0d697978518e4574668827e634638733fbaee5
|
|
BLAKE2b-256 checksum How to use checksums |
228b255d1f4552f2e308611aa22db3f61fb83b0148b75c67cb92c2d207ff24b2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / zensical_glossary-1.2.1-py3-none-any.whl
| Download URL | zensical_glossary-1.2.1-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
05e637767dd4def1c109140cf34c89600d8f6f68c60a88dafa6c174689ecfb3d
|
|
BLAKE2b-256 checksum How to use checksums |
f722902eccdaa015e0a98e51e3222c30c458b182e3302e6e1ad37b7f301158c1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|