From ffffd42abf0f4872461ccba2fdb8b3e0242a1b9b Mon Sep 17 00:00:00 2001
From: aeneasr <3372410+aeneasr@users.noreply.github.com>
Date: Tue, 24 Mar 2026 14:52:40 +0100
Subject: [PATCH 1/2] perf: load main CSS asynchronously to eliminate
render-blocking
Adds a Docusaurus postBuild plugin that replaces the blocking
Docusaurus injects for the hashed main CSS
bundle with an async preload+onload pattern. The CSS is fetched at
high priority but never blocks the initial render; a
fallback ensures it loads without JS.
Co-Authored-By: Claude Sonnet 4.6
---
docusaurus.config.ts | 1 +
src/plugins/preload-css.js | 54 ++++++++++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
create mode 100644 src/plugins/preload-css.js
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index 6b6185213..295267dca 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -151,6 +151,7 @@ const config: Config = {
// buttonPosition: "center-right",
// },
// ],
+ "./src/plugins/preload-css",
async function tailwindcss(context, options) {
return {
name: "docusaurus-tailwindcss",
diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js
new file mode 100644
index 000000000..defb95120
--- /dev/null
+++ b/src/plugins/preload-css.js
@@ -0,0 +1,54 @@
+// Copyright © 2026 Ory Corp
+// SPDX-License-Identifier: Apache-2.0
+
+const fs = require("fs")
+const path = require("path")
+
+module.exports = function preloadCssPlugin() {
+ return {
+ name: "preload-css",
+ async postBuild({ outDir, siteConfig }) {
+ const cssDir = path.join(outDir, "assets", "css")
+ if (!fs.existsSync(cssDir)) return
+
+ const cssFile = fs.readdirSync(cssDir).find(
+ (f) => f.startsWith("styles.") && f.endsWith(".css"),
+ )
+ if (!cssFile) return
+
+ const baseUrl = siteConfig.baseUrl.replace(/\/$/, "")
+ const href = `${baseUrl}/assets/css/${cssFile}`
+
+ // Non-blocking async CSS: preload with onload swap + noscript fallback.
+ // The onload flips rel from "preload" to "stylesheet" once the file is
+ // fetched, so it never blocks the initial render.
+ const asyncTag = ` `
+
+ // Pattern matching the blocking stylesheet Docusaurus injects into
+ const blockingPattern = new RegExp(
+ ` ]+rel="stylesheet"[^>]+href="${href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"[^>]*>`,
+ )
+
+ function walk(dir) {
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name)
+ if (entry.isDirectory()) {
+ walk(full)
+ } else if (entry.name.endsWith(".html")) {
+ transform(full)
+ }
+ }
+ }
+
+ function transform(filePath) {
+ let html = fs.readFileSync(filePath, "utf8")
+ if (!blockingPattern.test(html)) return
+ // Remove the blocking and replace with async pattern
+ html = html.replace(blockingPattern, asyncTag)
+ fs.writeFileSync(filePath, html)
+ }
+
+ walk(outDir)
+ },
+ }
+}
From 6f2b9d6d4ddac4e1f9a1a6eb55b8657f80134bef Mon Sep 17 00:00:00 2001
From: aeneasr <3372410+aeneasr@users.noreply.github.com>
Date: Tue, 24 Mar 2026 17:37:53 +0100
Subject: [PATCH 2/2] perf: only preload CSS, don't replace stylesheet link
Co-Authored-By: Claude Sonnet 4.6
---
src/plugins/preload-css.js | 20 +++++---------------
1 file changed, 5 insertions(+), 15 deletions(-)
diff --git a/src/plugins/preload-css.js b/src/plugins/preload-css.js
index defb95120..40cada0f7 100644
--- a/src/plugins/preload-css.js
+++ b/src/plugins/preload-css.js
@@ -18,16 +18,7 @@ module.exports = function preloadCssPlugin() {
const baseUrl = siteConfig.baseUrl.replace(/\/$/, "")
const href = `${baseUrl}/assets/css/${cssFile}`
-
- // Non-blocking async CSS: preload with onload swap + noscript fallback.
- // The onload flips rel from "preload" to "stylesheet" once the file is
- // fetched, so it never blocks the initial render.
- const asyncTag = ` `
-
- // Pattern matching the blocking stylesheet Docusaurus injects into
- const blockingPattern = new RegExp(
- ` ]+rel="stylesheet"[^>]+href="${href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"[^>]*>`,
- )
+ const preloadTag = ` `
function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
@@ -35,16 +26,15 @@ module.exports = function preloadCssPlugin() {
if (entry.isDirectory()) {
walk(full)
} else if (entry.name.endsWith(".html")) {
- transform(full)
+ inject(full)
}
}
}
- function transform(filePath) {
+ function inject(filePath) {
let html = fs.readFileSync(filePath, "utf8")
- if (!blockingPattern.test(html)) return
- // Remove the blocking and replace with async pattern
- html = html.replace(blockingPattern, asyncTag)
+ if (html.includes(preloadTag)) return
+ html = html.replace("", `${preloadTag}`)
fs.writeFileSync(filePath, html)
}