feat(web): pair scroll arrows beside section title; bleed scroller to viewport edge

Restructures HorizontalScrollRow so paired arrows sit in a flex header
beside an optional section title (instead of absolute-positioned at the
row's left/right edges). Adds margin-right: -1rem to the row so the
scroller's items extend to the viewport's right edge, escaping Shell's
p-4 padding. The header bar keeps its right gutter so titles align with
the rest of the page content.

Home page passes 'title' to the first row of each section; subsequent
rows in multi-row sections render arrow-only headers.
This commit is contained in:
2026-05-01 21:05:16 -04:00
parent 4412a4af0c
commit 4e47b2e7f5
3 changed files with 95 additions and 48 deletions
+37 -17
View File
@@ -30,14 +30,18 @@
{:else if data}
<!-- Recently added: 50 albums in 2 rows of 25 -->
<section class="space-y-3">
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Recently added</h2>
</header>
{#if data.recently_added_albums.length === 0}
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Recently added</h2>
</header>
<p class="text-text-secondary">Nothing added yet. Scan a folder via the server's config.</p>
{:else}
{#each chunk(data.recently_added_albums, 25) as row, i (i)}
<HorizontalScrollRow items={row} ariaLabel="Recently added row {i + 1}">
<HorizontalScrollRow
items={row}
title={i === 0 ? 'Recently added' : undefined}
ariaLabel="Recently added row {i + 1}"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
<div class="w-44"><AlbumCard {album} /></div>
{/snippet}
@@ -48,21 +52,29 @@
<!-- Rediscover: albums + artists -->
<section class="space-y-3">
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Rediscover</h2>
</header>
{#if data.rediscover_albums.length === 0 && data.rediscover_artists.length === 0}
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Rediscover</h2>
</header>
<p class="text-text-secondary">No forgotten favourites yet. Like some albums or artists to fill this in.</p>
{:else}
{#if data.rediscover_albums.length > 0}
<HorizontalScrollRow items={data.rediscover_albums} ariaLabel="Rediscover albums">
<HorizontalScrollRow
items={data.rediscover_albums}
title="Rediscover"
ariaLabel="Rediscover albums"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
<div class="w-44"><AlbumCard {album} /></div>
{/snippet}
</HorizontalScrollRow>
{/if}
{#if data.rediscover_artists.length > 0}
<HorizontalScrollRow items={data.rediscover_artists} ariaLabel="Rediscover artists">
<HorizontalScrollRow
items={data.rediscover_artists}
title={data.rediscover_albums.length === 0 ? 'Rediscover' : undefined}
ariaLabel="Rediscover artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}
@@ -73,14 +85,18 @@
<!-- Most played: 75 tracks in 3 rows of 25 -->
<section class="space-y-3">
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Most played</h2>
</header>
{#if data.most_played_tracks.length === 0}
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Most played</h2>
</header>
<p class="text-text-secondary">No plays to draw from. Listen to something.</p>
{:else}
{#each chunk(data.most_played_tracks, 25) as row, i (i)}
<HorizontalScrollRow items={row} ariaLabel="Most played row {i + 1}">
<HorizontalScrollRow
items={row}
title={i === 0 ? 'Most played' : undefined}
ariaLabel="Most played row {i + 1}"
>
{#snippet item(track: import('$lib/api/types').TrackRef, idx: number)}
<CompactTrackCard
{track}
@@ -95,13 +111,17 @@
<!-- Last played artists -->
<section class="space-y-3">
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Last played</h2>
</header>
{#if data.last_played_artists.length === 0}
<header>
<h2 class="font-display text-2xl font-medium text-text-primary">Last played</h2>
</header>
<p class="text-text-secondary">No recent plays.</p>
{:else}
<HorizontalScrollRow items={data.last_played_artists} ariaLabel="Last played artists">
<HorizontalScrollRow
items={data.last_played_artists}
title="Last played"
ariaLabel="Last played artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}