docs: self-host fonts, eliminate layout shift, add SPA navigation#50
Merged
docs: self-host fonts, eliminate layout shift, add SPA navigation#50
Conversation
why: Furo's default TOC title (10px) is nearly invisible and smaller than its own items (12px), inverting typographic hierarchy. Body line-height (1.5) is tighter than WCAG-recommended range. what: - Bump TOC item size 75% → 81.25% (12→13px) via --toc-font-size - Bump TOC title size 62.5% → 87.5% (10→14px) via --toc-title-font-size - Increase .toc-tree line-height 1.3 → 1.4 for wrapped entries - Increase article line-height 1.5 → 1.6 for paragraph readability - Enable text-rendering: optimizeLegibility on body
why: Furo hardcodes .toc-drawer to 15em; long TOC entries overflow and code blocks in the content area are cramped. what: - Override .toc-drawer min-width to 18em with flex-shrink: 0 - Move padding to .toc-sticky inner panel (1.5em right) - Set .content to flex: 1 1 46em with max-width: 46em - Override right offset at ≤82em breakpoint for collapse
why: 81.25% (13px) is still noticeably smaller than the 14px body text; at 87.5% (14px) the TOC matches body size and reads comfortably beside it. what: - Change --toc-font-size from --font-size--small--2 to --font-size--small - Move variables from :root to body for specificity with Furo
…brow labels why: Furo headings are large and bold, crowding the page and flattening visual hierarchy. Biome-inspired medium-weight scale uses size and spacing — not boldness — to convey structure. what: - Set all article headings to font-weight: 500 - Scale: h1 1.8em, h2 1.6em, h3 1.15em, h4-h6 eyebrow treatment - Add uppercase + letter-spacing + muted color for h4-h6 - Add changelog heading extras for #history section - Revert TOC variables from body back to :root
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #50 +/- ##
==========================================
+ Coverage 83.71% 85.52% +1.80%
==========================================
Files 25 27 +2
Lines 2235 2528 +293
==========================================
+ Hits 1871 2162 +291
- Misses 364 366 +2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Member
Author
Code reviewFound 1 issue:
Lines 57 to 68 in d8fe69a 🤖 Generated with Claude Code |
why: Standardize on IBM Plex Sans / Mono across projects without committing ~227KB of binary font files to the repo. what: - Add sphinx_fonts extension that downloads fonts at build time, caches in ~/.cache/sphinx-fonts/, and generates @font-face CSS - Configure IBM Plex Sans (400/500/600/700) and IBM Plex Mono (400) with CSS variable overrides for Furo theme - Add actions/cache step in docs workflow for font cache persistence - Gitignore generated font assets in docs/_static/
why: Furo sets --font-stack on body, so :root declarations lose in specificity. Using body selector matches Furo's own pattern. what: - Change CSS variable container from :root to body in _generate_css
why: The browser doesn't discover font URLs until it parses fonts.css, which itself must wait for the HTML to load. Preload hints in <head> tell the browser to start downloading fonts immediately. what: - Add sphinx_font_preload config option to sphinx_fonts extension - Emit <link rel="preload"> tags in page.html template's extrahead block - Preload 3 critical weights: Sans 400/700, Mono 400 - Rename layout.html → page.html (Furo extends !page.html)
why: IBM Plex Sans benefits from OpenType features that browsers disable by default; monospace blocks need opposite treatment. what: - Add font-kerning, font-variant-ligatures, letter-spacing to body - Add optimizeSpeed and disable ligatures for pre/code/kbd/samp
why: Every <img> on the docs site lacked dimension hints, causing Cumulative Layout Shift (CLS) on page load. what: - Add content-visibility: auto on all img for off-screen decode skip - Add height: auto !important for lazy-loaded images with aspect-ratio - Add CSS height: 20px for shields.io / badge / codecov badges - Add sidebar/brand.html with width/height/decoding on logo
why: Every page navigation re-downloads and re-parses CSS/JS/fonts and re-renders the entire layout. Only article content, TOC, and active sidebar link actually change between pages. what: - Create docs/_static/js/spa-nav.js (~170 lines, vanilla JS, no deps) - Intercept internal link clicks and swap three DOM regions - Preserve sidebar scroll, theme state, all CSS/JS/fonts - Register in conf.py setup() with loading_method="defer"
…flow why: When web fonts load, text reflowed because system fallbacks have different metrics. Capsize-derived overrides make fallback fonts match IBM Plex dimensions exactly. what: - Add sphinx_font_fallbacks config with size-adjust/ascent/descent overrides for Arial (sans) and Courier New (mono) - Update font stacks to include fallback font families
why: Badges from shields.io/codecov render at 0x0 until loaded, causing visible layout shift in the header area. what: - Add min-width: 60px, border-radius, and background placeholder to badge selectors for stable pre-load dimensions
why: All links render visibly then JS replaces the hostname-matching link with a bold span, causing a visible reflow. what: - Remove misleading class="current" from all project links - Hide #sidebar-projects until JS resolves active state (.ready) - Use textContent instead of innerHTML for safer DOM manipulation
…sfade why: SPA navigation instantly replaces DOM content, causing a jarring visual jump between pages instead of a smooth transition. what: - Wrap swap+reinit in document.startViewTransition() when available - Add 150ms crossfade animation via ::view-transition pseudo-elements - Progressive enhancement: unsupported browsers get instant swap
why: View transitions are not image-related — grouping them with image CLS rules is misleading. Moving to end of file keeps related sections together and matches the logical reading order. what: - Move view transitions CSS block after badge placeholder rules
why: font-display swap causes visible text reflow (FOUT). Matching the tony.nl/cv approach: block rendering until preloaded fonts arrive, and inline the @font-face CSS to eliminate the extra fonts.css request. what: - Change font-display from swap to block - Move @font-face CSS from external fonts.css to inline <style> in <head> - Use pathto() in template for correct relative font URLs - Remove _generate_css() function (CSS now generated in Jinja template)
why: ruff D101/D103 rules flag missing docstrings on SetupDict and setup(), causing CI lint failures in repos that lint docs/_ext/. what: - Add docstring to SetupDict TypedDict class - Add docstring to setup() function
why: codecov drops because docs/_ext/sphinx_fonts.py is measured for coverage but has zero tests across all repos. what: - Add test_sphinx_fonts.py with 21 tests covering all functions - Test pure functions, I/O with monkeypatch, Sphinx events with SimpleNamespace - Cover all branches: cached/success/URLError/OSError, html/non-html, empty/with fonts
why: CI ruff check fails on EM101 (string literal in exception), TRY003 (long exception message), and D403 (uncapitalized docstring). what: - Extract exception messages to variables for EM101/TRY003 - Capitalize docstrings starting with "setup" for D403
why: CI ruff format check fails on multi-line function call formatting. what: - Apply ruff format to test_sphinx_fonts.py
why: CI mypy fails with unused-ignore because sphinx_fonts is imported as an untyped module via sys.path, making arg-type suppression unnecessary. what: - Remove all type: ignore[arg-type] comments from test_sphinx_fonts.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port documentation frontend improvements from tmuxp:
Overhaul the docs frontend for faster, smoother page loads:
See tmuxp PRs for full design rationale and test plan.