FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

docs: self-host fonts, eliminate layout shift, add SPA navigation by tony · Pull Request #1022 · tmux-python/tmuxp · GitHub

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .css  (1) .html  (3) .js  (1) .md  (4) .py  (2) .yml  (1) dotfile  (1) All 7 file types selected
Only manifest files
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
10 changes: 10 additions & 0 deletions .github/workflows/docs.yml
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- master
- docs-fonts

permissions:
contents: read
Expand Down Expand Up @@ -62,6 +63,15 @@ jobs:
python -V
uv run python -V

- name: Cache sphinx fonts
if: env.PUBLISH == 'true'
uses: actions/cache@v5
with:
path: ~/.cache/sphinx-fonts
key: sphinx-fonts-${{ hashFiles('docs/conf.py') }}
restore-keys: |
sphinx-fonts-

- name: Build documentation
if: env.PUBLISH == 'true'
run: |
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
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
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ doc/_build/
# MonkeyType
monkeytype.sqlite3

# Generated by sphinx_fonts extension (downloaded at build time)
docs/_static/fonts/
docs/_static/css/fonts.css

# Claude code
**/CLAUDE.local.md
**/CLAUDE.*.md
Expand Down
148 changes: 148 additions & 0 deletions docs/_ext/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
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
"""Sphinx extension for self-hosted fonts via Fontsource CDN.

Downloads font files at build time, caches them locally, and passes
structured font data to the template context for inline @font-face CSS.
"""

from __future__ import annotations

import logging
import pathlib
import shutil
import typing as t
import urllib.error
import urllib.request

if t.TYPE_CHECKING:
from sphinx.application import Sphinx

logger = logging.getLogger(__name__)

CDN_TEMPLATE = (
"https://cdn.jsdelivr.net/npm/{package}@{version}"
"/files/{font_id}-{subset}-{weight}-{style}.woff2"
)


class SetupDict(t.TypedDict):
version: str
parallel_read_safe: bool
parallel_write_safe: bool


def _cache_dir() -> pathlib.Path:
return pathlib.Path.home() / ".cache" / "sphinx-fonts"


def _cdn_url(
package: str,
version: str,
font_id: str,
subset: str,
weight: int,
style: str,
) -> str:
return CDN_TEMPLATE.format(
package=package,
version=version,
font_id=font_id,
subset=subset,
weight=weight,
style=style,
)


def _download_font(url: str, dest: pathlib.Path) -> bool:
if dest.exists():
logger.debug("font cached: %s", dest.name)
return True
dest.parent.mkdir(parents=True, exist_ok=True)
try:
urllib.request.urlretrieve(url, dest)
logger.info("downloaded font: %s", dest.name)
except (urllib.error.URLError, OSError):
logger.warning("failed to download font: %s", url)
return False
return True


def _on_builder_inited(app: Sphinx) -> None:
if app.builder.format != "html":
return

fonts: list[dict[str, t.Any]] = app.config.sphinx_fonts
variables: dict[str, str] = app.config.sphinx_font_css_variables
if not fonts:
return

cache = _cache_dir()
static_dir = pathlib.Path(app.outdir) / "_static"
fonts_dir = static_dir / "fonts"
fonts_dir.mkdir(parents=True, exist_ok=True)

font_faces: list[dict[str, str]] = []
for font in fonts:
font_id = font["package"].split("/")[-1]
version = font["version"]
package = font["package"]
subset = font.get("subset", "latin")
for weight in font["weights"]:
for style in font["styles"]:
filename = f"{font_id}-{subset}-{weight}-{style}.woff2"
cached = cache / filename
url = _cdn_url(package, version, font_id, subset, weight, style)
if _download_font(url, cached):
shutil.copy2(cached, fonts_dir / filename)
font_faces.append(
{
"family": font["family"],
"style": style,
"weight": str(weight),
"filename": filename,
}
)

preload_hrefs: list[str] = []
preload_specs: list[tuple[str, int, str]] = app.config.sphinx_font_preload
for family_name, weight, style in preload_specs:
for font in fonts:
if font["family"] == family_name:
font_id = font["package"].split("/")[-1]
subset = font.get("subset", "latin")
filename = f"{font_id}-{subset}-{weight}-{style}.woff2"
preload_hrefs.append(filename)
break

fallbacks: list[dict[str, str]] = app.config.sphinx_font_fallbacks

app._font_preload_hrefs = preload_hrefs # type: ignore[attr-defined]
app._font_faces = font_faces # type: ignore[attr-defined]
app._font_fallbacks = fallbacks # type: ignore[attr-defined]
app._font_css_variables = variables # type: ignore[attr-defined]


def _on_html_page_context(
app: Sphinx,
pagename: str,
templatename: str,
context: dict[str, t.Any],
doctree: t.Any,
) -> None:
context["font_preload_hrefs"] = getattr(app, "_font_preload_hrefs", [])
context["font_faces"] = getattr(app, "_font_faces", [])
context["font_fallbacks"] = getattr(app, "_font_fallbacks", [])
context["font_css_variables"] = getattr(app, "_font_css_variables", {})


def setup(app: Sphinx) -> SetupDict:
app.add_config_value("sphinx_fonts", [], "html")
app.add_config_value("sphinx_font_fallbacks", [], "html")
app.add_config_value("sphinx_font_css_variables", {}, "html")
app.add_config_value("sphinx_font_preload", [], "html")
app.connect("builder-inited", _on_builder_inited)
app.connect("html-page-context", _on_html_page_context)
return {
"version": "1.0",
"parallel_read_safe": True,
"parallel_write_safe": True,
}
79 changes: 79 additions & 0 deletions docs/_static/css/custom.css
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
margin-right: calc(var(--sidebar-item-spacing-horizontal) / 2.5);
}

#sidebar-projects:not(.ready) {
visibility: hidden;
}

.sidebar-tree .active {
font-weight: bold;
}
Expand Down Expand Up @@ -157,11 +161,86 @@ article h6 {
/* ── Body typography refinements ────────────────────────────
* Improve paragraph readability with wider line-height and
* sharper text rendering. Furo already sets font-smoothing.
*
* IBM Plex tracks slightly wide at default spacing; -0.01em
* tightens it to feel more natural (matches tony.sh/tony.nl).
* Kerning + ligatures polish AV/To pairs and fi/fl combos.
* ────────────────────────────────────────────────────────── */
body {
text-rendering: optimizeLegibility;
font-kerning: normal;
font-variant-ligatures: common-ligatures;
letter-spacing: -0.01em;
}

/* ── Code block text rendering ────────────────────────────
* Monospace needs fixed-width columns: disable kerning,
* ligatures, and letter-spacing that body sets for prose.
* optimizeSpeed skips heuristics that can shift the grid.
* ────────────────────────────────────────────────────────── */
pre,
code,
kbd,
samp {
text-rendering: optimizeSpeed;
font-kerning: none;
font-variant-ligatures: none;
letter-spacing: normal;
}

article {
line-height: 1.6;
}

/* ── Image layout shift prevention ────────────────────────
* Reserve space for images before they load. Furo already
* sets max-width: 100%; height: auto on img. We add
* content-visibility and badge-specific height to prevent CLS.
* ────────────────────────────────────────────────────────── */

img {
content-visibility: auto;
}

/* Docutils emits :width:/:height: as inline CSS (style="width: Xpx;
* height: Ypx;") rather than HTML attributes. When Furo's
* max-width: 100% constrains width below the declared value,
* the fixed height causes distortion. height: auto + aspect-ratio
* lets the browser compute the correct height from the intrinsic
* ratio once loaded; before load, aspect-ratio reserves space
* at the intended proportion — preventing both CLS and distortion. */
article img[loading="lazy"] {
height: auto !important;
}

/* Per-image aspect ratios for CLS reservation before load */
img[src*="tmuxp-demo"] {
aspect-ratio: 888 / 589;
}

img[src*="tmuxp-shell"] {
aspect-ratio: 878 / 109;
}

img[src*="tmuxp-dev-screenshot"] {
aspect-ratio: 1030 / 605;
}

img[src*="shields.io"],
img[src*="badge.svg"],
img[src*="codecov.io"] {
height: 20px;
width: auto;
min-width: 60px;
border-radius: 3px;
background: var(--color-background-secondary);
}

/* ── View Transitions (SPA navigation) ────────────────────
* Crossfade between pages during SPA navigation.
* Browsers without View Transitions API get instant swap.
* ────────────────────────────────────────────────────────── */
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 150ms;
}
Loading
Loading

Back | FazBrowse Home | New Git URL