Badge Backdrop-Filter Checkerboarding#
Root cause: .badge { backdrop-filter: blur(2px) } β introduced in PR #100 (Venice design hand-off) and removed in PR #141 β created a separate Chromium compositor render surface for every badge element. Sessions at 50 rows and Files at 100 rows produce up to 150 badge surfaces inside a single scrolling panel with a 16px rounded overflow: hidden clip. A clipped panel containing composited children must take the slow render-surface path; during a fast trackpad fling, raster fell behind and the compositor drew blank tiles (checkerboarding).
The backdrop blur was visually a no-op: the badge background is a 12% alpha token over a flat panel color, so removing it changes nothing in light or dark theme .
Historical note: PR #106 had already removed the same property from the sticky topbar to fix scroll repaints. The badge rule was missed in that pass.
The Fix (PR #141)#
Two CSS changes in src/ui/styles.css:
-
Remove per-badge blur β the
.badgerule no longer carriesbackdrop-filteror-webkit-backdrop-filter. -
Disable skeleton animation on session rows β the 50 reserved skeleton rows carried 550
.skeleton-lineelements animatingbackground-position(@keyframes skeleton-pulse), a main-thread repaint per frame that competes with raster precisely when a user flings on a cold load. The override rule.session-row-skeleton .skeleton-line { animation: none }is placed after the base.skeleton-linerule so Biome'snoDescendingSpecificitylint stays quiet.
Guardrail Test#
test/ui-restyle-contract.test.ts enforces two contracts:
-
"backdrop blur stays on transient overlays, never on per-row chrome"β asserts.badgecontains nobackdrop-filter, then uses a regex to extract every selector that does carry the property and checks it against an explicit allowlist:.command-palette-backdrop,.command-palette.share-review-backdrop.overflow-menu-popover.sidebar-backdrop.is-open
-
"persistent sticky headers stay opaque during scrolling"β asserts.topbarand.thread-headeralso omitbackdrop-filter(the fix from PR #106).
Any new per-row element with backdrop-filter will fail CI.
Diagnostic Gotchas#
-
rAF probes are blind to checkerboarding. A
requestAnimationFrame-based scroll probe measures main-thread frame cadence. The compositor draws blank tiles without delaying rAF callbacks, so the probe reports a smooth 60 fps while the user sees paint bands. Confirm with a real trackpad fling or CDP tracing (LayerTree compositing reasons,ccraster events). -
Quick in-browser A/B: inject
<style>.badge{backdrop-filter:none!important}</style>in the DevTools console before flinging to verify the fix on any build. -
Local dev:
decant servepipessrc/ui/through Bun's HTML bundler; CSS edits appear on page reload without restarting the server.