diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js
index 2a8f909aa..68e7060ff 100644
--- a/src/plugins/preload-css.js
+++ b/src/plugins/preload-css.js
@@ -4,6 +4,51 @@
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 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
+}
+
module.exports = function embedCssPlugin() {
return {
name: "embed-css",
@@ -16,7 +61,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 +106,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)
}
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",