From ab86078ca38893b378e7f0393a0646d74d8024a8 Mon Sep 17 00:00:00 2001 From: loki5512344 Date: Wed, 30 Sep 2026 10:37:40 +0200 Subject: [PATCH] perf(frontend): stop theme switches from janking the whole page Two inherited-custom-property transitions were forcing a full style recalculation on every animation frame during a theme switch: - html transitioned --color-ice/--page-accent-glow (inherits: true), both read by dozens of selectors site-wide (buttons, links, glows, color-mix() gradients) -- animating them recomputed style for every element that references them, across the whole page, not just the swatch that was clicked. - .lv-gui (the ClickGui replica used in ThemeStrip, HudPlayground, the login backdrop and the theme editor panel) transitioned 21 inherited custom properties at once, including per-row/per-tab ones (--gui-row-*, --gui-tab-*, --gui-menu-*) -- with a long module list that's the same cost multiplied by every row/tab in the tree. Both now snap instantly instead of transitioning; .lv-gui keeps a 300ms cross-fade but only on the ~8 properties that read as "the theme" (window/header/stroke/text/accent/panel), not per-row state. Claude-Session: https://claude.ai/code/session_01F1M1Jic1wTSn4igUENynmZ --- .../src/features/clickgui/parts/clickgui.css | 16 +++++++++++----- frontend/src/index.css | 7 ++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/frontend/src/features/clickgui/parts/clickgui.css b/frontend/src/features/clickgui/parts/clickgui.css index 0ad447e..5b0cdd1 100644 --- a/frontend/src/features/clickgui/parts/clickgui.css +++ b/frontend/src/features/clickgui/parts/clickgui.css @@ -38,11 +38,17 @@ color: var(--gui-text); text-align: left; user-select: none; - transition-property: --gui-window-from, --gui-window-to, --gui-stroke-from, --gui-stroke-to, --gui-text, --gui-text-muted, - --gui-accent, --gui-accent-soft, --gui-accent-glow, --gui-panel-tint, --gui-panel-stroke, --gui-split, --gui-row-hover, - --gui-row-glow, --gui-row-text, --gui-tab-left, --gui-tab-right, --gui-tab-active-left, --gui-tab-active-right, - --gui-menu-left, --gui-menu-right; - transition-duration: 400ms; + /* Only the big, always-visible surfaces cross-fade. The old list also animated + per-row/per-tab properties (--gui-row-*, --gui-tab-*, --gui-menu-*) -- since those are + `inherits: true` custom properties, transitioning them forces the browser to recompute + style for every module row/tab in the tree on every frame, not just once. With a long + module list that was the actual jank a theme switch caused; trimming the list to the + handful of properties that read as "the theme" (not each individual row) keeps the + visible cross-fade while cutting the recalculation cost roughly proportional to how many + properties/elements were removed from it. */ + transition-property: --gui-window-from, --gui-window-to, --gui-stroke-from, --gui-stroke-to, --gui-text, --gui-accent, + --gui-panel-tint, --gui-panel-stroke; + transition-duration: 300ms; transition-timing-function: ease; } .lv-gui button { font: inherit; color: inherit; cursor: pointer; } diff --git a/frontend/src/index.css b/frontend/src/index.css index 36b5ad2..9e27483 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -47,7 +47,12 @@ /* 100vw full-bleed sections exceed the scrollbar-less client width by a few px; clip on html too so the document can never pan horizontally. */ overflow-x: clip; - transition: --color-ice 400ms ease, --page-accent-glow 400ms ease; + /* No transition here on purpose: --color-ice/--page-accent-glow are `inherits: true` + custom properties read by dozens of selectors across the whole site (buttons, links, + glows, color-mix() gradients). Transitioning an inherited custom property at the root + forces the browser to recompute style for every element that references it, on every + frame, for the whole page -- that was the actual site-wide jank a theme switch caused. + The value still snaps instantly, which reads fine for a deliberate theme-swatch click. */ } body { @apply bg-abyss text-snow font-sans antialiased;