From 04eacf642184867f5ab3437f0aef09ff4ee73e0c Mon Sep 17 00:00:00 2001 From: Fringg Date: Fri, 27 Feb 2026 07:52:45 +0300 Subject: [PATCH] fix: isolate content layer from animated background to eliminate flickering Root cause: backdrop-filter on header/nav resamples the animated background 60fps, causing GPU compositing flicker (Chromium bug). - Add isolation: isolate wrapper in AppShell to prevent backdrop-filter from seeing through to the animated background layer - Add will-change: transform + translateZ(0) to BackgroundRenderer for stable GPU layer - Replace contain: layout style with contain: content on main - Replace transition-all with specific properties in Card component --- .../backgrounds/BackgroundRenderer.tsx | 2 + src/components/data-display/Card/Card.tsx | 2 +- src/components/layout/AppShell/AppShell.tsx | 268 +++++++++--------- src/styles/globals.css | 6 +- 4 files changed, 142 insertions(+), 136 deletions(-) diff --git a/src/components/backgrounds/BackgroundRenderer.tsx b/src/components/backgrounds/BackgroundRenderer.tsx index 54f790c..b802cf3 100644 --- a/src/components/backgrounds/BackgroundRenderer.tsx +++ b/src/components/backgrounds/BackgroundRenderer.tsx @@ -90,6 +90,8 @@ export function BackgroundRenderer() { opacity: effectiveConfig.opacity, filter: effectiveConfig.blur > 0 ? `blur(${effectiveConfig.blur}px)` : undefined, contain: 'strict', + willChange: 'transform', + transform: 'translateZ(0)', }} > diff --git a/src/components/data-display/Card/Card.tsx b/src/components/data-display/Card/Card.tsx index 76245be..f96a263 100644 --- a/src/components/data-display/Card/Card.tsx +++ b/src/components/data-display/Card/Card.tsx @@ -11,7 +11,7 @@ const cardVariants = cva( 'relative overflow-hidden', 'border border-dark-700/40 bg-dark-900/70', 'rounded-[var(--bento-radius)]', - 'transition-all duration-200', + 'transition-[border-color,background-color,box-shadow,transform,opacity] duration-200', // GPU acceleration 'transform-gpu', // Glass border inset diff --git a/src/components/layout/AppShell/AppShell.tsx b/src/components/layout/AppShell/AppShell.tsx index 4a6d521..bd9a2a6 100644 --- a/src/components/layout/AppShell/AppShell.tsx +++ b/src/components/layout/AppShell/AppShell.tsx @@ -280,158 +280,162 @@ export function AppShell({ children }: AppShellProps) { return (
- {/* Animated background */} + {/* Animated background — on its own GPU layer, below everything */} - {/* Global components */} - - - + {/* Content layer — isolated from background to prevent backdrop-filter + from resampling the animated background every frame (Chromium bug) */} +
+ {/* Global components */} + + + - {/* Desktop Header */} -
-
- {/* Logo */} - -
- - {logoLetter} - - {hasCustomLogo && logoUrl && ( - {appName +
+ {/* Logo */} + +
+ - )} -
- {appName} - + > + {logoLetter} + + {hasCustomLogo && logoUrl && ( + {appName + )} +
+ {appName} + - {/* Center Navigation */} - + + {/* Right side actions */} +
+ + + + +
-
-
+ - {/* Mobile Header */} - {}} - headerHeight={headerHeight} - isFullscreen={isMobileFullscreen} - safeAreaInset={safeAreaInset} - contentSafeAreaInset={contentSafeAreaInset} - telegramPlatform={platform} - wheelEnabled={wheelEnabled} - referralEnabled={referralEnabled} - hasContests={hasContests} - hasPolls={hasPolls} - /> + {/* Mobile Header */} + {}} + headerHeight={headerHeight} + isFullscreen={isMobileFullscreen} + safeAreaInset={safeAreaInset} + contentSafeAreaInset={contentSafeAreaInset} + telegramPlatform={platform} + wheelEnabled={wheelEnabled} + referralEnabled={referralEnabled} + hasContests={hasContests} + hasPolls={hasPolls} + /> - {/* Desktop spacer */} -
+ {/* Desktop spacer */} +
- {/* Mobile spacer */} -
+ {/* Mobile spacer */} +
- {/* Main content */} -
{children}
+ {/* Main content */} +
{children}
- {/* Mobile Bottom Navigation */} - + {/* Mobile Bottom Navigation */} + +
); } diff --git a/src/styles/globals.css b/src/styles/globals.css index 4f46194..b61e32b 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -268,10 +268,10 @@ } } - /* Optimize main scrollable content */ + /* Main content — containment removed to avoid GPU layer conflicts + with isolation: isolate on content wrapper */ main { - /* Prevent layout shifts during scroll */ - contain: layout style; + contain: content; } /* Light theme - Champagne */