Skip to content

Commit 5ee03dc

Browse files
authored
docs: migrate from MkDocs to Zensical and adopt the shared theme (#353)
* docs: add zensical.toml alongside mkdocs.yml Additive step of the MkDocs -> Zensical migration; both generators build at this commit and the toolchain cutover follows. Every key from mkdocs.yml was reconciled by hand rather than trusting the build: Zensical silently ignores unknown config keys even under --strict, so a clean build proves nothing about the translation. Adopts the shared stridelabs-docs-theme instead of carrying the look across -- theme.palette, theme.font and theme.features are dropped because the theme owns them, so restyling the fleet becomes a version bump rather than an edit in every repo. theme.icon.logo is kept: it is what identifies this project beside the shared owl. Working notes previously withheld via MkDocs exclude_docs move to discovery/ at the repo root. Zensical has no exclude_docs equivalent and would have published them. .gitignore picks up Zensical's root-level /.cache/ (anchored, matching the file's existing convention). * docs: cut over from MkDocs to Zensical Atomic cutover: dependency group, lockfile, both workflows, the Makefile targets and the removal of mkdocs.yml land together, so no commit has docs CI or `make docs` pointing at a config that does not exist. Dependencies: mkdocs, mkdocs-material and pymdown-extensions are dropped for zensical plus the shared stridelabs-docs-theme (git dependency pinned to v0.2.2 -- a public repo rather than a registry, so PRs from forks and local clones can still build docs). pymdown-extensions now arrives transitively; pinning it twice invites conflicts. `grep -c mkdocs uv.lock` is 0. Workflows: adds Docs PR Build, which did not exist -- docs previously built only on push to main and without --strict, so a broken link or anchor could land on main and be caught at deploy time or not at all. Both workflows now build --strict and watch uv.lock, so a theme bump redeploys. Action pins bumped and each ref verified through the git refs API; setup-uv is pinned to v9.0.0 because it publishes no moving v9 tag and actionlint does not catch that. `make docs` / `make docs-serve` now use --locked, matching CI: the theme tag is mutable, so the lockfile's resolved commit SHA is the real integrity check. --strict required a small content fix that MkDocs had been warning about without failing: out-of-tree links to tools/, linux/, and packaging/ became GitHub blob URLs, and the DL-12 heading anchor gained the date suffix that the generated id actually uses. Verified with the locked toolchain: identical 27-page set and all 374 heading anchors preserved, theme markup present, appcast.xml still copied through, and zero requests to Google Fonts.
1 parent 9be2493 commit 5ee03dc

18 files changed

Lines changed: 367 additions & 468 deletions

.github/workflows/docs-pr.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Docs PR Build
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- 'docs/**'
7+
- 'zensical.toml'
8+
- 'pyproject.toml'
9+
- 'uv.lock'
10+
- '.github/workflows/docs-pr.yml'
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: docs-pr-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
build:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 10
23+
steps:
24+
- uses: actions/checkout@v7
25+
with:
26+
persist-credentials: false
27+
# setup-uv publishes no moving major tag -- v9 does not resolve.
28+
- uses: astral-sh/setup-uv@v9.0.0
29+
- run: uv python install 3.13
30+
- run: uv sync --locked --group docs
31+
- run: uv run --locked zensical build --strict

.github/workflows/docs.yml

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ on:
55
branches: [main]
66
paths:
77
- 'docs/**'
8-
- 'mkdocs.yml'
8+
- 'zensical.toml'
99
- 'pyproject.toml'
10+
- 'uv.lock'
1011
- '.github/workflows/docs.yml'
1112
workflow_dispatch:
1213

@@ -23,13 +24,14 @@ jobs:
2324
build:
2425
runs-on: ubuntu-latest
2526
steps:
26-
- uses: actions/checkout@v6
27+
- uses: actions/checkout@v7
2728
with:
2829
persist-credentials: false
29-
- uses: astral-sh/setup-uv@v7
30+
# setup-uv publishes no moving major tag -- v9 does not resolve.
31+
- uses: astral-sh/setup-uv@v9.0.0
3032
- run: uv python install 3.13
31-
- run: uv sync --group docs
32-
- run: uv run mkdocs build
33+
- run: uv sync --locked --group docs
34+
- run: uv run --locked zensical build --strict
3335
- uses: actions/upload-pages-artifact@v5
3436
with:
3537
path: site-build
@@ -41,5 +43,6 @@ jobs:
4143
runs-on: ubuntu-latest
4244
needs: build
4345
steps:
44-
- id: deployment
46+
- name: Deploy to GitHub Pages
47+
id: deployment
4548
uses: actions/deploy-pages@v5

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
/build/ghostty-src/
66
/site-build/
77

8+
# Zensical differential-build cache (root only -- the anchor keeps this
9+
# from also swallowing a nested .cache/ elsewhere in the tree)
10+
/.cache/
11+
812
# Rust workspace
913
/target/
1014
**/*.rs.bk

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,43 @@ builds the DMG + `.deb`s and publishes to the apt repo. Bump
99
`[workspace.package].version` in `Cargo.toml` to match before tagging (the
1010
release workflow asserts they agree).
1111

12+
## Unreleased
13+
14+
### Changed
15+
16+
- **Docs site migrated from Material for MkDocs to
17+
[Zensical](https://zensical.org)**, the successor from the same team.
18+
Material entered maintenance mode in November 2025 and now warns on every
19+
build that MkDocs 2.0 will remove the plugin and theming systems with no
20+
migration path. `mkdocs.yml` is replaced by a native `zensical.toml`;
21+
documentation content is unchanged aside from a handful of links that
22+
`--strict` now validates (out-of-tree paths became GitHub blob URLs; one
23+
stale heading anchor was corrected).
24+
25+
The look now comes from the shared
26+
[stridelabs-docs-theme](https://github.com/charliek/stridelabs-docs-theme)
27+
package rather than per-repo config, so restyling every site is a version
28+
bump instead of an edit in each repo. Fonts are self-hosted by the theme, so
29+
the site no longer requests anything from `fonts.googleapis.com` or
30+
`fonts.gstatic.com`.
31+
32+
Working notes previously withheld via MkDocs `exclude_docs` moved to
33+
`discovery/` at the repo root — Zensical has no equivalent, and leaving
34+
them under `docs/` would have published them.
35+
36+
Verified against the pre-migration build: identical 27-page set and all
37+
374 heading anchors preserved, so existing deep links still resolve.
38+
`docs/appcast.xml` still copies through to the published site. Page
39+
`<title>` now derives from the page `<h1>` rather than the nav label, which
40+
is the one intentional difference.
41+
42+
### Added
43+
44+
- **`Docs PR Build` workflow.** Docs previously built only on push to `main`,
45+
and without `--strict`, so a broken link or heading anchor could land on
46+
`main` and be caught at deploy time or not at all. Both workflows now build
47+
`--strict` and watch `uv.lock`.
48+
1249
## v0.0.17 — 2026-07-31
1350

1451
Agents move out of the palette and into the sidebar. Every running agent now

CLAUDE.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,44 @@ wrapper small.
241241
and before the single-instance lock, so it never touches a running
242242
instance.
243243

244+
## Docs
245+
246+
**Not part of `make check` or the `ci-success` gate.** The docs site has
247+
its own toolchain (uv/Python) and its own CI workflows; the Rust/Swift
248+
gates do not cover it. Run it for commits touching `docs/`,
249+
`zensical.toml`, `pyproject.toml`, `uv.lock`, or either docs workflow —
250+
both workflows trigger on those shared inputs (and each additionally on
251+
its own file), because a dependency or lockfile change can break the
252+
build just as easily as a content change:
253+
254+
```bash
255+
make docs # uv run --locked zensical build --strict
256+
make docs-serve # preview on http://127.0.0.1:7070
257+
```
258+
259+
The site is [Zensical](https://zensical.org) (not MkDocs — migrated 2026-08),
260+
configured in `zensical.toml`, built into `site-build/`. `--strict` fails
261+
on broken links and anchors and is what both CI workflows run, so run it
262+
locally before pushing docs changes. Note `zensical serve --strict` is
263+
unsupported; verify strictness via `build`.
264+
265+
The look comes from the shared
266+
[stridelabs-docs-theme](https://github.com/charliek/stridelabs-docs-theme)
267+
package, pinned by tag in `pyproject.toml`. Palette, fonts and feature
268+
toggles live there, not here — do not add `theme.palette`, `theme.features`,
269+
or a `[project.theme.font]` table to `zensical.toml`. The last is the
270+
sharp edge: it re-enables Zensical's Google Fonts `<link>` on every page
271+
while the theme's self-hosted faces keep loading anyway.
272+
273+
Working notes in `discovery/` live outside `docs/` on purpose. Zensical
274+
has no `exclude_docs` equivalent; files under `docs_dir` are published.
275+
276+
Two gotchas worth knowing: Zensical **silently ignores unknown config
277+
keys** even under `--strict`, so a green build does not prove a config
278+
edit did what you meant; and the `pymdownx.emoji` callables live in the
279+
`zensical.extensions.emoji` namespace — the Material for MkDocs
280+
`material.extensions.emoji` namespace aborts the build.
281+
244282
## Build
245283

246284
- libghostty-vt is pinned to a specific Ghostty commit in

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,11 @@ check: fmt-check clippy themes-check test ## Pre-push gate: fmt-check + clippy
259259
# ---- docs -------------------------------------------------------------
260260

261261
.PHONY: docs docs-serve
262-
docs: ## Build the mkdocs site into site-build/
263-
uv sync --group docs && uv run mkdocs build
262+
docs: ## Build the docs site into site-build/ (same as CI)
263+
uv sync --locked --group docs && uv run --locked zensical build --strict
264264

265-
docs-serve: ## Serve the docs locally (mkdocs serve)
266-
uv sync --group docs && uv run mkdocs serve
265+
docs-serve: ## Serve the docs locally with live reload
266+
uv sync --locked --group docs && uv run --locked zensical serve
267267

268268
# ---- clean ------------------------------------------------------------
269269

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ cd mac && swift build # or: ./mac/scripts/bundle.sh release →
5656

5757
## Documentation
5858

59-
The full site lives under `docs/` and builds with `mkdocs-material` (`make docs-serve`http://127.0.0.1:7070):
59+
The full site lives under `docs/` and builds with [Zensical](https://zensical.org) (`make docs-serve`http://127.0.0.1:7070):
6060

6161
- [Installation](docs/getting-started/installation.md) — toolchain + build
6262
- [First Run](docs/getting-started/first-run.md) — launch behavior + where state lives

RELEASING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ Inspect or edit at https://github.com/charliek/roost/rules.
176176
## The appcast lives where
177177

178178
The Sparkle appcast is at `docs/appcast.xml`, served by GitHub Pages from
179-
`https://charliek.github.io/roost/appcast.xml` via `docs.yml`'s mkdocs
179+
`https://charliek.github.io/roost/appcast.xml` via `docs.yml`'s Zensical
180180
deploy. The `appcast` job mutates that file in place, commits it as the
181181
release-bot, and pushes to main; `docs.yml` redeploys Pages shortly after.
182182

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Discovery notes
22

3-
Working notes, not product commitments. `mkdocs.yml` lists this
4-
folder under `exclude_docs`, so it is not in the nav and is not
5-
built into the public site.
3+
Working notes, not product commitments. These files live outside
4+
`docs/` so they are not built into the public site — Zensical has
5+
no equivalent of MkDocs `exclude_docs`.
66

77
| Note | What it is |
88
|---|---|
File renamed without changes.

0 commit comments

Comments
 (0)