From 8e345170ea673248122282dcb804f6d67a87d79e Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Fri, 24 Apr 2026 13:28:59 -0400 Subject: [PATCH 1/6] Hidybar static banner which hides on scrolldown, reappears on scrollup --- docs/src/lib/components/Hidybar.svelte | 61 +++++++ docs/src/lib/components/OpenWithButton.svelte | 12 +- docs/src/routes/docs/+layout.svelte | 8 +- .../docs/components/[name]/+layout.svelte | 167 ++++++++++-------- .../docs/components/[name]/+page.svelte | 120 ++----------- docs/vite.config.ts | 2 +- 6 files changed, 192 insertions(+), 178 deletions(-) create mode 100644 docs/src/lib/components/Hidybar.svelte diff --git a/docs/src/lib/components/Hidybar.svelte b/docs/src/lib/components/Hidybar.svelte new file mode 100644 index 000000000..b904ed243 --- /dev/null +++ b/docs/src/lib/components/Hidybar.svelte @@ -0,0 +1,61 @@ + + + +
+ {#if loaded} +
+ {@render children()} +
+ {/if} + {loaded} +
+ + diff --git a/docs/src/lib/components/OpenWithButton.svelte b/docs/src/lib/components/OpenWithButton.svelte index 48cf7e838..6dc1d1091 100644 --- a/docs/src/lib/components/OpenWithButton.svelte +++ b/docs/src/lib/components/OpenWithButton.svelte @@ -82,10 +82,18 @@ } - + - - {/if} -
-
- {metadata.category} -
+ const title = $derived( + pageExample?.module?.title ?? + toTitleCase(page.params.example?.replaceAll('-', ' ') ?? toTitleCase(metadata.name)) + ); - {#if page.params.example} - - {metadata.name} - {/if} -
+ const breadcrumbs = $derived([ + { label: metadata.category }, + ...(page.params.example + ? [{ label: metadata.name, href: `/docs/components/${page.params.name}` }] + : []), + { + label: title, + href: `/docs/components/${metadata.name}/${title.toLowerCase().replaceAll(' ', '-')}` + } + ]); + -
-

- {pageExample?.module?.title ?? page.params.example?.replaceAll('-', ' ') ?? metadata.name} -

- + +
+ + {#snippet item({ item }: { item: (typeof breadcrumbs)[number] })} + {#if item.href} + {item.label} + {:else} + {item.label} + {/if} + {/snippet} + + {#if layers?.length} {#each layers as layer} {toTitleCase(layer)} {/each} {/if} - + + + +
- - {#if pageExample?.module?.description} -
{pageExample.module.description}
- {/if} - - {#if page.params.example == null} -
{metadata.description}
- -
- - - - - -
- {/if} -
+ + +

{ + navigator.clipboard.writeText(`[${title}](${url}`); + }} +> + {title} +

+{#if pageExample?.module?.description} +
{pageExample.module.description}
+{/if} + +{#if page.params.example == null} +
{metadata.description}
+ + +{/if} {#snippet pending()} diff --git a/docs/src/routes/docs/components/[name]/+page.svelte b/docs/src/routes/docs/components/[name]/+page.svelte index f1671b738..269596464 100644 --- a/docs/src/routes/docs/components/[name]/+page.svelte +++ b/docs/src/routes/docs/components/[name]/+page.svelte @@ -1,113 +1,31 @@ - - - - -{#if catalog && (catalog.examples?.length || catalog.usage?.length)} -
- -
-{/if} -{#if api?.properties.length} -

API Reference

+ const example = $derived(page.params.example!); + const component = $derived(page.url.searchParams.get('component') ?? page.params.name!); - - - {#each (data ?? []) as rowData, rowIndex (rowIndex)} - {@const row = rowData as PropertyInfo} - - {#each columns as column (column.name)} - {@const value = getCellValue(column, rowData, rowIndex)} + const exampleInfo = $derived(data.catalog?.examples.find((e) => e.name === example)); + - - {/each} - - {:else} - - - - {/each} - -
- {#if column.name === 'name'} -
- {value} - {#if row.required} - required - {/if} -
- {:else if column.name === 'type'} - {value} - {:else if column.name === 'description'} - {#if row.descriptionHtml} - {@html row.descriptionHtml} - {:else} - {value} - {/if} - {#if row.default != null} -
- default: {row.default} -
- {/if} - {:else} - {getCellContent(column, rowData, rowIndex)} - {/if} -
No properties
+ - {#if api.extends?.length} -
- also available: -
- {#each api.extends as extended} - {extended.name} - {/each} -
-
- {/if} -{/if} +

Components

+
+ {#each exampleInfo?.components as componentUsage} + + {/each} +
-{#if metadata.related.length} - -
- {#each metadata.related as related} - - {/each} +{#if data.catalog} +
+
{/if} diff --git a/docs/vite.config.ts b/docs/vite.config.ts index 0c01d5fcd..2ceaf2bb0 100644 --- a/docs/vite.config.ts +++ b/docs/vite.config.ts @@ -34,7 +34,7 @@ export default defineConfig({ }) /*, devtoolsJson()*/ ], server: { - // allowedHosts: ['.trycloudflare.com'], + allowedHosts: ['.ngrok-free.app', '.trycloudflare.com'], fs: { allow: ['.live-code'] }, From 7c923004a0d190a759cedf66f37b27d0e3f46cff Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Tue, 28 Apr 2026 18:26:04 -0400 Subject: [PATCH 2/6] Hidybar additional work --- .claude/settings.json | 8 ++ .../lib/components/HideyBar Issue README.md | 105 +++++++++++++++ docs/src/lib/components/Hidybar.svelte | 22 ++-- docs/src/routes/docs/+layout.svelte | 2 +- .../docs/components/[name]/+layout.svelte | 10 +- .../docs/components/[name]/+page.svelte | 120 +++++++++++++++--- .../components/[name]/[example]/+page.svelte | 5 - 7 files changed, 234 insertions(+), 38 deletions(-) create mode 100644 .claude/settings.json create mode 100644 docs/src/lib/components/HideyBar Issue README.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..b2dbcaf5b --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Skill(playwright-cli)", + "Skill(playwright-cli:*)" + ] + } +} diff --git a/docs/src/lib/components/HideyBar Issue README.md b/docs/src/lib/components/HideyBar Issue README.md new file mode 100644 index 000000000..0b373d25b --- /dev/null +++ b/docs/src/lib/components/HideyBar Issue README.md @@ -0,0 +1,105 @@ + Why is hidybar not ALWAYS showing after page nagivation? +I tried all 3 without success, leaving here to jump start your roubleshooting. + + Root Cause + + The bug is an interaction between three things working against each other: + + 1. The hidybar's visibility mechanism + + /* Hidybar.svelte */ + @container scroll-state(scrolled: bottom) { + translate: 0 calc(-100% - var(--header-height)); /* HIDE */ + } + @container scroll-state(scrolled: top) { + translate: none; /* SHOW */ + } + The bar is hidden when scrolled: bottom is true. Showing it is triggered by + scrolled: top via a CSS translate transition (150ms, from the Tailwind + transition-[translate] class on the header). + + 2. The View Transitions API wrapping navigations + + In page-transitions.ts: + document.startViewTransition(async () => { + resolve(); // ← SvelteKit navigates, resets scroll to 0 + await navigation.complete; // ← waits for nav to finish + // browser takes NEW snapshot here ↑ + }); + + 3. The timing mismatch + + When the user was scrolled to the bottom of Page A (hidybar hidden), then + navigates to Page B: + + 1. Old snapshot taken → hidybar is fully hidden (translated off-screen) + 2. SvelteKit's resolve() runs → new page renders, window.scrollTo(0, 0) is + called + 3. scroll-state(scrolled: top) becomes true → browser starts the CSS translate + transition (hidden → none, 150ms) + 4. navigation.complete resolves → callback returns → new snapshot taken + 5. The new snapshot is captured at time ≈ 0ms of the 150ms CSS transition — the + hidybar is still essentially fully translated off-screen + 6. View transition animation plays: old (fully hidden) → new (still fully + hidden) — the bar is invisible during the entire animation (~250ms) + 7. View transition ends, pseudo-elements are removed, actual elements revealed → + by this point 250ms have passed, the 150ms CSS transition has completed → + hidybar snaps into view + + Why "sometimes": This only happens when the user was scrolled to the bottom of + the previous page (the only condition that hides the bar). If the hidybar was + already visible, both old and new snapshots show it visible — no issue. + + In short: + + The view transition captures a "new page" snapshot before the CSS translate + transition triggered by the scroll-state change has had any time to animate. So + both snapshots effectively show the hidden state, and the bar only appears after + the view transition animation ends — it looks like it's missing during the + navigation. + +✻ Sautéed for 7m 43s + +❯ asking how would that be fixed? + +⏺ There are a few approaches, from simplest to most correct: + + --- + Option 1: Disable the CSS transition during view transitions (simplest) + + In app.css, the code already does this pattern for scroll-behavior: + html:active-view-transition { + scroll-behavior: auto; + } + + The same pattern would fix the hidybar — add: + html:active-view-transition header.hidybar { + transition-duration: 0ms; + } + + During an active view transition, the translate snaps instantly when + scroll-state(scrolled: top) fires. So when the new snapshot is taken, the + hidybar is already at translate: none (fully visible). The view transition's own + animation provides visual smoothness — no need for the CSS transition too. + + --- + Option 2: Give the hidybar its own view-transition-name + + Add view-transition-name: hidybar to the
. The browser then captures it + separately from the page root and you can control its animation independently + via ::view-transition-old(hidybar) / ::view-transition-new(hidybar) — e.g., + slide it in from the top rather than cross-fade with the page. More control, + more code. + + --- + Option 3: Delay the new snapshot + + After await navigation.complete in page-transitions.ts, add await new Promise(r + => setTimeout(r, 160)) — just long enough for the 150ms CSS transition to + complete before the snapshot is taken. The new snapshot then captures the bar as + fully visible. Works, but adds latency to every navigation. + + --- + Option 1 is the right call — one line in app.css, uses the existing + html:active-view-transition pattern already in the file, and doesn't change the + Hidybar component at all. \ No newline at end of file diff --git a/docs/src/lib/components/Hidybar.svelte b/docs/src/lib/components/Hidybar.svelte index b904ed243..0e9104c4e 100644 --- a/docs/src/lib/components/Hidybar.svelte +++ b/docs/src/lib/components/Hidybar.svelte @@ -1,29 +1,27 @@ @@ -32,20 +30,20 @@ bind:this={hideybarWrapper} bind:clientWidth={contentWidth} style={`height: ${hidybarHeight};`} + class="-mx-3" /* -margin to distinuish from content */ > {#if loaded}
{@render children()}
{/if} - {loaded}
diff --git a/docs/src/routes/docs/+layout.svelte b/docs/src/routes/docs/+layout.svelte index 69ada9d35..85814df0f 100644 --- a/docs/src/routes/docs/+layout.svelte +++ b/docs/src/routes/docs/+layout.svelte @@ -43,7 +43,7 @@
import { dev } from '$app/environment'; + import { onMount } from 'svelte'; import { getSettings } from 'layerchart'; import { Button, @@ -29,6 +30,11 @@ const url = $derived(`${origin}${page.url.pathname})`); let { data, children } = $props(); + let loaded = $state(false); + onMount(() => { + loaded = true; + }); + const { metadata } = $derived(data); // Derive examples reactively so changes propagate to child components @@ -108,9 +114,7 @@ - import Example from '$lib/components/Example.svelte'; - import { page } from '$app/state'; - import H2 from '$lib/markdown/components/h2.svelte'; + import { Table } from 'svelte-ux'; + import type { Component } from 'content-collections'; - import ComponentLink from '$lib/components/ComponentLink.svelte'; + import { h2 as H2 } from '$lib/markdown/blueprints/default/blueprint.svelte'; + import { tableCell } from '@layerstack/svelte-table'; import ExampleListing from '$lib/components/ExampleListing.svelte'; - import OpenWithButton from '$lib/components/OpenWithButton.svelte'; + import { page } from '$app/state'; + + import RelatedLink from '$lib/components/RelatedLink.svelte'; + import type { PropertyInfo } from '$lib/api-types.js'; let { data } = $props(); + const { PageComponent, catalog } = $derived(data); + const metadata = $derived(data.metadata as Component); + const api = $derived(metadata.api); + - const example = $derived(page.params.example!); - const component = $derived(page.url.searchParams.get('component') ?? page.params.name!); + + - const exampleInfo = $derived(data.catalog?.examples.find((e) => e.name === example)); - +{#if catalog && (catalog.examples?.length || catalog.usage?.length)} +
+ +
+{/if} - +{#if api?.properties.length} +

API Reference

-

Components

-
- {#each exampleInfo?.components as componentUsage} - - {/each} -
+ + + {#each (data ?? []) as rowData, rowIndex (rowIndex)} + {@const row = rowData as PropertyInfo} + + {#each columns as column (column.name)} + {@const value = getCellValue(column, rowData, rowIndex)} -{#if data.catalog} -
- +
+ {/each} + + {:else} + + + + {/each} + +
+ {#if column.name === 'name'} +
+ {value} + {#if row.required} + required + {/if} +
+ {:else if column.name === 'type'} + {value} + {:else if column.name === 'description'} + {#if row.descriptionHtml} + {@html row.descriptionHtml} + {:else} + {value} + {/if} + {#if row.default != null} +
+ default: {row.default} +
+ {/if} + {:else} + {getCellContent(column, rowData, rowIndex)} + {/if} +
No properties
+ + {#if api.extends?.length} +
+ also available: +
+ {#each api.extends as extended} + {extended.name} + {/each} +
+
+ {/if} +{/if} + +{#if metadata.related.length} + +
+ {#each metadata.related as related} + + {/each}
{/if} diff --git a/docs/src/routes/docs/components/[name]/[example]/+page.svelte b/docs/src/routes/docs/components/[name]/[example]/+page.svelte index 0ff3daef5..0e737d1aa 100644 --- a/docs/src/routes/docs/components/[name]/[example]/+page.svelte +++ b/docs/src/routes/docs/components/[name]/[example]/+page.svelte @@ -5,7 +5,6 @@ import ComponentLink from '$lib/components/ComponentLink.svelte'; import ExampleListing from '$lib/components/ExampleListing.svelte'; - import OpenWithButton from '$lib/components/OpenWithButton.svelte'; let { data } = $props(); @@ -15,10 +14,6 @@ const exampleInfo = $derived(data.catalog?.examples.find((e) => e.name === example)); -
- -
-

Components

From 1014f48c38554c93a0b7565927d7b78e39b46a9d Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Mon, 4 May 2026 19:16:45 -0400 Subject: [PATCH 3/6] hideybar prior attempt using CSS only was not successful - not supported safari or firefox yet - was not consitently showing on load. Complicated interaction with data loading and view-transitions. Went with attachment to make hideybar adjusts to horizontal resizes including sidebar, and TOC showing. --- docs/src/lib/attachments/hideybar.ts | 64 +++++++++++ .../lib/components/HideyBar Issue README.md | 105 ------------------ docs/src/lib/components/Hidybar.svelte | 63 ----------- .../docs/components/[name]/+layout.svelte | 13 ++- 4 files changed, 71 insertions(+), 174 deletions(-) create mode 100644 docs/src/lib/attachments/hideybar.ts delete mode 100644 docs/src/lib/components/HideyBar Issue README.md delete mode 100644 docs/src/lib/components/Hidybar.svelte diff --git a/docs/src/lib/attachments/hideybar.ts b/docs/src/lib/attachments/hideybar.ts new file mode 100644 index 000000000..da6522e1d --- /dev/null +++ b/docs/src/lib/attachments/hideybar.ts @@ -0,0 +1,64 @@ +import { on } from 'svelte/events'; + +/** + * Hides a sticky bar on scroll down, reveals on scroll up. + * @param {HTMLElement} node + * @param {{ offsetTop?: string, mx?: string, threshold?: number }} [params] + * @param {string} [params.offsetTop='0px'] - The top offset of the sticky bar + * @param {string} [params.mx='0px'] - The "margin-inline" around the sticky bar + * @param {number} [params.threshold=0] - The scroll threshold at which to hide the bar + */ +export function hideybar(node: HTMLElement, { offsetTop = '0px', mx = '0px', threshold = 0 } = {}) { + const parent = node.parentElement; + if (!parent) return; + + const spacer = document.createElement('div'); + node.insertAdjacentElement('afterend', spacer); + + node.style.position = 'fixed'; + node.style.top = offsetTop; + node.style.transform = 'translateY(0)'; + node.style.transition = 'transform 200ms ease-in-out'; + node.style.overflow = 'hidden'; + + const syncSize = () => { + const parentRect = parent.getBoundingClientRect(); + node.style.left = `calc(${parentRect.left}px + ${mx})`; + node.style.width = `calc(${parentRect.width}px - ${mx} * 2)`; + const h = node.getBoundingClientRect().height; + if (h > 0) spacer.style.height = `${h}px`; + }; + syncSize(); + + const ro = new ResizeObserver(syncSize); + ro.observe(parent); + ro.observe(node); + + let lastScrollY = window.scrollY; + let isHidden = false; + const cleanupScroll = on( + window, + 'scroll', + () => { + const currentScrollY = window.scrollY; + const goingUp = currentScrollY < lastScrollY; + const shouldHide = !goingUp && currentScrollY > threshold; + if (shouldHide !== isHidden) { + node.style.transform = shouldHide + ? `translateY(calc(-100% - ${offsetTop}))` + : 'translateY(0)'; + isHidden = shouldHide; + } + lastScrollY = currentScrollY; + }, + { passive: true } + ); + + return { + destroy() { + cleanupScroll(); + ro.disconnect(); + spacer.remove(); + } + }; +} diff --git a/docs/src/lib/components/HideyBar Issue README.md b/docs/src/lib/components/HideyBar Issue README.md deleted file mode 100644 index 0b373d25b..000000000 --- a/docs/src/lib/components/HideyBar Issue README.md +++ /dev/null @@ -1,105 +0,0 @@ - Why is hidybar not ALWAYS showing after page nagivation? -I tried all 3 without success, leaving here to jump start your roubleshooting. - - Root Cause - - The bug is an interaction between three things working against each other: - - 1. The hidybar's visibility mechanism - - /* Hidybar.svelte */ - @container scroll-state(scrolled: bottom) { - translate: 0 calc(-100% - var(--header-height)); /* HIDE */ - } - @container scroll-state(scrolled: top) { - translate: none; /* SHOW */ - } - The bar is hidden when scrolled: bottom is true. Showing it is triggered by - scrolled: top via a CSS translate transition (150ms, from the Tailwind - transition-[translate] class on the header). - - 2. The View Transitions API wrapping navigations - - In page-transitions.ts: - document.startViewTransition(async () => { - resolve(); // ← SvelteKit navigates, resets scroll to 0 - await navigation.complete; // ← waits for nav to finish - // browser takes NEW snapshot here ↑ - }); - - 3. The timing mismatch - - When the user was scrolled to the bottom of Page A (hidybar hidden), then - navigates to Page B: - - 1. Old snapshot taken → hidybar is fully hidden (translated off-screen) - 2. SvelteKit's resolve() runs → new page renders, window.scrollTo(0, 0) is - called - 3. scroll-state(scrolled: top) becomes true → browser starts the CSS translate - transition (hidden → none, 150ms) - 4. navigation.complete resolves → callback returns → new snapshot taken - 5. The new snapshot is captured at time ≈ 0ms of the 150ms CSS transition — the - hidybar is still essentially fully translated off-screen - 6. View transition animation plays: old (fully hidden) → new (still fully - hidden) — the bar is invisible during the entire animation (~250ms) - 7. View transition ends, pseudo-elements are removed, actual elements revealed → - by this point 250ms have passed, the 150ms CSS transition has completed → - hidybar snaps into view - - Why "sometimes": This only happens when the user was scrolled to the bottom of - the previous page (the only condition that hides the bar). If the hidybar was - already visible, both old and new snapshots show it visible — no issue. - - In short: - - The view transition captures a "new page" snapshot before the CSS translate - transition triggered by the scroll-state change has had any time to animate. So - both snapshots effectively show the hidden state, and the bar only appears after - the view transition animation ends — it looks like it's missing during the - navigation. - -✻ Sautéed for 7m 43s - -❯ asking how would that be fixed? - -⏺ There are a few approaches, from simplest to most correct: - - --- - Option 1: Disable the CSS transition during view transitions (simplest) - - In app.css, the code already does this pattern for scroll-behavior: - html:active-view-transition { - scroll-behavior: auto; - } - - The same pattern would fix the hidybar — add: - html:active-view-transition header.hidybar { - transition-duration: 0ms; - } - - During an active view transition, the translate snaps instantly when - scroll-state(scrolled: top) fires. So when the new snapshot is taken, the - hidybar is already at translate: none (fully visible). The view transition's own - animation provides visual smoothness — no need for the CSS transition too. - - --- - Option 2: Give the hidybar its own view-transition-name - - Add view-transition-name: hidybar to the
. The browser then captures it - separately from the page root and you can control its animation independently - via ::view-transition-old(hidybar) / ::view-transition-new(hidybar) — e.g., - slide it in from the top rather than cross-fade with the page. More control, - more code. - - --- - Option 3: Delay the new snapshot - - After await navigation.complete in page-transitions.ts, add await new Promise(r - => setTimeout(r, 160)) — just long enough for the 150ms CSS transition to - complete before the snapshot is taken. The new snapshot then captures the bar as - fully visible. Works, but adds latency to every navigation. - - --- - Option 1 is the right call — one line in app.css, uses the existing - html:active-view-transition pattern already in the file, and doesn't change the - Hidybar component at all. \ No newline at end of file diff --git a/docs/src/lib/components/Hidybar.svelte b/docs/src/lib/components/Hidybar.svelte deleted file mode 100644 index 0e9104c4e..000000000 --- a/docs/src/lib/components/Hidybar.svelte +++ /dev/null @@ -1,63 +0,0 @@ - - - -
- {#if loaded} -
- {@render children()} -
- {/if} -
- - diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 883396b1b..4439e3b54 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -16,7 +16,7 @@ import LoadingPlaceholder from '$lib/components/LoadingPlaceholder.svelte'; import OpenWithButton from '$lib/components/OpenWithButton.svelte'; import ScrollToTop from '~icons/lucide/arrow-up-to-line'; - import Hidybar from '$lib/components/Hidybar.svelte'; + import { hideybar } from '$lib/attachments/hideybar'; import { examples } from '$lib/context.js'; import { intersectExampleLayers } from '$lib/utils/layers.js'; import { page } from '$app/state'; @@ -113,11 +113,12 @@ ]); -
@@ -173,7 +174,7 @@
-
+

Date: Tue, 5 May 2026 17:18:48 -0400 Subject: [PATCH 4/6] hideybar convert action -> attachment --- docs/src/lib/attachments/hideybar.ts | 95 +++++++++---------- .../docs/components/[name]/+layout.svelte | 4 +- 2 files changed, 46 insertions(+), 53 deletions(-) diff --git a/docs/src/lib/attachments/hideybar.ts b/docs/src/lib/attachments/hideybar.ts index da6522e1d..3ac78683c 100644 --- a/docs/src/lib/attachments/hideybar.ts +++ b/docs/src/lib/attachments/hideybar.ts @@ -1,64 +1,57 @@ import { on } from 'svelte/events'; -/** - * Hides a sticky bar on scroll down, reveals on scroll up. - * @param {HTMLElement} node - * @param {{ offsetTop?: string, mx?: string, threshold?: number }} [params] - * @param {string} [params.offsetTop='0px'] - The top offset of the sticky bar - * @param {string} [params.mx='0px'] - The "margin-inline" around the sticky bar - * @param {number} [params.threshold=0] - The scroll threshold at which to hide the bar - */ -export function hideybar(node: HTMLElement, { offsetTop = '0px', mx = '0px', threshold = 0 } = {}) { - const parent = node.parentElement; - if (!parent) return; +/** Hides a sticky bar on scroll down, reveals on scroll up. */ +export function hideybar({ offsetTop = '0px', mx = '0px', threshold = 0 } = {}) { + return (node: HTMLElement) => { + const parent = node.parentElement; + if (!parent) return; - const spacer = document.createElement('div'); - node.insertAdjacentElement('afterend', spacer); + const spacer = document.createElement('div'); + node.insertAdjacentElement('afterend', spacer); - node.style.position = 'fixed'; - node.style.top = offsetTop; - node.style.transform = 'translateY(0)'; - node.style.transition = 'transform 200ms ease-in-out'; - node.style.overflow = 'hidden'; + node.style.position = 'fixed'; + node.style.top = offsetTop; + node.style.transform = 'translateY(0)'; + node.style.transition = 'transform 200ms ease-in-out'; + node.style.overflow = 'hidden'; - const syncSize = () => { - const parentRect = parent.getBoundingClientRect(); - node.style.left = `calc(${parentRect.left}px + ${mx})`; - node.style.width = `calc(${parentRect.width}px - ${mx} * 2)`; - const h = node.getBoundingClientRect().height; - if (h > 0) spacer.style.height = `${h}px`; - }; - syncSize(); + const syncSize = () => { + const parentRect = parent.getBoundingClientRect(); + node.style.left = `calc(${parentRect.left}px + ${mx})`; + node.style.width = `calc(${parentRect.width}px - ${mx} * 2)`; + const h = node.getBoundingClientRect().height; + if (h > 0) spacer.style.height = `${h}px`; + }; + syncSize(); - const ro = new ResizeObserver(syncSize); - ro.observe(parent); - ro.observe(node); + const ro = new ResizeObserver(syncSize); + ro.observe(parent); + ro.observe(node); - let lastScrollY = window.scrollY; - let isHidden = false; - const cleanupScroll = on( - window, - 'scroll', - () => { - const currentScrollY = window.scrollY; - const goingUp = currentScrollY < lastScrollY; - const shouldHide = !goingUp && currentScrollY > threshold; - if (shouldHide !== isHidden) { - node.style.transform = shouldHide - ? `translateY(calc(-100% - ${offsetTop}))` - : 'translateY(0)'; - isHidden = shouldHide; - } - lastScrollY = currentScrollY; - }, - { passive: true } - ); + let lastScrollY = window.scrollY; + let isHidden = false; + const cleanupScroll = on( + window, + 'scroll', + () => { + const currentScrollY = window.scrollY; + const goingUp = currentScrollY < lastScrollY; + const shouldHide = !goingUp && currentScrollY > threshold; + if (shouldHide !== isHidden) { + node.style.transform = shouldHide + ? `translateY(calc(-100% - ${offsetTop}))` + : 'translateY(0)'; + isHidden = shouldHide; + } + lastScrollY = currentScrollY; + }, + { passive: true } + ); - return { - destroy() { + return () => { cleanupScroll(); ro.disconnect(); spacer.remove(); - } + }; }; } diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 4439e3b54..dceaf6c90 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -16,7 +16,7 @@ import LoadingPlaceholder from '$lib/components/LoadingPlaceholder.svelte'; import OpenWithButton from '$lib/components/OpenWithButton.svelte'; import ScrollToTop from '~icons/lucide/arrow-up-to-line'; - import { hideybar } from '$lib/attachments/hideybar'; + import { hideybar } from '$lib/attachments/hideybar.js'; import { examples } from '$lib/context.js'; import { intersectExampleLayers } from '$lib/utils/layers.js'; import { page } from '$app/state'; @@ -114,7 +114,7 @@
Date: Sun, 10 May 2026 10:15:33 -0400 Subject: [PATCH 5/6] Move "Scroll to top" as separate button in bottom right --- .../docs/components/[name]/+layout.svelte | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index dceaf6c90..6113bdebe 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -1,6 +1,7 @@