From 6e0e76a5a500faacefaf951177d649b7e33863f1 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Thu, 26 Mar 2026 18:17:26 +0100 Subject: [PATCH 1/4] chore: update regex --- vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vercel.json b/vercel.json index dc7d3aa89..cce01d1bd 100644 --- a/vercel.json +++ b/vercel.json @@ -1235,7 +1235,7 @@ ], "headers": [ { - "source": "/docs/(assets)/(.*)", + "source": "/docs/assets/(.*)", "headers": [ { "key": "Vercel-CDN-Cache-Control", From 5b68ee9ec9d44b26d96d294d58e2723d08ee6d14 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:28:27 +0100 Subject: [PATCH 2/4] perf: split DocSearch CSS into lazy-loaded separate file Extracts ~30KB of DocSearch CSS from the main stylesheet into a separate file (docsearch..css) that is only loaded when the user first interacts with the search button (mouseover/focus/click). Reduces inlined CSS from ~132KB to ~102KB. Co-Authored-By: Claude Sonnet 4.6 --- src/plugins/preload-css.js | 75 +++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 2 deletions(-) diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js index 2a8f909aa..7992e3a96 100644 --- a/src/plugins/preload-css.js +++ b/src/plugins/preload-css.js @@ -4,6 +4,49 @@ const fs = require("fs") const path = require("path") +/** + * Split minified CSS into top-level rule blocks, handling nested braces + * (e.g. @media queries containing inner rules). + */ +function splitCssBlocks(css) { + const blocks = [] + let i = 0 + while (i < css.length) { + const braceStart = css.indexOf("{", i) + if (braceStart === -1) { + if (i < css.length) blocks.push(css.slice(i)) + break + } + let depth = 0 + let j = braceStart + while (j < css.length) { + if (css[j] === "{") depth++ + else if (css[j] === "}") { + depth-- + if (depth === 0) { + blocks.push(css.slice(i, j + 1)) + i = j + 1 + break + } + } + j++ + } + if (j >= css.length) { + blocks.push(css.slice(i)) + break + } + } + return blocks +} + +function isDocSearchBlock(block) { + if (!block.includes(".DocSearch") && !block.includes("--docsearch-")) + return false + // Keep mixed :root blocks (with both --docsearch and --ifm vars) in main CSS + if (block.includes(":root") && block.includes("--ifm-")) return false + return true +} + module.exports = function embedCssPlugin() { return { name: "embed-css", @@ -16,7 +59,34 @@ module.exports = function embedCssPlugin() { ) if (!cssFile) return - const cssContent = fs.readFileSync(path.join(cssDir, cssFile), "utf8") + const cssPath = path.join(cssDir, cssFile) + const fullCss = fs.readFileSync(cssPath, "utf8") + + // --- Phase 1: Extract DocSearch CSS into a separate file --- + const blocks = splitCssBlocks(fullCss) + const mainBlocks = [] + const docSearchBlocks = [] + + for (const block of blocks) { + if (isDocSearchBlock(block)) { + docSearchBlocks.push(block) + } else { + mainBlocks.push(block) + } + } + + const mainCss = mainBlocks.join("") + const docSearchCss = docSearchBlocks.join("") + + // Extract the hash from the original filename (styles.HASH.css) + const hash = cssFile.replace("styles.", "").replace(".css", "") + const docSearchFile = `docsearch.${hash}.css` + + fs.writeFileSync(cssPath, mainCss) + fs.writeFileSync(path.join(cssDir, docSearchFile), docSearchCss) + + // --- Phase 2: Embed main CSS inline + lazy-load DocSearch CSS --- + const lazyLoadScript = `` function walk(dir) { for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { @@ -34,9 +104,10 @@ module.exports = function embedCssPlugin() { if (!html.includes(cssFile)) return + // Replace the stylesheet link with inline styles + DocSearch lazy loader html = html.replace( /]*\bhref="[^"]*styles\.[^"]*\.css"[^>]*>/i, - ``, + `${lazyLoadScript}`, ) fs.writeFileSync(filePath, html) } From f4b264bbdda75c5033c670dbe82f2d30a9bb1366 Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:39:08 +0100 Subject: [PATCH 3/4] fix: keep DocSearch button styles in main CSS to prevent layout jump The button is always visible on initial load, so its styles must be inlined rather than lazy-loaded. Co-Authored-By: Claude Sonnet 4.6 --- src/plugins/preload-css.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js index 7992e3a96..ce1861d97 100644 --- a/src/plugins/preload-css.js +++ b/src/plugins/preload-css.js @@ -44,6 +44,8 @@ function isDocSearchBlock(block) { return false // Keep mixed :root blocks (with both --docsearch and --ifm vars) in main CSS if (block.includes(":root") && block.includes("--ifm-")) return false + // Keep DocSearch button styles in main CSS — the button is always visible + if (block.includes(".DocSearch-Button")) return false return true } From 3c23073f765cfddb7372d51a2e4ece19808a9f9e Mon Sep 17 00:00:00 2001 From: aeneasr <3372410+aeneasr@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:43:15 +0100 Subject: [PATCH 4/4] fix: keep all :root blocks in main CSS to prevent layout jump on hover --docsearch-* custom properties (e.g. --docsearch-searchbox-height) are referenced by .DocSearch-Button styles. Deferring them caused the button to snap to the correct size when the lazy CSS loaded, triggering a layout shift. Now all :root variable definitions stay in the inlined CSS. Co-Authored-By: Claude Sonnet 4.6 --- src/plugins/preload-css.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js index ce1861d97..68e7060ff 100644 --- a/src/plugins/preload-css.js +++ b/src/plugins/preload-css.js @@ -42,8 +42,8 @@ function splitCssBlocks(css) { function isDocSearchBlock(block) { if (!block.includes(".DocSearch") && !block.includes("--docsearch-")) return false - // Keep mixed :root blocks (with both --docsearch and --ifm vars) in main CSS - if (block.includes(":root") && block.includes("--ifm-")) return false + // Keep all :root blocks — CSS variable definitions are needed for initial layout + if (block.includes(":root")) return false // Keep DocSearch button styles in main CSS — the button is always visible if (block.includes(".DocSearch-Button")) return false return true