A new look for the documentation site¶
The maintainer asked for the site to take on their own design language, the Micfong Typst template and micfong.space, with ideas from PRTS Design, the design system behind the prts.wiki skin. The whole change is CSS over Zensical's modern variant:
mcs.cssholds the Micfong Colour System (MCS) andhenad.cssnames its entries by role, with no template override and no script. The header and tabs became a black band textured with micfong.space's grid, the content took the Typst template's rules, callouts, faded section numbers and bracketed code, and PRTS Design lent diamond bullets, hazard stripes and a corner mark. Code is set in a 34 KB subset of Iosevka Micfong in place of JetBrains Mono from Google Fonts. After a first review the maintainer found it too large, too loose and too bright in the dark scheme, so the body went back to the stock 15px, the margins tightened and the dark ground moved to gray 950. A second review asked for eight changes, among them no orange on headings or list numbers, and an audit for consistency. Six audit lenses reported 70 findings, adversarial verifiers kept 69, and 23 merged fixes went in, followed by 16 of the 18 problems a second verification pass confirmed.
State before¶
master was at d6ef652 with a clean tree.
The site ran Zensical's modern variant almost unchanged.
docs/stylesheets/henad.css was 60 lines: IBM Plex Sans as the text face, the .ui chip and the status icon for the agent records.
Everything else was stock: rounded corners, code in filled boxes, JetBrains Mono and Inter requested from Google Fonts, and an orange accent from the palette settings.
What was done¶
Sources¶
- The Micfong Typst template, the document half of the maintainer's design system. Its tokens give each MCS hue four roles that mirror between schemes (strong 700/200, base 500, soft 100/800, subtle 50/900), and its elements are 0.75pt rules, callouts ruled on the left, panels under a tinted title strip, faded heading counters, inline code between grey brackets and blocks between rules whose ends turn inwards.
- micfong.space, from its
packages/uistyles. A black ground with a 32px grid and a crosshair every 128px, uppercase bold navigation with an orange bar over the current item, a one-pixel reading-progress line under the navigation, orange links with a translucent underline, and the same bracketed code. - PRTS Design and the
mediawiki-skins-Arknightssource, which vendors it as plain CSS. Its rules are monochrome first with the accent kept for links, selection and the primary action, square corners, a thick bar beside the page title, a short accent stroke on a section's rule, diamond bullets, hazard stripes for danger and a triangular corner mark for selection. prts.wiki itself refused the browser extension, so the skin source stood in for it.
Where the sources disagree, the maintainer's own systems decide, as the request asked.
Customising Zensical with CSS alone¶
Zensical offers extra_css, extra_javascript, template overrides through custom_dir, and theme.font.
Only the first and the last were needed.
theme.font = false stops the request to Google Fonts, and the faces are declared in henad.css.
Three selectors carry the overrides past the modern variant's own rules.
The variables sit on [data-md-color-scheme][data-md-color-primary], which outranks the per-primary rules that set the link colour.
The callouts take [class] (.md-typeset :is(.admonition, details)[class]), which matches the specificity of the per-type rules and comes later.
The content-tab indicator lives under .js, so its rule repeats that class.
Colour¶
mcs.css lists all 77 entries as --mcs-<hue>-<step>, and a script confirmed each against crates/henad-app/src/ui/mcs.rs.
henad.css defines the roles per scheme as --hd-* variables and points Zensical's --md-* variables at them, the syntax colours included.
| Role | Light | Dark |
|---|---|---|
| Page | white | gray 950 |
| Body text | gray 900 | gray 300 |
| Headings | black | white |
| Secondary text | gray 700 | gray 400 |
| Faded numbers | gray 400 | gray 600 |
| Light and dark rules | gray 200, 300 | gray 800, 700 |
| Link | orange 700 | orange 500 |
| Selection | orange 600 | orange 500 at 67% |
| Bullets and list numbers | gray 700 | gray 400 |
| Band (header, tabs, footer) | black | black |
The dark page started at gray 900, the Typst template's dark page, and body text at gray 100. The maintainer found it too bright, so the page moved to gray 950 and the body to gray 300, close to the stock theme's softened text. With the page at 950, the band went to pure black, as micfong.space has it.
Shell¶
- Band. The header and the tabs share the black band, with micfong.space's grid and crosshair drawn from an SVG data URI and faded in from the left by a mask, the way PRTS Design fades its halftone.
The tabs offset the tile by the header height, so the grid runs across both.
The crosshairs sit on the header's middle line, at
round(1.2rem, 1px)so they stay there when Zensical raises the root size. At the header's edge they were cut in half once the tabs scrolled away. The header buttons, the repository name and the page title knock the grid out behind them, as the search box does. - Tabs. Uppercase bold labels 6.4rem wide, the current one under a 3px orange bar over a 9% orange tint, as micfong.space marks its navigation. The tint clears when Zensical hides the tabs on scrolling, and the inset shadow Zensical draws along the tabs' lower edge is gone, since the grid crossed it.
- Reading progress. A one-pixel orange line along the header's lower edge, driven by
animation-timeline: scroll()inside@supports, so browsers without scroll-driven animations draw nothing. - Sidebar. Items carry a 2px left border, grey on hover and orange on the current page, with an orange tint. The hover selectors repeat Zensical's own, which otherwise filled a hovered item heavier than the current one. On a wide screen a group name is a small uppercase label on one line followed by a rule, as micfong.space heads its cards. A page without an icon keeps its title in line with the pages that have one.
- Drawer. On a narrow screen the drawer's title is a strip of the band, so the white logo shows in the light scheme.
- Table of contents. A one-pixel rail on the left, the title marked with a 2px grey bar, the current section in orange, and the first level numbered to match the headings. Nested entries start at their parent's text, and code in an entry takes the entry's colour.
- Page actions. The edit and view buttons are bare icons. Headings are positioned for their permalinks, and the buttons needed a z-index of their own to take clicks again.
- Footer. The previous and next pages as ruled blocks of at most half the width, hovered like a grid card, then the band again under the copyright, closed on the right by the Typst wordmark,
MICFONGand its triangle. The copyright, its links and the copy notice take the band's own colours in both schemes.
Content¶
- Type. IBM Plex Sans at 15px and 1.7 line height with
ss01,ss02andtnum, and no tracking. - Headings. No heading carries orange.
A section heading has a faded number from a CSS counter and a light rule beneath it.
Only
##headings are numbered: the agent records number their own###headings ("### 1. ..."), and a second number in front would read "4.1 1.". The permalink hangs in the left margin, and below the wide layout, where the gutter is too narrow for it, it is hidden. Inline, it wrapped a full heading onto a second line, and placed after the heading it widened three pages at phone width. - Links are orange with a 40% orange underline that turns solid on hover, and a link of code draws the same underline. An abbreviation has one dotted grey underline, and none inside a link.
- Lists take a 5px grey diamond, fainter when nested, PRTS Design's source-stone motif and the diamond of micfong.space's header. Ordered lists take grey numbers, and a definition list's term is bold.
- Inline code sits between
[and]in the dark rule colour, generated content that is never selected or copied. Each one is an inline block, so a line never breaks inside it unless the code is longer than the line. - Code blocks sit between a top and a bottom rule.
Four background gradients draw the inward ends, the file name sits in the top rule at the left on one line and the language at the right, and the copy and select buttons sit in the bottom rule.
Every frame rule names
div.highlight, since inline code highlighted with#!carries the block's classes. The language label is a short CSS list of the fence languages the pages use, andtextshows none. Highlighted lines take the yellow highlight role, and a code annotation is a still square that turns into a diamond when open. Syntax colours follow the Typst template's TextMate theme, mapped onto the Pygments classes. - Ligatures. The Typst template shows Iosevka Micfong's ligatures, so code turns them on. The modern variant's slight negative letter spacing kept Chrome from applying them, so code and body text set it to zero.
- Tables follow the Typst template: a dark rule above and below, light rules between rows, and a tinted header row closed by a dark rule, with no vertical rules. They sit on the text column, keep the page size inside a callout, and above phone width drop Zensical's 5rem column minimum and never break code inside a cell.
- Callouts follow the Typst panel: a one-pixel rule in the hue's key colour, a title strip in its subtle tint (50 in light, 900 in dark) with the strong text, and the body on the page.
The strip first took the soft tint (100 and 800), which read as a solid slab, most of all in dark.
A collapsed callout ends at its strip, and the body has equal space above and below.
Note and info are blue, tip and success green, question yellow, warning orange, failure, danger and bug red, example purple, and abstract and quote grey.
Danger adds PRTS Design's hazard stripes, and three icons moved to the Typst template's set (
information-outline,lightbulb-on-outline,alert-octagon-outline). The title has a 1.5 line height, so the icon centres on its first line. - Content tabs keep one weight for every label and mark the current one with colour and a 2px orange indicator.
- Buttons are ruled and square. The primary one is orange 500 with black text, as the Typst template's frame cover sets type on a key colour.
- Grid cards are ruled boxes, and a hovered card shows PRTS Design's orange corner triangle.
The whole card is its title's link: the link's
::beforestretches over the card, and the corner mark lets clicks through. - Blockquotes take a one-pixel grey rule, in place of Zensical's 4px bar.
- Corners are square everywhere through one universal
border-radius: 0 !important.
Fonts¶
The three Iosevka Micfong files are subsets of the maintainer's build 34.8.1, made with fonttools 4.60.1:
pyftsubset IosevkaMicfong-Regular.ttf \
--unicodes="U+0000-017F,U+2000-209F,U+2190-21FF,U+2200-22FF,U+2500-25FF" \
--layout-features='calt,liga,kern,ccmp,locl,mark,mkmk' \
--no-hinting --desubroutinize --flavor=woff2 \
--output-file="Iosevka Micfong-Regular.woff2"
The ranges hold Latin, punctuation, arrows, maths and box drawing, which covers every character in the site's code (± · × — … ← → ↔ − ─ │ └ ├ beside ASCII).
Kept with every feature and hinting, a style came to about 220 KB, and these settings bring it to 34 KB (Italic 39 KB).
HarfBuzz confirmed that ->, =>, != and >= still ligate in the subset.
Iosevka's licence names no reserved font name, so the subset keeps its name, and docs/assets/fonts/license-iosevka.txt holds the licence.
| Source file | SHA-256 |
|---|---|
IosevkaMicfong-Regular.ttf |
e4f8316e0839b0f0210bc7e269fab504bb0df219adcdede72170cd937e8fa796 |
IosevkaMicfong-Italic.ttf |
ab1841e05a55bb5c00faf3b2cd1159efea89276794f24fc4e8a738cdea64414d |
IosevkaMicfong-Bold.ttf |
bcfa0f5d0dbc07d5baee3779935f5a64b156de8ff3eb23aea3df033d373a408a |
Second round¶
The maintainer asked for eight changes after looking at the first version:
- A text selection one step darker in light, now solid orange 600.
- No lines at the lower edge of the band in light. Zensical draws an inset shadow along the tabs in the scheme's rule colour, and the grid crossed it.
- One dotted underline on abbreviations. Zensical adds a dotted border, and the browser an underline.
- No orange on headings. The page-title bar and the orange stroke went, and the grey rule under a section stays.
- No lines hanging from the collapsed header. Half of each crosshair hung on its lower edge.
- Legible footer text in light. A Zensical rule at higher specificity coloured it from the scheme's text.
- Grey ordered-list numbers.
- An audit for consistency, and polish where cheap, without the site feeling plastic.
The audit ran as a multi-agent workflow over live pages, with screenshots and computed styles from Playwright driving the installed Chrome. Six lenses each reported findings: colour and accent, typography and rhythm, components, the shell, CSS conflicts with Zensical, and a craft critique. An adversarial verifier per lens reproduced each finding before it counted. 69 of 70 survived, and a planning agent merged them into 23 fixes, 5 deferred items and 7 questions. The fixes include the edit and view buttons, which nothing could click, lists directly in the article that still showed round discs, tables that overhung the text column by 16px, and the drawer's logo, which vanished in light. A second workflow then checked every request and fix by area in both schemes and at phone width, and its verifiers confirmed 18 problems. 16 went in, and the other two are under the issues below.
Review¶
- Screenshots in both schemes at 1512px and 400px of the home page, a guide, an authoring page with definition lists, the tutorial's tabs, callouts and annotations, the command-line reference, an agent record, the search dialog and the navigation drawer.
- Every page in the sitemap, 89 of them, at 400px and 800px: the document is never wider than the viewport.
- Every callout icon on the tutorial page centres within 0.34px of its title's first line.
- Against the stock theme with the two stylesheets disabled, the Game of Life tutorial is 11,112px tall against 11,714px, with 32px sidebar rows against 36px.
- After the second round, four reviewers and their verifiers found every one of the eight requests holding in both schemes, at 1440px and 390px, and at 2x for edges.
uv run zensical buildreports no issues../check.shwas not run, since it builds no documentation and no file it covers changed.
Edited tree¶
.
├── AGENTS.md ~ Documentation site: the stylesheets and the code font
├── zensical.toml ~ mcs.css before henad.css, theme.font off, three callout icons, record #49
└── docs/
├── assets/fonts/
│ ├── Iosevka Micfong-Regular.woff2 +
│ ├── Iosevka Micfong-Italic.woff2 +
│ ├── Iosevka Micfong-Bold.woff2 +
│ └── license-iosevka.txt +
├── stylesheets/
│ ├── mcs.css + the 77 MCS entries as custom properties
│ └── henad.css ~ rewritten: fonts, roles per scheme, band, navigation, content
└── developing/agent-record/
└── 20261009-49-docs-theme.md +
State after¶
The site builds with the new look in both schemes and at phone width, with no request to Google Fonts.
Nothing is committed.
uv run zensical serve shows it locally.
Issues found & future directions¶
- No switch for section numbers. Zensical gives a page no class of its own, so a page cannot opt out of the counter.
Numbering
###headings as well would need the agent records' numbered headings rewritten, or a template override that adds a class. - The language labels are a list. A fence in a new language shows no label until
henad.cssgains a line for it. - Reading progress needs scroll-driven animations, present in Chromium and Safari 26. Firefox shows no line.
- The modern variant's internals are not an interface. The overrides lean on its class names, the
.jstab indicator, the--color-*triplets of the search dialog and the specificity of its per-type callout rules. A Zensical upgrade can move any of them, so an upgrade wants the same screenshot pass. - Ligatures in teaching code.
->and!=render as joined glyphs. The Typst template shows them, but a reader new to Rust might misread them, and one declaration turns them off. - Two code faces. The site sets code in Iosevka Micfong, as the Typst template does, and the app uses Henad Mono, built from IBM Plex Mono.
- The search dialog stays round and frosted. Zensical renders it in an open shadow root that no stylesheet reaches.
Squaring it needs about 15 lines of
extra_javascriptadopting a stylesheet into that root, against minified class names that change between versions. - Long inline code. Inline code is an inline block, so short code never splits, but code longer than the line becomes a full-width box, and punctuation after it starts the next line (1 to 4 places a page at 1440, up to 18 at 390).
Inline code would fix that and split about 170 short spans such as
--lockedafter their hyphen. :has()keeps an icon-less sidebar entry in line, and the crosshairs useround(). A browser without either shows the earlier, slightly misaligned layout.license.htmlis cargo-about's page with its own styling, and keeps it.- Rebuilding the font. A new build of Iosevka Micfong needs the command above run again for each style, and the table of hashes updated.