From e2ec7fb0d3eba2f94a5ab2039e5739845c489786 Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Wed, 21 Oct 2026 18:07:11 +0300 Subject: [PATCH] perf(theme): send font preloads as a Link header for Early Hints Same as cloudhal-dev/cloudhal#56: pages send their pairing's font preloads as a Link response header, which Cloudflare turns into a 103 Early Hints response when the zone has Early Hints on. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- src/fonts.ts | 10 ++++++++++ src/pages/[...path].astro | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/src/fonts.ts b/src/fonts.ts index a48cca9..9d98212 100644 --- a/src/fonts.ts +++ b/src/fonts.ts @@ -19,3 +19,13 @@ export const PAIRING_PRELOADS: Readonly> = { ], classic: ["/fonts/v1/source-serif-4-latin-wght-normal.woff2"], }; + +/** + * The same preloads as a `Link` response header. On a Cloudflare zone with Early Hints on, the + * edge sends it as a 103 before the page, so the fonts load while the Worker is still rendering. + */ +export function fontLinkHeader(hrefs: readonly string[]): string { + return hrefs + .map((h) => `<${h}>; rel=preload; as=font; type="font/woff2"; crossorigin`) + .join(", "); +} diff --git a/src/pages/[...path].astro b/src/pages/[...path].astro index 990f859..53216e2 100644 --- a/src/pages/[...path].astro +++ b/src/pages/[...path].astro @@ -51,6 +51,7 @@ import { relatedEntries, termAncestors, } from "@cloudhal/astro-renderer/taxonomy"; +import { themeVariables } from "@cloudhal/astro-renderer/theme-settings"; import type { ContentEntry } from "@cloudhal/core-contracts"; /** * One route for the whole graph (RFC-0002 §7 order): a Page answers at its `path`; an article at @@ -73,8 +74,10 @@ import NotFound from "../components/NotFound.astro"; import PostTerms from "../components/PostTerms.astro"; import RelatedPosts from "../components/RelatedPosts.astro"; import Search from "../components/Search.astro"; +import { fontLinkHeader, PAIRING_PRELOADS } from "../fonts.ts"; import { iconSet } from "../icons.ts"; import Base from "../layouts/Base.astro"; +import { themeSettings } from "../settings.ts"; import { ARCHIVE_PAGE_SIZE, blocks, @@ -354,6 +357,10 @@ const localized = localizeHead(head, site, { }); const meta = fallback || notFound ? { ...localized, robots: "noindex,follow" } : localized; Astro.response.headers.set("content-language", shownIn); +// The pairing's fonts as a Link header too (Early Hints), not only as in . +const pairing = themeVariables(site, themeSettings).attributes["data-ch-type-pairing"]; +const preloads = PAIRING_PRELOADS[pairing ?? ""] ?? []; +if (preloads.length) Astro.response.headers.set("link", fontLinkHeader(preloads)); // The reader's notice (RFC-0033 §4): machine output until a person reviews it (or always, when // the site asks), a page shown in its original language, each with a link to the original. const original = alternates.find((a) => a.original && !a.current);