From b270034d3ad809827e3ffa670004238c36726582 Mon Sep 17 00:00:00 2001 From: Makisuo Date: Fri, 30 Oct 2026 21:52:59 +0100 Subject: [PATCH 1/2] perf(landing): subset Geist and Geist Mono to what the site renders Replaces the 13 @fontsource-variable faces with subsets cut by a script run by hand (apps/landing/scripts/fonts/subset-fonts.py). Per family: a preloaded core with ASCII and common punctuation, an extended file behind unicode-range with Latin-1, Latin Extended-A and the other characters in use, and for Geist Mono a small box-drawing file. The wght axis starts at 400; unused OpenType features are dropped. A typical page now downloads 22.6 KB of fonts instead of 52.5 KB, and 510 of 603 pages need nothing beyond the two preloaded cores. Pixel diffs against a baseline built from the same commit are identical in Chromium on all 27 pages checked, at 1x, 2x and 390px wide. --- apps/landing/scripts/fonts/subset-fonts.py | 470 ++++++++++++++++++ .../landing/src/components/FontPreloads.astro | 10 + apps/landing/src/fonts/geist-core.woff2 | Bin 0 -> 13228 bytes apps/landing/src/fonts/geist-ext.woff2 | Bin 0 -> 14692 bytes apps/landing/src/fonts/geist-mono-box.woff2 | Bin 0 -> 980 bytes apps/landing/src/fonts/geist-mono-core.woff2 | Bin 0 -> 9360 bytes apps/landing/src/fonts/geist-mono-ext.woff2 | Bin 0 -> 12836 bytes apps/landing/src/layouts/DocsLayout.astro | 2 + apps/landing/src/layouts/Layout.astro | 2 + apps/landing/src/styles/fonts.generated.css | 57 +++ apps/landing/src/styles/global.css | 3 +- knip.json | 4 +- 12 files changed, 545 insertions(+), 3 deletions(-) create mode 100755 apps/landing/scripts/fonts/subset-fonts.py create mode 100644 apps/landing/src/components/FontPreloads.astro create mode 100644 apps/landing/src/fonts/geist-core.woff2 create mode 100644 apps/landing/src/fonts/geist-ext.woff2 create mode 100644 apps/landing/src/fonts/geist-mono-box.woff2 create mode 100644 apps/landing/src/fonts/geist-mono-core.woff2 create mode 100644 apps/landing/src/fonts/geist-mono-ext.woff2 create mode 100644 apps/landing/src/styles/fonts.generated.css diff --git a/apps/landing/scripts/fonts/subset-fonts.py b/apps/landing/scripts/fonts/subset-fonts.py new file mode 100755 index 0000000000..792ea2d341 --- /dev/null +++ b/apps/landing/scripts/fonts/subset-fonts.py @@ -0,0 +1,470 @@ +#!/usr/bin/env -S uv run --script +# /// script +# requires-python = ">=3.10" +# dependencies = ["fonttools[woff]==4.60.2", "uharfbuzz==0.57.0"] +# /// +"""Cut Geist and Geist Mono down to what the landing site renders. + +Run by hand from apps/landing, commit the output: + + bun run build && ./scripts/fonts/subset-fonts.py + bun run build && ./scripts/fonts/subset-fonts.py --check # stale? exit 1 + +It reads the built site in dist/ for the characters in use, downloads the +pinned upstream fonts, and writes per family a preloaded core file and an +extended file behind `unicode-range` (plus a box-drawing file for Geist Mono), +into src/fonts/, and the @font-face rules in src/styles/fonts.generated.css. +The build itself does no font work. + +Rerun (`--check` tells you) when: + - content gains a character Geist already drew that the files lack, e.g. + a new symbol or box-drawing character; + - a page starts using a weight outside WGHT, or turns on an OpenType feature + not in FEATURES (`font-variant-numeric`, `font-feature-settings`, ...); + - the source fonts are bumped (update SOURCES and the sha256s). + +Rendering must stay identical, so the script checks its own output: every +kept glyph has the same outline and advance as the source at each weight the +site uses, and every text run in dist/ shapes to the same positions across +the file split as with the whole font. It exits non-zero otherwise. The one +that will trip first is kerning across the split: text like "Té" or "café" +puts the accented letter in another file, and the pair kern is lost. Move the +letter into CORE, or accept the change. +""" + +from __future__ import annotations + +import hashlib +import html.parser +import io +import itertools +import re +import subprocess +import sys +import tempfile +import urllib.request +from dataclasses import dataclass +from pathlib import Path + +import uharfbuzz as hb +from fontTools import subset +from fontTools.pens.recordingPen import DecomposingRecordingPen +from fontTools.ttLib import TTFont +from fontTools.varLib import instancer + +LANDING = Path(__file__).resolve().parents[2] +DIST = LANDING / "dist" +OUT_FONTS = LANDING / "src" / "fonts" +OUT_CSS = LANDING / "src" / "styles" / "fonts.generated.css" +CACHE = LANDING / "node_modules" / ".cache" / "landing-fonts" + + +@dataclass(frozen=True) +class Source: + url: str + sha256: str + + +# Same builds the @fontsource-variable 5.3.0 files were cut from (Geist 1.800, +# Geist Mono 1.701): glyph outlines, deltas and advances match those files. +SOURCES = { + "geist": Source( + "https://raw.githubusercontent.com/google/fonts/87a44ad3d4535f7b3a5a715a08e8944f144afd06/ofl/geist/Geist%5Bwght%5D.ttf", + "73894e0448cae90a92b6c2f8732b7bb9acb7b94c418bff559dad4a18e1de9659", + ), + "geist-mono": Source( + "https://raw.githubusercontent.com/google/fonts/9e25e2ba265e5298f70f6182dd4e8a3ebf1b9123/ofl/geistmono/GeistMono%5Bwght%5D.ttf", + "d00e590b8eb3a59acc329b2d044fd143ae935090b7da33199ebee27cc7de8196", + ), +} + + +def ranges(spec: str) -> frozenset[int]: + out: set[int] = set() + for part in spec.replace("U+", "").split(","): + lo, _, hi = part.strip().partition("-") + out.update(range(int(lo, 16), int(hi or lo, 16) + 1)) + return frozenset(out) + + +# What each family drew before this script existed: the union of the +# @fontsource-variable 5.3.0 faces' unicode-ranges, intersected with the glyphs +# those files carry. Anything else (→, ⌘, ✓, CJK) has always come from a system +# font. Keeping it that way is what keeps rendering identical, so a character +# outside this set is never added, even when Geist has a glyph for it. +_COMMON = ( + "U+0020-007E,U+00A0-00AC,U+00AE-0113,U+0116-012B,U+012E-0131,U+0134-0137,U+0139-013E," + "U+0141-0148,U+014A-014D,U+0150-017E,U+018F,U+0192,U+01A0-01A1,U+01AF-01B0,U+01CD-01CE," + "U+01E4-01E9,U+0218-021B,U+0237,U+0259,U+02B9,U+02BC,U+02C6-02C8,U+02DA,U+02DC-02DD," + "U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0400-045F,U+0462-0463,U+046A-046B," + "U+0472-0475,U+0490-0493,U+0496-0497,U+049A-049B,U+04A2-04A3,U+04AE-04B3,U+04B6-04B7," + "U+04BA-04BB,U+04C0,U+04CF,U+04D8-04D9,U+04E2-04E3,U+04E8-04E9,U+04EE-04EF,U+1E20-1E21," + "U+1E80-1E85,U+1E9E,U+1EA0-1EF9,U+2013-2014,U+2018-201A,U+201C-201E,U+2020,U+2022,U+2026," + "U+2032-2033,U+2039-203A,U+2044,U+20AA,U+20AC,U+20B1,U+20B4,U+20B9,U+20BD,U+2116,U+2122," + "U+2191,U+2193,U+2212,U+A78B-A78C" +) +# Geist Mono lacks U+0132-0133; Geist lacks U+2500-259F (Mono's symbols2 face). +DRAWN_TODAY = { + "geist": ranges(_COMMON) | ranges("U+0132-0133"), + "geist-mono": ranges(_COMMON) | ranges("U+2500-259F"), +} + +# ASCII and the punctuation most pages use. `…` also has to be here for +# `text-overflow: ellipsis`, which draws it without it being in the text. +# `©` and `·` sit in the footer and nav of every page. +CORE = ranges("U+0020-007E") | {0x2013, 0x2014, 0x2018, 0x2019, 0x201C, 0x201D, 0x2026, 0x00A9, 0x00B7} + +# The old faces' unicode-ranges also listed characters their files had no +# glyph for, and browsers still drew them from that face. Chromium drew U+2011 +# (in the pricing headline) as Geist's own hyphen; WebKit picks the face for a +# tab, a newline collapsed to a space, or a zero-width space by the original +# character, then draws it with that face's space. The core maps them to those +# glyphs rather than handing them to a system font. +ALIASES = {0x2011: 0x002D, 0x0009: 0x0020, 0x000A: 0x0020, 0x200B: 0x0020} + +LATIN_1_AND_EXT_A = ranges("U+00A0-017F") + +# Chromium on macOS looks up the glyphs named `X` and `o` in each font file, +# and whether they are there changes how every glyph in that file is +# antialiased at text sizes (seen at 14-16px, gone by 22px). fontsource's latin +# file had both, so the extended file carries an unencoded `X` (it already has +# `o` as a component of ò ó ô). Its symbols2 file had neither, so Geist Mono's +# box drawing gets a file of its own without them. +EXT_GLYPHS = ("X", "o") +BOX_DRAWING = ranges("U+2500-259F") + +# WebKit picks the face for a no-break space by its unicode-range, then draws +# it with that face's U+0020. Without one it borrows a system font's space, +# narrower and with other line metrics. The non-core files carry U+0020 for +# that, encoded but left out of their unicode-range so the core still owns it. +UNLISTED = {0x0020} + +# Default-on features plus `tnum` (Tailwind `tabular-nums`, the only feature +# the site turns on). Dropped: frac/numr/dnom and pnum (never enabled), locl +# (only AZE/CAT/NLD/ROM/TRK-style language systems; pages are en/ja/ko). +FEATURES = ["ccmp", "kern", "liga", "mark", "mkmk", "tnum"] + +# Weights in use, from a computed-style audit of every page: Geist 400, 430, +# 500, 550, 600, 650, 700, 750; Geist Mono 400, 430, 500, 550, 600, 700. Both +# fonts are two masters, 100 and 900, around a 400 default. Starting the axis +# at 400 drops the 100 master outright. The top stays at 900: cutting it to +# 750/700 removes no master, saves almost nothing, and rescales every delta, +# which rounds outlines off by up to half a unit. +WGHT = {"geist": (400, 900), "geist-mono": (400, 900)} +CHECK_WEIGHTS = {"geist": (400, 430, 500, 550, 600, 650, 700, 750, 900), "geist-mono": (400, 430, 500, 550, 600, 700, 900)} + + +@dataclass(frozen=True) +class Family: + slug: str + css_family: str + + +FAMILIES = (Family("geist", "Geist Variable"), Family("geist-mono", "Geist Mono Variable")) + + +def fetch(slug: str) -> bytes: + src = SOURCES[slug] + path = CACHE / f"{slug}-{src.sha256[:12]}.ttf" + if not path.exists(): + CACHE.mkdir(parents=True, exist_ok=True) + with urllib.request.urlopen(src.url) as res: + path.write_bytes(res.read()) + data = path.read_bytes() + if hashlib.sha256(data).hexdigest() != src.sha256: + path.unlink() + sys.exit(f"{slug}: sha256 mismatch for {src.url}") + return data + + +class TextCollector(html.parser.HTMLParser): + """Text content of a page, minus scripts and styles, plus input text.""" + + SKIP = {"script", "style", "template"} + + def __init__(self) -> None: + super().__init__(convert_charrefs=True) + self.depth = 0 + self.runs: list[str] = [] + + def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: + if tag in self.SKIP: + self.depth += 1 + for name, value in attrs: + if value and name in ("placeholder", "value"): + self.runs.append(value) + + def handle_endtag(self, tag: str) -> None: + if tag in self.SKIP and self.depth: + self.depth -= 1 + + def handle_data(self, data: str) -> None: + if not self.depth and data.strip(): + self.runs.append(data) + + +ESCAPE = re.compile(r"\\u\{?([0-9a-fA-F]{4,6})\}?") + + +def content() -> tuple[set[int], list[str]]: + """Every codepoint in dist/, and the HTML text runs for the shaping check. + + Scripts count too, for text the islands render on the client. That reaches + library strings as well, but only characters a family drew already can get + in, so the cost is a few glyphs at most. + """ + if not (DIST / "index.html").exists(): + sys.exit("dist/ is missing: run `bun run build` first") + cps: set[int] = set() + runs: list[str] = [] + for page in sorted(DIST.rglob("*.html")): + parser = TextCollector() + parser.feed(page.read_text("utf-8")) + runs.extend(parser.runs) + for path in [*DIST.rglob("*.js"), *DIST.rglob("*.css")]: + text = path.read_text("utf-8", errors="ignore") + cps.update(ord(c) for c in text) + cps.update(int(m, 16) for m in ESCAPE.findall(text)) + for run in runs: + cps.update(ord(c) for c in run) + # text-transform can draw the other case. + for cp in list(cps): + for c in (chr(cp).upper(), chr(cp).lower()): + if len(c) == 1: + cps.add(ord(c)) + return cps, list(dict.fromkeys(runs)) + + +def check_license(font: TTFont, slug: str) -> None: + for name_id in (0, 13): + text = font["name"].getDebugName(name_id) or "" + if "reserved font name" in text.lower(): + sys.exit(f"{slug}: declares a Reserved Font Name; subsets must be renamed before shipping") + + +def build(source: bytes, unicodes: set[int], wght: tuple[int, int], glyphs: tuple[str, ...] = ()) -> TTFont: + font = TTFont(io.BytesIO(source), recalcTimestamp=False) + options = subset.Options() + options.layout_features = FEATURES + # The only "hinting" here is gftools' unhinted-font fix (a 7-byte prep and + # a gasp table); no glyph has instructions. Windows uses them for + # smoothing, so they stay. + options.hinting = True + options.drop_tables += ["meta"] + # Chromium on macOS antialiases small text differently when the post table + # has no glyph names, or different ones (seen at 16px, not at 28px). The + # names cost ~0.5 KB a file and are what keeps the pixels identical. + options.glyph_names = True + options.recalc_timestamp = False + # Otherwise `›` pulls in `‹` and `«` pulls in `»`, for right-to-left + # mirroring that no page here needs. + options.bidi_closure = False + subsetter = subset.Subsetter(options) + subsetter.populate(unicodes=unicodes, glyphs=glyphs) + subsetter.subset(font) + for table in font["cmap"].tables: + # Extra glyphs come back encoded; they stay in the file, not the cmap. + table.cmap = {cp: glyph for cp, glyph in table.cmap.items() if cp in unicodes} + for alias, target in ALIASES.items(): + if alias in unicodes and target in table.cmap: + table.cmap[alias] = table.cmap[target] + # The instancer bakes the deltas the source leaves to interpolation (IUP) + # into rounded integers, moving points by a fraction of a unit. The default + # stays at 400, so the upper master's normalized coordinates don't change: + # put its original tuples back and only drop the ones below the default. + tuples = {glyph: list(variations) for glyph, variations in font["gvar"].variations.items()} + font = instancer.instantiateVariableFont(font, {"wght": wght}, optimize=False) + if wght[0] != 400: + sys.exit("restoring gvar tuples assumes the axis keeps its 400 default") + font["gvar"].variations = { + glyph: [v for v in variations if v.axes["wght"][1] > 0] for glyph, variations in tuples.items() + } + # Keep the full font's bounding box, as the fontsource files did: a box + # recomputed from the kept glyphs also shifts antialiasing on macOS. + source_head = TTFont(io.BytesIO(source))["head"] + for key in ("xMin", "yMin", "xMax", "yMax"): + setattr(font["head"], key, getattr(source_head, key)) + font.recalcBBoxes = False + font.flavor = "woff2" + return font + + +def reload(font: TTFont) -> tuple[TTFont, bytes, bytes]: + """The font as written, its WOFF2 bytes, and the plain sfnt that HarfBuzz + can read (it treats WOFF2 bytes as an empty face).""" + buf = io.BytesIO() + font.save(buf) + data = buf.getvalue() + written = TTFont(io.BytesIO(data), recalcTimestamp=False, recalcBBoxes=False) + sfnt = io.BytesIO() + written.flavor = None + written.save(sfnt) + return TTFont(io.BytesIO(data), recalcTimestamp=False, recalcBBoxes=False), data, sfnt.getvalue() + + +def unicode_range(cps: set[int]) -> str: + cps_sorted, out, i = sorted(cps), [], 0 + while i < len(cps_sorted): + j = i + while j + 1 < len(cps_sorted) and cps_sorted[j + 1] == cps_sorted[j] + 1: + j += 1 + lo, hi = cps_sorted[i], cps_sorted[j] + out.append(f"U+{lo:04X}" if lo == hi else f"U+{lo:04X}-{hi:04X}") + i = j + 1 + return ",".join(out) + + +def outlines(font: TTFont, weight: int, cps: set[int]) -> dict[int, tuple]: + cmap = font.getBestCmap() + glyphs = font.getGlyphSet(location={"wght": weight}, normalized=False) + out = {} + for cp in cps: + glyph = glyphs[cmap.get(cp) or cmap[ALIASES[cp]]] + pen = DecomposingRecordingPen(glyphs) + glyph.draw(pen) + out[cp] = (glyph.width, tuple(pen.value)) + return out + + +class Shaper: + def __init__(self, data: bytes) -> None: + self.face = hb.Face(data) + self.fonts: dict[int, hb.Font] = {} + + def __call__(self, text: str, weight: int, features: dict[str, bool]) -> list[tuple[int, int, int, int]]: + if weight not in self.fonts: + self.fonts[weight] = hb.Font(self.face) + self.fonts[weight].set_variations({"wght": weight}) + buf = hb.Buffer() + buf.add_codepoints([ord(c) for c in text]) + buf.guess_segment_properties() + hb.shape(self.fonts[weight], buf, features) + return [(i.cluster, p.x_advance, p.x_offset, p.y_offset) for i, p in zip(buf.glyph_infos, buf.glyph_positions)] + + +def check_shaping(source: bytes, files: list[tuple[bytes, set[int]]], runs: list[str], slug: str) -> tuple[list[str], int]: + """Shape each text run as the browser does, split by which file covers each + character, and compare it with the source font shaping the same run whole. + + Characters no file has come from a fallback font before and after, so they + end a run. Returns the differing runs and how many runs were checked.""" + shape_source = Shaper(source) + shapers = [(Shaper(data), cps) for data, cps in files] + owner = {cp: shaper for shaper, cps in shapers for cp in cps} + # Browsers collapse tabs and newlines to spaces, and HarfBuzz hides a + # zero-width space whether or not the font maps it: only U+2011 shapes as + # its alias. + collapsed = (re.sub(r"[\t\n]+", " ", run) for run in runs) + pieces = {p for run in collapsed for p in re.split(f"[^{''.join(re.escape(chr(c)) for c in sorted(owner))}]+", run) if p.strip()} + problems = [] + for piece in sorted(pieces): + segments = ["".join(g) for _, g in itertools.groupby(piece, lambda c: id(owner[ord(c)]))] + for weight in (400, 600): + for features in ({}, {"tnum": True}): + want = shape_source(piece.translate({0x2011: 0x002D}), weight, features) + got, offset = [], 0 + for seg in segments: + got += [(c + offset, *rest) for c, *rest in owner[ord(seg[0])](seg, weight, features)] + offset += len(seg) + if want != got: + problems.append(f"{slug} wght {weight} {features or ''}: {piece.strip()[:80]!r}") + return problems, len(pieces) + + +def formatted(css: str) -> str: + """The CSS as the repo's oxfmt leaves it, so a rerun is not a diff.""" + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "fonts.css" + path.write_text(css) + subprocess.run([LANDING.parents[1] / "node_modules" / ".bin" / "oxfmt", path], check=True, capture_output=True) + return path.read_text() + + +def main() -> None: + check = "--check" in sys.argv[1:] + outputs: dict[Path, bytes] = {} + used, runs = content() + css = [ + "/* Generated by scripts/fonts/subset-fonts.py. Do not edit; rerun the script.", + " Core files hold ASCII and common punctuation and are preloaded (FontPreloads.astro);", + " the others only download on pages that use one of their characters. `no-inline`", + " keeps Vite from inlining a small file into the CSS every page loads. */", + ] + failures: list[str] = [] + OUT_FONTS.mkdir(parents=True, exist_ok=True) + for family in FAMILIES: + source = fetch(family.slug) + src_font = TTFont(io.BytesIO(source)) + check_license(src_font, family.slug) + has = set(src_font.getBestCmap()) & DRAWN_TODAY[family.slug] + core_cps = (CORE & has) | {alias for alias, target in ALIASES.items() if target in CORE & has} + box_cps = BOX_DRAWING & used & has + ext_cps = ((LATIN_1_AND_EXT_A | used) & has) - core_cps - box_cps + parts = [("core", core_cps, ()), ("ext", ext_cps, EXT_GLYPHS), ("box", box_cps, ())] + + files = {} + for part, cps, glyphs in (p for p in parts if p[1]): + unlisted = UNLISTED - cps if part != "core" else set() + font, data, sfnt = reload(build(source, cps | unlisted, WGHT[family.slug], glyphs)) + got = set(font.getBestCmap()) + if got != cps | unlisted: + failures.append(f"{family.slug}-{part}: cmap differs from plan: {sorted(got ^ (cps | unlisted))}") + got -= unlisted + has_context = [g in font.getGlyphOrder() for g in EXT_GLYPHS] + if has_context != [part != "box"] * len(EXT_GLYPHS): + failures.append(f"{family.slug}-{part}: glyphs {EXT_GLYPHS} present: {has_context}") + if font["post"].formatType != 2.0 or font.getGlyphOrder() != [g for g in src_font.getGlyphOrder() if g in set(font.getGlyphOrder())]: + failures.append(f"{family.slug}-{part}: glyph names were not kept") + if [getattr(font["head"], k) for k in ("xMin", "yMin", "xMax", "yMax")] != [getattr(src_font["head"], k) for k in ("xMin", "yMin", "xMax", "yMax")]: + failures.append(f"{family.slug}-{part}: head bounding box differs from the source") + axis = next(a for a in font["fvar"].axes if a.axisTag == "wght") + if (axis.minValue, axis.maxValue) != WGHT[family.slug]: + failures.append(f"{family.slug}-{part}: wght axis is {axis.minValue}-{axis.maxValue}") + for weight in CHECK_WEIGHTS[family.slug]: + if outlines(font, weight, got) != outlines(src_font, weight, got): + failures.append(f"{family.slug}-{part}: outlines or advances differ from the source at wght {weight}") + path = OUT_FONTS / f"{family.slug}-{part}.woff2" + outputs[path] = data + files[part] = (sfnt, got) + features = sorted({r.FeatureTag for t in ("GSUB", "GPOS") if t in font for r in font[t].table.FeatureList.FeatureRecord}) + print(f"{path.relative_to(LANDING)}: {len(data):,} B, {len(got)} codepoints, {font['maxp'].numGlyphs} glyphs, features {' '.join(features)}, tables {' '.join(sorted(font.keys()))}") + css += [ + "", + "@font-face {", + f'\tfont-family: "{family.css_family}";', + "\tfont-style: normal;", + "\tfont-display: swap;", + f"\tfont-weight: {WGHT[family.slug][0]} {WGHT[family.slug][1]};", + f'\tsrc: url("../fonts/{path.name}?no-inline") format("woff2-variations");', + f"\tunicode-range: {unicode_range(got)};", + "}", + ] + problems, checked = check_shaping(source, list(files.values()), runs, family.slug) + print(f" {family.slug}: {checked:,} text runs shaped, {len(problems)} differ from the source font") + failures += problems + + skipped = sorted(cp for cp in used & set(src_font.getBestCmap()) if cp not in DRAWN_TODAY[family.slug] and cp > 0x7E) + if skipped: + print(f" {family.slug}: has glyphs for, but never drew, {' '.join(chr(c) for c in skipped)} (left to fallback fonts)") + + outputs[OUT_CSS] = formatted("\n".join(css) + "\n").encode() + committed = {p for p in OUT_FONTS.glob("*.woff2")} | {OUT_CSS} + if check: + stale = sorted(str(p.relative_to(LANDING)) for p in committed | set(outputs) if not p.exists() or p not in outputs or p.read_bytes() != outputs[p]) + print("committed fonts are current" if not stale else f"stale, rerun without --check: {' '.join(stale)}") + failures += [f"stale: {p}" for p in stale] + else: + for path in committed - set(outputs): + path.unlink() + for path, data in outputs.items(): + path.write_bytes(data) + print(f"wrote {len(outputs)} files") + if failures: + print("\nFAILED:", *failures[:40], sep="\n ") + if len(failures) > 40: + print(f" ... and {len(failures) - 40} more") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/apps/landing/src/components/FontPreloads.astro b/apps/landing/src/components/FontPreloads.astro new file mode 100644 index 0000000000..dd7b5b5f5f --- /dev/null +++ b/apps/landing/src/components/FontPreloads.astro @@ -0,0 +1,10 @@ +--- +// The core subsets from scripts/fonts/subset-fonts.py. Every page draws both +// families (Geist Mono is the body face), so both are preloaded; the extended +// files stay behind their unicode-range. +import geistCore from "../fonts/geist-core.woff2?url&no-inline"; +import geistMonoCore from "../fonts/geist-mono-core.woff2?url&no-inline"; +--- + + + diff --git a/apps/landing/src/fonts/geist-core.woff2 b/apps/landing/src/fonts/geist-core.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e336e219522f6bf80156649eefd8a77a77ee00ca GIT binary patch literal 13228 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 eH7?O~QHVAR+5=(#@j!3nryL)BH8DvJeB{;nR literal 0 HcmV?d00001 diff --git a/apps/landing/src/fonts/geist-ext.woff2 b/apps/landing/src/fonts/geist-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..89effaa487ce1f2782bd512a4b444209bfeab27f GIT binary patch literal 14692 zcmV-qIh)3JPew8T0RR9106Am;6aWAK0D_DF066df0nE(+00000000000000000000 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 _#;* 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 Cc%zK~ literal 0 HcmV?d00001 diff --git a/apps/landing/src/fonts/geist-mono-core.woff2 b/apps/landing/src/fonts/geist-mono-core.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..33ffe372e236824756cfa43f68d242c118de36db GIT binary patch literal 9360 zcmV;BByZbyPew8T0RR9103?tA6aWAK06-7`03;y*0kd@g00000000000000000000 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