fix: stop icons remounting and shelves collapsing into nothing

This commit is contained in:
2026-08-11 20:08:53 -04:00
parent c09b8365fa
commit ef8e5e7fba
3 changed files with 99 additions and 75 deletions
@@ -23,8 +23,10 @@
// Start observing
resizeObserver.observe(scrollContainer);
// Initial check with delay
setTimeout(updateScrollState, 0);
// Measure synchronously. Reading scrollWidth/clientWidth forces layout, so
// the numbers are already accurate here — deferring to a macrotask just
// guaranteed one painted frame with the arrows in the wrong state.
updateScrollState();
// Cleanup
return () => {
@@ -33,12 +35,14 @@
});
const scrollLeft = () => {
if (!scrollContainer) return;
scrollContainer.scrollBy({ left: -scrollContainer.clientWidth, behavior: 'smooth' });
// Update state after scroll
setTimeout(updateScrollState, 100);
};
const scrollRight = () => {
if (!scrollContainer) return;
scrollContainer.scrollBy({ left: scrollContainer.clientWidth, behavior: 'smooth' });
// Update state after scroll
setTimeout(updateScrollState, 100);
@@ -61,29 +65,32 @@
{#if books.length > 0}
<div class="flex w-full flex-col">
<div class="flex items-center">
<h1 class="ml-4 text-xl font-semibold">{title}</h1>
{#if needsScroll}
<div class="ml-auto">
<button
type="button"
disabled={!canScrollLeft}
onclick={scrollLeft}
class={`${canScrollLeft ? 'text-primary/70 hover:text-primary' : 'text-primary/30'}`}
aria-label="Scroll Left"
>
<ChevronLeft size="20" strokeWidth="3" />
</button>
<button
type="button"
disabled={!canScrollRight}
onclick={scrollRight}
class={`${canScrollRight ? 'text-primary/70 hover:text-primary' : 'text-primary/30'}`}
aria-label="Scroll Right"
>
<ChevronRight size="20" strokeWidth="3" />
</button>
</div>
{/if}
<h1 class="ml-4 font-serif text-xl font-semibold">{title}</h1>
<!--
Always mounted, only hidden. `needsScroll` is measured after mount,
so mounting on it made the arrows pop in a frame late and shift the
header; `invisible` keeps the space reserved from the first paint.
-->
<div class="ml-auto {needsScroll ? '' : 'invisible'}" aria-hidden={!needsScroll}>
<button
type="button"
disabled={!canScrollLeft}
onclick={scrollLeft}
class={`${canScrollLeft ? 'text-primary/70 hover:text-primary' : 'text-primary/30'}`}
aria-label="Scroll Left"
>
<ChevronLeft size="20" strokeWidth="3" />
</button>
<button
type="button"
disabled={!canScrollRight}
onclick={scrollRight}
class={`${canScrollRight ? 'text-primary/70 hover:text-primary' : 'text-primary/30'}`}
aria-label="Scroll Right"
>
<ChevronRight size="20" strokeWidth="3" />
</button>
</div>
</div>
<div
bind:this={scrollContainer}