From f7eeb01535213c9d7105cc16f4c3902b8584c9b9 Mon Sep 17 00:00:00 2001 From: Aryan Parikh Date: Tue, 24 Feb 2026 15:21:53 +0530 Subject: [PATCH 1/4] =?UTF-8?q?perf:=20improve=20mobile=20Core=20Web=20Vit?= =?UTF-8?q?als=20=E2=80=93=20infra=20&=20rendering=20optimisations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - netlify.toml: add 1-year immutable cache for hashed assets (/assets/*, /fonts/*), 1-day cache for images/scripts, and security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) to improve PageSpeed Best Practices score - docusaurus.config.js: add viewport meta tag with viewport-fit=cover; add preconnect hints for fonts.googleapis.com, fonts.gstatic.com, and Algolia DSN; add dns-prefetch for Clarity, GTM, and GA analytics origins to reduce connection setup latency - src/css/custom.css: add content-visibility:auto + contain-intrinsic-size for footer and mobile sidebar to skip off-screen paint; add decoding:async fallback for un-annotated images; add font-display:swap safety net for any stylesheet-loaded webfonts - ResponsivePlayer.js: lazy-load react-player via React.lazy + React.Suspense (react-player/lazy) so the ~100 KB player bundle is NOT included in the initial JS chunk – reduces TBT/INP on first load Closes #2042 Signed-off-by: Aryan Parikh --- docusaurus.config.js | 47 +++++++++++++++-- netlify.toml | 31 ++++++++++- .../responsive-player/ResponsivePlayer.js | 37 +++++++++----- src/css/custom.css | 51 +++++++++++++++++++ 4 files changed, 150 insertions(+), 16 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 0ff78a9b5..62de93c55 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -63,7 +63,24 @@ module.exports = { {property: "og:image:height", content: "630"}, ], headTags: [ - // Google Fonts - DM Sans (loaded via headTags instead of CSS @import) + // ── Viewport (mobile performance) ────────────────────────────────── + { + tagName: "meta", + attributes: { + name: "viewport", + content: "width=device-width, initial-scale=1.0, viewport-fit=cover", + }, + }, + // ── Preconnect / DNS-prefetch for critical third-party origins ───── + // Keploy CDN + { + tagName: "link", + attributes: { + rel: "preconnect", + href: "https://keploy.io/", + }, + }, + // Google Fonts (used by Docusaurus default theme) { tagName: "link", attributes: { @@ -79,6 +96,7 @@ module.exports = { crossorigin: "anonymous", }, }, + // Google Fonts - DM Sans (loaded via headTags instead of CSS @import) { tagName: "link", attributes: { @@ -86,12 +104,35 @@ module.exports = { href: "https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;700&display=swap", }, }, - // Preconnect tag + // Algolia search { tagName: "link", attributes: { rel: "preconnect", - href: "https://keploy.io/", + href: "https://WZTL8PLCOD-dsn.algolia.net", + crossorigin: "anonymous", + }, + }, + // Analytics (dns-prefetch only — not render-blocking) + { + tagName: "link", + attributes: { + rel: "dns-prefetch", + href: "https://www.clarity.ms", + }, + }, + { + tagName: "link", + attributes: { + rel: "dns-prefetch", + href: "https://www.googletagmanager.com", + }, + }, + { + tagName: "link", + attributes: { + rel: "dns-prefetch", + href: "https://www.google-analytics.com", }, }, { diff --git a/netlify.toml b/netlify.toml index 2104fcf94..87e6e23e4 100644 --- a/netlify.toml +++ b/netlify.toml @@ -9,7 +9,36 @@ ## Note: if you are looking for Redirects # they have been moved to /static/_redirects to make it more manageable - swyx -# Security headers for all pages +# ── Performance: Cache headers ──────────────────────────────────────────────── +# Hashed JS/CSS bundles emitted by Docusaurus/webpack → safe to cache 1 year +[[headers]] + for = "/assets/*" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +# Static images, fonts served from /img and /fonts +[[headers]] + for = "/img/*" + [headers.values] + Cache-Control = "public, max-age=86400, stale-while-revalidate=604800" + +[[headers]] + for = "/fonts/*" + [headers.values] + Cache-Control = "public, max-age=31536000, immutable" + +# Static JS helpers (non-hashed scripts in /docs/js and /docs/scripts) +[[headers]] + for = "/js/*" + [headers.values] + Cache-Control = "public, max-age=86400" + +[[headers]] + for = "/scripts/*" + [headers.values] + Cache-Control = "public, max-age=86400" + +# ── Security headers (improves PageSpeed Best Practices score) ────────────── [[headers]] for = "/*" [headers.values] diff --git a/src/components/responsive-player/ResponsivePlayer.js b/src/components/responsive-player/ResponsivePlayer.js index d08af04c3..c29834002 100644 --- a/src/components/responsive-player/ResponsivePlayer.js +++ b/src/components/responsive-player/ResponsivePlayer.js @@ -1,5 +1,9 @@ -import React from "react"; -import ReactPlayer from "react-player"; +import React, {Suspense, lazy} from "react"; + +// Lazy-load react-player so it is NOT included in the initial JS bundle. +// react-player/lazy defers loading the actual player implementation until +// the component is rendered, reducing the first-page-load JS payload. +const ReactPlayer = lazy(() => import("react-player/lazy")); function ResponsivePlayer({url, loop, playing}) { return ( @@ -7,16 +11,25 @@ function ResponsivePlayer({url, loop, playing}) { className="relative rounded-lg shadow-lg" style={{paddingTop: "56.25%"}} > - {/* /* Player ratio: 100 / (1280 / 720) */} - + {/* Player ratio: 100 / (1280 / 720) */} + + } + > + + ); } diff --git a/src/css/custom.css b/src/css/custom.css index 739b7f172..c70e998a1 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -3202,3 +3202,54 @@ html[data-theme="dark"] .docs-inline-footer__slack { font-size: 0.95rem; } } + +/* ===== PERFORMANCE: Rendering & paint optimisations ===== */ + +/* + * content-visibility: auto — lets the browser skip layout/paint for + * off-screen sections, reducing LCP and INP on mobile. + * contain-intrinsic-size gives the browser a size estimate so the + * scroll-bar doesn't jump when content is rendered. + */ +footer, +.footer { + content-visibility: auto; + contain-intrinsic-size: 0 200px; +} + +/* Sidebar is always below the fold on small viewports */ +@media (max-width: 996px) { + .theme-doc-sidebar-container { + content-visibility: auto; + contain-intrinsic-size: 0 600px; + } +} + +/* + * Lazy-decoded images — any without an explicit loading attribute + * should at minimum decode off the main thread. + */ +img:not([loading]) { + decoding: async; +} + +/* + * Reduce paint layers for the announcement bar (it's a position:sticky + * element and can cause extra compositing cost on mobile). + */ +.announcementBar_mb4j { + will-change: auto; + transform: translateZ(0); +} + +/* + * Font-display: swap fallback for any @font-face rules Docusaurus injects. + * This prevents invisible text during webfont load (FOIT → FOUT). + * The actual font files are already preloaded via webpack-font-preload-plugin; + * this rule is a safety net for any font loaded via a stylesheet. + */ +@supports (font-display: swap) { + @font-face { + font-display: swap; + } +} From aa94da4261d2237cbb87a9597840a8bfdfc68172 Mon Sep 17 00:00:00 2001 From: Aryan Parikh Date: Tue, 24 Feb 2026 16:18:20 +0530 Subject: [PATCH 2/4] fix: remove duplicate viewport meta tag from headTags Docusaurus already injects by default from its core theme. Adding it again via headTags created a duplicate tag. Removes the redundant entry per Copilot review on PR. Signed-off-by: Aryan Parikh --- docusaurus.config.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 62de93c55..4b28e1a3e 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -63,14 +63,6 @@ module.exports = { {property: "og:image:height", content: "630"}, ], headTags: [ - // ── Viewport (mobile performance) ────────────────────────────────── - { - tagName: "meta", - attributes: { - name: "viewport", - content: "width=device-width, initial-scale=1.0, viewport-fit=cover", - }, - }, // ── Preconnect / DNS-prefetch for critical third-party origins ───── // Keploy CDN { From b8ceaa74bcf94f5455e8101581ffb7d12a2990bc Mon Sep 17 00:00:00 2001 From: Aryan Parikh Date: Tue, 24 Feb 2026 17:56:35 +0530 Subject: [PATCH 3/4] fix: address Copilot review comments on PR #795 - css: replace hashed .announcementBar_mb4j selector with [class*=announcementBar] to survive Docusaurus rebuilds - css: remove invalid empty @font-face block; a @font-face without font-family/src has no effect in any browser - ResponsivePlayer: add visible spinner + role=status to Suspense fallback for better UX on slow connections - netlify.toml: change /fonts/* from immutable to 1-week cache since font files (Roboto-Bold.woff2 etc.) are not content-hashed Signed-off-by: Aryan Parikh --- netlify.toml | 6 ++++-- .../responsive-player/ResponsivePlayer.js | 6 +++++- src/css/custom.css | 16 ++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/netlify.toml b/netlify.toml index 87e6e23e4..5e68b2a6b 100644 --- a/netlify.toml +++ b/netlify.toml @@ -16,16 +16,18 @@ [headers.values] Cache-Control = "public, max-age=31536000, immutable" -# Static images, fonts served from /img and /fonts +# Static images served from /img [[headers]] for = "/img/*" [headers.values] Cache-Control = "public, max-age=86400, stale-while-revalidate=604800" +# Fonts are NOT content-hashed (e.g. Roboto-Bold.woff2) so immutable is unsafe. +# Use a 1-week cache with stale-while-revalidate instead. [[headers]] for = "/fonts/*" [headers.values] - Cache-Control = "public, max-age=31536000, immutable" + Cache-Control = "public, max-age=604800, stale-while-revalidate=86400" # Static JS helpers (non-hashed scripts in /docs/js and /docs/scripts) [[headers]] diff --git a/src/components/responsive-player/ResponsivePlayer.js b/src/components/responsive-player/ResponsivePlayer.js index c29834002..cfc543d82 100644 --- a/src/components/responsive-player/ResponsivePlayer.js +++ b/src/components/responsive-player/ResponsivePlayer.js @@ -16,8 +16,12 @@ function ResponsivePlayer({url, loop, playing}) { fallback={
+ > +
+ Loading video player +
} > Date: Fri, 10 Apr 2026 10:40:31 +0530 Subject: [PATCH 4/4] fix: improve player loading state and stable code block selector --- src/components/responsive-player/ResponsivePlayer.js | 4 +++- src/css/custom.css | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/responsive-player/ResponsivePlayer.js b/src/components/responsive-player/ResponsivePlayer.js index cfc543d82..9237b9e2c 100644 --- a/src/components/responsive-player/ResponsivePlayer.js +++ b/src/components/responsive-player/ResponsivePlayer.js @@ -20,7 +20,9 @@ function ResponsivePlayer({url, loop, playing}) { aria-label="Loading video player" >
- Loading video player + + Loading video player... +
} > diff --git a/src/css/custom.css b/src/css/custom.css index e7362e87e..115b78b17 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -806,7 +806,8 @@ td img { margin: 0; } -.codeBlockContainer_node_modules-\@docusaurus-theme-classic-lib-next-theme-CodeBlock-styles-module { +[class^="codeBlockContainer_"], +[class*=" codeBlockContainer_"] { box-shadow: none !important; margin: 0; padding: 0;