From 9fbe4faac31426b49e9bb3d22bee9472ed52c564 Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.net>
Date: Tue, 2 Jun 2026 12:26:13 +0000
Subject: perf(badges): skip off-screen cells via IntersectionObserver
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Re-adds the off-screen layout/paint skip that was reverted for clipping the
hover tilt/scale and tooltip. content-visibility: auto is now applied only to
cells the IntersectionObserver marks off-screen (600px margin), so the
containment — and the visible<->auto flip that flashes — never affects a cell
that is on screen and interactive. Cells start off-screen so the first paint
still skips, and the observer clears it for visible cells after mount.
---
 src/lib/User/BadgeWall/Badges.svelte | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

(limited to 'src/lib/User/BadgeWall/Badges.svelte')

diff --git a/src/lib/User/BadgeWall/Badges.svelte b/src/lib/User/BadgeWall/Badges.svelte
index d2c56392..a77aebd6 100644
--- a/src/lib/User/BadgeWall/Badges.svelte
+++ b/src/lib/User/BadgeWall/Badges.svelte
@@ -1,4 +1,6 @@
 <script lang="ts">
+import { onDestroy } from "svelte";
+import { browser } from "$app/environment";
 import FallbackImage from "$lib/Image/FallbackImage.svelte";
 import Spacer from "$lib/Layout/Spacer.svelte";
 import LinkedTooltip from "$lib/Tooltip/LinkedTooltip.svelte";
@@ -16,6 +18,30 @@ export let categoryFilter: string | null;
 export let editMode: boolean;
 export let preferences: Preferences | undefined;
 export let selectedBadge: IndexedBadge | undefined = undefined;
+
+const skipObserver =
+	browser && typeof IntersectionObserver !== "undefined"
+		? new IntersectionObserver(
+				(entries) => {
+					for (const entry of entries)
+						entry.target.classList.toggle(
+							"is-offscreen",
+							!entry.isIntersecting,
+						);
+				},
+				{ rootMargin: "600px" },
+			)
+		: null;
+
+const skipWhenOffscreen = (node: HTMLElement) => {
+	skipObserver?.observe(node);
+
+	return {
+		destroy: () => skipObserver?.unobserve(node),
+	};
+};
+
+onDestroy(() => skipObserver?.disconnect());
 </script>
 
 {#if ungroupedBadges.length === 0}
@@ -42,7 +68,7 @@ export let selectedBadge: IndexedBadge | undefined = undefined;
 
     <div class="badges">
       {#each badges as badge}
-        <div id={`badge-${badge.id}`}>
+        <div id={`badge-${badge.id}`} class="is-offscreen" use:skipWhenOffscreen>
           {#if editMode}
             <LinkedTooltip
               content={`${
-- 
cgit v1.2.3

