From c1bdec0269d327abbad0882edb96b232aa7f647e Mon Sep 17 00:00:00 2001 From: marinom2 Date: Wed, 29 Jul 2026 12:26:31 +0300 Subject: [PATCH] perf(nav): drop the backdrop-blur behind the sticky header Scrolling in the desktop app was painful while the app sat at 0% CPU when idle - the cost was entirely per-frame, and only while the view moved. The header is `sticky top-0` and carried `backdrop-blur-xl` over a 55%-opaque background. A backdrop-filter has to sample whatever is behind the element and blur it; because the header is sticky, "whatever is behind it" is different on every scroll frame. So the browser re-sampled and re-blurred the full width of the viewport ~60 times a second, at Tailwind's largest radius. On a 3840x2160 display that is a lot of pixels, and WebKitGTK - which is what the desktop app renders with, since the AppImage is a shell over the site - is substantially slower at backdrop-filter than Chromium. Replaced with an opaque background. On a dark theme the two are nearly indistinguishable, and the per-frame cost goes to zero. Also dropped the blur on the nav dropdown: its background was already bg-card/95, so the filter bought almost nothing visually while still forcing a filtered layer. It sits over static content anyway, so it was never the scroll problem. The other 23 backdrop-blur uses are on ordinary cards that do not sit over moving content, and are left alone - blur is not the problem, blur over a scrolling backdrop is. Verified: tsc clean, 673 tests, eslint clean, next build succeeds. --- components/nav.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/components/nav.tsx b/components/nav.tsx index a979387..4373faf 100644 --- a/components/nav.tsx +++ b/components/nav.tsx @@ -136,7 +136,9 @@ function DesktopDropdown({ {open && item.children ? (
{item.children.map((c) => ( !l.webOnly) : ALL_LINKS; + // The header carries no backdrop-blur, deliberately. It is `sticky`, so the + // content behind it changes on every scroll frame - which means a + // backdrop-filter has to re-sample and re-blur the full width of the viewport + // 60 times a second. On a 4K display that is ~3840px of blur per frame, and + // WebKitGTK (what the desktop app renders with) is markedly slower at it than + // Chromium. It was the largest single cause of scroll jank in the desktop + // app, and invisible in profiles taken at rest: idle cost was zero. + // + // An opaque background lands within a hair of the same look on a dark theme + // for no per-frame cost. Blur is still fine on surfaces that do not sit over + // moving content - the dropdown above only exists while it is open. return ( -
+
setOpen(false)}> {/* eslint-disable-next-line @next/next/no-img-element */}