From 3fda483688b6f11b89ffeb5a43aee11ae2e78d8b Mon Sep 17 00:00:00 2001 From: Nicolas Date: Wed, 16 Sep 2026 19:20:01 +0200 Subject: [PATCH] Make the library page header (title + search bar) fully sticky The search bar was already sticky on its own; the title above it still scrolled away. Wraps both in one sticky .libraryHeader block, scoped to views/library only, and stacks it below the mobile shell's own sticky top bar instead of racing it for the same spot. Co-Authored-By: Claude Sonnet 5 --- README.md | 2 ++ client/src/shell/Shell.css | 3 +- client/src/styles/base.css | 1 + client/src/views/library/Library.css | 24 +++++++++++++ client/src/views/library/Library.html | 6 ++-- client/src/views/library/Library.ts | 1 + docs/architecture.md | 52 +++++++++++++++++++++++++++ 7 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 client/src/views/library/Library.css diff --git a/README.md b/README.md index 88527db..b9eceeb 100644 --- a/README.md +++ b/README.md @@ -367,6 +367,8 @@ Then put it behind your existing reverse proxy like everything else - [x] Library/artist grid tiles shrink fluidly on a narrow phone instead of a fixed 160px floor collapsing to one column per row — see docs/architecture.md +- [x] Library page title now stays pinned alongside the search bar while + scrolling, instead of just the search bar — see docs/architecture.md - [ ] Try a full party end-to-end with two real accounts (still the main gap) - [ ] Try casting to a real Chromecast (only the SDK's own bootstrap is confirmed here — no real device/session) diff --git a/client/src/shell/Shell.css b/client/src/shell/Shell.css index 09a2301..4e10556 100644 --- a/client/src/shell/Shell.css +++ b/client/src/shell/Shell.css @@ -79,7 +79,8 @@ position: sticky; top: 0; z-index: 3; - padding: 14px 16px; + height: var(--mobile-topbar-h); + padding: 0 16px; background: var(--bg-header); } diff --git a/client/src/styles/base.css b/client/src/styles/base.css index 5748950..42506c1 100644 --- a/client/src/styles/base.css +++ b/client/src/styles/base.css @@ -14,6 +14,7 @@ --accent: #52b54b; /* Emby's theme-primary-color (hsl(116, 41.7%, 50.2%)) */ --accent-bright: #6ccf65; /* Emby's --theme-accent-text-color-darkbg */ --danger: #ff6b6b; + --mobile-topbar-h: 49px; /* shell/Shell.css's .mobileTopBar - a fixed height so any other sticky element (e.g. views/library's .libraryHeader) can stack directly below it instead of guessing its rendered height */ } * { box-sizing: border-box; } diff --git a/client/src/views/library/Library.css b/client/src/views/library/Library.css new file mode 100644 index 0000000..71cc115 --- /dev/null +++ b/client/src/views/library/Library.css @@ -0,0 +1,24 @@ +/* ---- Library page header (title + search bar) - see Library.ts/html. ---- + Wraps .pageHeader and tiles.css's .searchBar (already sticky on its own) + in one sticky block so the title stays put too, instead of scrolling + away while the search bar stays behind - reported directly. Needs its + own opaque background since .pageHeader's

has none - without it, + grid tiles scrolling underneath would show through the title text once + stuck. */ +.libraryHeader { + position: sticky; + top: 0; + z-index: 2; + background: var(--bg); + padding-top: 4px; +} + +/* Below 720px, shell/Shell.css's own sticky .mobileTopBar already occupies + top:0 - stacking this header at top:0 too would have both fighting over + the same spot, with this one (later in paint order) covering the app's + brand bar. Stick it right below instead. */ +@media (max-width: 720px) { + .libraryHeader { + top: var(--mobile-topbar-h); + } +} diff --git a/client/src/views/library/Library.html b/client/src/views/library/Library.html index 69365c4..e6d1fcd 100644 --- a/client/src/views/library/Library.html +++ b/client/src/views/library/Library.html @@ -1,4 +1,6 @@ - - +
+ + +

Aucun résultat.

Chargement...

diff --git a/client/src/views/library/Library.ts b/client/src/views/library/Library.ts index dcc0989..70a64d2 100644 --- a/client/src/views/library/Library.ts +++ b/client/src/views/library/Library.ts @@ -1,3 +1,4 @@ +import './Library.css'; import html from './Library.html'; import { getArtists, getLibraries, getLibraryItems } from '../../api/embyApi'; import { createParty } from '../../api/party'; diff --git a/docs/architecture.md b/docs/architecture.md index e1c29b3..f7ef0ca 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -3911,6 +3911,58 @@ from 320 to 393px now gets 2 columns (previously 1), 430px gets 3, and 768/1200px match what the old fixed `160px` floor already gave - confirmed empirically, not assumed from the formula alone. +## The library header wasn't fully sticky: the title still scrolled away + +"A sticky search bar" (above) only ever made the `` itself +sticky - asked for directly again, this time to also keep the page +title (`.pageHeader`'s `

`) pinned alongside it on `views/library`, +so both stay visible together while a long grid scrolls underneath +instead of just the search box. + +New `.libraryHeader` wrapper (`views/library/Library.css` - the one +view of this shape that had no dedicated stylesheet yet, everything +borrowed from `tiles.css`/`base.css`) around the existing `.pageHeader` ++ `.searchBar` pair, `position: sticky; top: 0` on the wrapper itself +with its own opaque `var(--bg)` background - the `

` has none of its +own, so without this, tiles scrolling underneath would show through the +title text once stuck. `.searchBar` keeps its own pre-existing +`position: sticky; top: 0` from `tiles.css` unchanged (still needed as-is +on `views/artist`/`views/album`, which don't get this wrapper); nested +inside an already-stuck ancestor that isn't itself scrolling, it's +inert rather than conflicting. + +Scoped to `views/library` only, not `tiles.css`'s shared `.pageHeader` - +`views/home` uses that same class for a plain, non-sticky title, and +`views/artist`/`views/album` have since moved to their own hero headers +(see "Music UI rework" above), not `.pageHeader` at all. + +One real collision worth catching: below 720px, `shell/Shell.css`'s own +`.mobileTopBar` is *also* `position: sticky; top: 0` (the app's brand +bar, sitting above `.mainContent` as a sibling, not an ancestor) - the +pre-existing sticky `.searchBar` already raced it for the same spot, +just narrowly enough (one slim input) not to get reported. Stacking a +whole title+search block at `top: 0` too would have properly covered +it, later in paint order. `.mobileTopBar` got an explicit +`height: var(--mobile-topbar-h)` (new `base.css` token, `49px` - +replacing its previous padding-derived auto height with a fixed one +anything else can reliably stack against), and `.libraryHeader` sticks +to `top: var(--mobile-topbar-h)` instead of `0` under that same 720px +breakpoint - directly below the brand bar instead of underneath it. + +Verified: full `typecheck`/`build` (client only). No Emby session is +needed to see either state, so checked directly: real compiled +`app.css` loaded (same-origin, from the running dev server) into two +`