diff --git a/src/components/backgrounds/BackgroundRenderer.tsx b/src/components/backgrounds/BackgroundRenderer.tsx index b802cf3..d9dcb27 100644 --- a/src/components/backgrounds/BackgroundRenderer.tsx +++ b/src/components/backgrounds/BackgroundRenderer.tsx @@ -1,4 +1,5 @@ import { Suspense, useMemo } from 'react'; +import { createPortal } from 'react-dom'; import { useQuery } from '@tanstack/react-query'; import { brandingApi } from '@/api/branding'; import type { AnimationConfig, BackgroundType } from '@/components/ui/backgrounds/types'; @@ -83,20 +84,25 @@ export function BackgroundRenderer() { ? reduceMobileSettings(effectiveConfig.settings) : effectiveConfig.settings; - return ( + // Render via portal on document.body with z-index: -1. + // This places the animated background BELOW the root stacking context, + // preventing Chrome's implicit compositing from promoting every + // overlapping element to its own GPU layer (the root cause of flickering). + return createPortal(
0 ? `blur(${effectiveConfig.blur}px)` : undefined, contain: 'strict', - willChange: 'transform', - transform: 'translateZ(0)', + backfaceVisibility: 'hidden', }} > -
+ , + document.body, ); } diff --git a/src/components/layout/AppShell/AppShell.tsx b/src/components/layout/AppShell/AppShell.tsx index bd9a2a6..4752ed0 100644 --- a/src/components/layout/AppShell/AppShell.tsx +++ b/src/components/layout/AppShell/AppShell.tsx @@ -280,162 +280,158 @@ export function AppShell({ children }: AppShellProps) { return (
- {/* Animated background — on its own GPU layer, below everything */} + {/* Animated background renders via portal on document.body at z-index: -1 */} - {/* Content layer — isolated from background to prevent backdrop-filter - from resampling the animated background every frame (Chromium bug) */} -
- {/* Global components */} - - - + {/* Global components */} + + + - {/* Desktop Header */} -
-
- {/* Logo */} - -
- - {logoLetter} - - {hasCustomLogo && logoUrl && ( - {appName - )} -
- {appName} - - - {/* Center Navigation */} - - - {/* Right side actions */} -
- - - - + {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 b61e32b..5746cf1 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -400,7 +400,6 @@ img.twemoji { transition: opacity 0.3s ease; pointer-events: none; z-index: 10; - will-change: opacity; } .bento-card-hover:hover::after {