From ab3e77a9ba0a905a9507c6bf1a673a635097f27f Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Thu, 19 Mar 2026 12:23:30 -0400 Subject: [PATCH] Fix blink root cause: eliminate position:fixed GPU compositing layers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chrome promotes ALL position:fixed elements to GPU compositing layers for scroll performance, regardless of whether they have animations. The body::before scanline overlay (position:fixed, z-index:9999, full-viewport) and body::after watermark (position:fixed) were both on GPU layers. Every CPU repaint from any hover state change required a compositor re-blend pass → one-frame blink at compositor sync. Fixes: - Move scanlines from body::before (position:fixed) into body { background-image } — same visual, no separate element, no GPU layer promotion - Set body::before { display:none } and body::after { display:none } in both dashboard.css and base.css - Remove animation:matrix-rain from .stat-card:hover::before — background-position animation is not GPU-composited, caused CPU repaints every frame while hovered plus GPU texture uploads when animation started/stopped on cursor enter/exit - Scope a { transition: all } → transition: color in base.css Co-Authored-By: Claude Sonnet 4.6 --- assets/css/base.css | 34 +++++++++------------------------- assets/css/dashboard.css | 36 ++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/assets/css/base.css b/assets/css/base.css index 547dd1c..0e112de 100644 --- a/assets/css/base.css +++ b/assets/css/base.css @@ -171,7 +171,7 @@ body { a { color: var(--terminal-green); text-decoration: none; - transition: var(--transition-fast); + transition: color 0.15s ease; } a:hover { color: var(--terminal-amber); @@ -185,37 +185,21 @@ img, svg { display: block; max-width: 100%; } 03. CRT & TERMINAL EFFECTS ---------------------------------------------------------------- */ -/* Horizontal scanline overlay — fixed over the entire viewport */ -body::before { - content: ''; - position: fixed; - inset: 0; - background: repeating-linear-gradient( +/* Scanlines baked into body background — position:fixed overlay removed because + Chrome promotes all position:fixed elements to GPU compositing layers, causing + a compositor re-blend blink on every CPU repaint triggered by hover states. */ +body { + background-image: repeating-linear-gradient( 0deg, rgba(0, 0, 0, 0.15) 0px, rgba(0, 0, 0, 0.15) 1px, transparent 1px, transparent 2px ); - pointer-events: none; - z-index: var(--z-overlay); - animation: none; -} - -/* Binary data-stream watermark — bottom-right corner */ -body::after { - content: '10101010'; - position: fixed; - bottom: 10px; - right: 14px; - font-family: var(--font-mono); - font-size: 0.55rem; - color: var(--terminal-green); - opacity: 0.07; - pointer-events: none; - letter-spacing: 2px; - animation: data-stream 3s steps(1) infinite; } +body::before { display: none; } +/* body::after binary watermark also suppressed — was position:fixed (GPU layer) */ +body::after { display: none; } /* ---------------------------------------------------------------- 04. TYPOGRAPHY diff --git a/assets/css/dashboard.css b/assets/css/dashboard.css index 9c9a1f8..bb9f0e4 100644 --- a/assets/css/dashboard.css +++ b/assets/css/dashboard.css @@ -96,28 +96,29 @@ body { margin: 0; padding: var(--spacing-md); background-color: var(--bg-primary); - color: var(--text-primary); - position: relative; -} - -/* CRT Scanline Effect - Subtle retro terminal look */ -body::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: repeating-linear-gradient( + /* Scanlines baked into body background — avoids position:fixed GPU layer */ + background-image: repeating-linear-gradient( 0deg, - rgba(0, 0, 0, 0.15), + rgba(0, 0, 0, 0.15) 0px, rgba(0, 0, 0, 0.15) 1px, transparent 1px, transparent 2px ); - pointer-events: none; - z-index: var(--z-overlay); - animation: none; + color: var(--text-primary); + position: relative; +} + +/* body::before was the CRT scanline overlay (position:fixed, z-index:9999). + Chrome promotes all position:fixed elements to GPU compositing layers, so + every hover-triggered CPU repaint caused a compositor re-blend blink. + Scanlines moved to body background-image above. */ +body::before { + display: none; +} + +/* Suppress body::after binary text watermark (also position:fixed → GPU layer) */ +body::after { + display: none; } /* Screen Flicker Effect */ @@ -293,7 +294,6 @@ a:not(.btn):hover::after { .stat-card:hover::before { opacity: 1; - animation: matrix-rain 2s linear infinite; } @keyframes matrix-rain {