From f124529ee8afe5bdd0262c7e8a38cba271502efd Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 28 May 2026 16:22:09 +0200 Subject: [PATCH] fix(stammbaum): seed gutter media-query state synchronously (#689) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI flagged two browser tests: - "renders a G{n} label per occupied generation row …" - "wraps the visible G3 text inside an aria-labelled group …" Both queried g[role="text"] and got an empty array. Root cause: isMdOrUp was initialised to false and only flipped to true inside a $effect — but $effect runs after the first render, so the test's post-render DOM scan saw the pre-effect (gutter-absent) state. Seed the rune synchronously from window.matchMedia(...).matches when window is available; SSR still picks the false branch and hydrates without a layout flash. The effect now only attaches the change listener for subsequent resizes. Refs #689 Co-Authored-By: Claude Opus 4.7 --- frontend/src/lib/person/genealogy/StammbaumTree.svelte | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/person/genealogy/StammbaumTree.svelte b/frontend/src/lib/person/genealogy/StammbaumTree.svelte index 7cfbddd1..6d85067e 100644 --- a/frontend/src/lib/person/genealogy/StammbaumTree.svelte +++ b/frontend/src/lib/person/genealogy/StammbaumTree.svelte @@ -30,11 +30,17 @@ const layout = $derived.by(() => buildLayout(nodes, edges)); // too costly on a 320 px screen). const GUTTER_WIDTH_DESKTOP = 100; const GUTTER_MEDIA_QUERY = '(min-width: 768px)'; -let isMdOrUp = $state(false); +// Seed synchronously so the first paint already has the right gutter state — +// otherwise the test (and a brief flash on real CSR mount) would see the +// pre-effect false. SSR has no window; the gutter stays hidden until hydrate. +let isMdOrUp = $state( + typeof window !== 'undefined' && typeof window.matchMedia === 'function' + ? window.matchMedia(GUTTER_MEDIA_QUERY).matches + : false +); $effect(() => { if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return; const mq = window.matchMedia(GUTTER_MEDIA_QUERY); - isMdOrUp = mq.matches; const handler = (e: MediaQueryListEvent) => (isMdOrUp = e.matches); mq.addEventListener('change', handler); return () => mq.removeEventListener('change', handler);