Android: the album detail page should scroll as one page, and fit the screen width #66

Closed
opened 2026-08-18 06:00:07 +00:00 by logan · 4 comments
Collaborator

Report

Opening an album on mobile, the album header does not scroll — only the tracklist does. It should all be one normally-scrolling page. The content is also slightly wider than the phone screen and gets cut off.

Findings

  • explore-album-details is built as a fixed header over a scrolling tracklist, which is the desktop arrangement.
  • The overflow is the documented grid trap: a grid item's implicit minimum is its content, so one child insisting on more than the viewport makes the whole body wider and overflow-x: hidden then hides part of the app. Every box between the viewport and the shrinkable content needs min-width: 0, and anything that genuinely cannot shrink must say so in its own stylesheet.
  • A media query inside a shadow root is answered by the viewport, so this component states its own phone layout rather than the shell reaching in.

Direction

At phone widths, make the whole page one scroll container with the header scrolling away (optionally collapsing to a compact title bar). Fix the width overflow with min-width: 0 down the chain and check the header's own row — release metadata, badges and the version selector are the likely culprits. Verify on the real device, not a resized Chromium.

**Report** Opening an album on mobile, the album header does not scroll — only the tracklist does. It should all be one normally-scrolling page. The content is also slightly wider than the phone screen and gets cut off. **Findings** - `explore-album-details` is built as a fixed header over a scrolling tracklist, which is the desktop arrangement. - The overflow is the documented grid trap: **a grid item's implicit minimum is its content**, so one child insisting on more than the viewport makes the whole body wider and `overflow-x: hidden` then hides part of the app. Every box between the viewport and the shrinkable content needs `min-width: 0`, and anything that genuinely cannot shrink must say so in its own stylesheet. - A media query inside a shadow root is answered by the viewport, so this component states its own phone layout rather than the shell reaching in. **Direction** At phone widths, make the whole page one scroll container with the header scrolling away (optionally collapsing to a compact title bar). Fix the width overflow with `min-width: 0` down the chain and check the header's own row — release metadata, badges and the version selector are the likely culprits. Verify on the real device, not a resized Chromium.
logan added the Area/ExploreKind/BugPlatform/Android
Priority
High
2
labels 2026-08-18 14:36:16 +00:00
Author
Collaborator

Measured while doing #17, since that Direction names the version
selector as a likely culprit for the width overflow. Two findings, and
one of them takes an item off this issue's list.

The version selector is no longer in the header's row at all. #17
moved it below the tracklist into a collapsed disclosure, so whatever
this issue has to fix in the header, that is not part of it. The
disclosure itself measures clean at phone width: 390×800, panel open,
document.body.scrollWidth === window.innerWidth === 390.

The header row is genuinely broken at 390px, and it is the cover
art.
Chromium at 390×800, album page open:

  • the cover art keeps its full desktop size (~200px of a 390px
    viewport, plus the back button and the shell's gutters);
  • .album-info is squeezed to roughly 110px, so the title renders as
    a single ellipsised glyph — an album called Glass Harbour draws as
    G…;
  • the "Request this" button is wider than what is left and is clipped
    at the right edge of its container.

Note what this is not: body.scrollWidth equals the viewport, so
nothing overflows the document and the min-width: 0 chain this issue
describes is doing its job. The clipping is inside the header's own
flex row — a fixed-size sibling next to a shrinkable one — so it is
invisible to layout-overflow.spec.ts, which asserts the shell needs
no sideways scrolling. That is why no tier has caught it: the shell is
correct and the header is not.

Reproduction without a device, which is enough for the width half
(the scroll half still needs the real thing):

make dev-headless SEED=default
playwright-cli -s=yj open http://localhost:34115
playwright-cli -s=yj eval "() => document.querySelector('#main-content')
  ?.dispatchEvent(new CustomEvent('navigate', {bubbles:true, composed:true,
  detail:{view:'explore-album-details', releaseGroupMBID:'rg-demo',
  albumName:'Glass Harbour'}}))"
playwright-cli -s=yj resize 390 800
playwright-cli -s=yj screenshot

So the header's phone layout needs the art to shrink (or to stack above
the text) rather than another min-width: 0.

Measured while doing #17, since that Direction names the version selector as a likely culprit for the width overflow. Two findings, and one of them takes an item off this issue's list. **The version selector is no longer in the header's row at all.** #17 moved it below the tracklist into a collapsed disclosure, so whatever this issue has to fix in the header, that is not part of it. The disclosure itself measures clean at phone width: 390×800, panel open, `document.body.scrollWidth === window.innerWidth === 390`. **The header row is genuinely broken at 390px, and it is the cover art.** Chromium at 390×800, album page open: - the cover art keeps its full desktop size (~200px of a 390px viewport, plus the back button and the shell's gutters); - `.album-info` is squeezed to roughly 110px, so the title renders as a single ellipsised glyph — an album called *Glass Harbour* draws as `G…`; - the "Request this" button is wider than what is left and is clipped at the right edge of its container. Note what this is *not*: `body.scrollWidth` equals the viewport, so nothing overflows the document and the `min-width: 0` chain this issue describes is doing its job. The clipping is inside the header's own flex row — a fixed-size sibling next to a shrinkable one — so it is invisible to `layout-overflow.spec.ts`, which asserts the shell needs no sideways scrolling. That is why no tier has caught it: the shell is correct and the header is not. **Reproduction without a device**, which is enough for the width half (the scroll half still needs the real thing): ``` make dev-headless SEED=default playwright-cli -s=yj open http://localhost:34115 playwright-cli -s=yj eval "() => document.querySelector('#main-content') ?.dispatchEvent(new CustomEvent('navigate', {bubbles:true, composed:true, detail:{view:'explore-album-details', releaseGroupMBID:'rg-demo', albumName:'Glass Harbour'}}))" playwright-cli -s=yj resize 390 800 playwright-cli -s=yj screenshot ``` So the header's phone layout needs the art to shrink (or to stack above the text) rather than another `min-width: 0`.
Author
Collaborator

Measured, while verifying #72 at 390×780 — and the "cut off" half is
worse than "slightly wider".
Found by reading a screenshot; no
assertion in any tier reports it.

On explore-album-details for a library album, with the cover art
beside the info column:

box width right edge scrollWidth
body 390 390 390
main.main-panel 390 390 390
explore-album-details 390 390 443
div.album-header 390 390 443
div.album-info 78 370 151
div.album-actions 78 370 151
wa-button "Shuffle album" 151 443 151
wa-button "Add to queue" 148 440 148

So the shell is fine — body.scrollWidth === clientWidth === 390,
which is what layout-overflow.spec.ts asserts and why it passes. The
overflow is entirely inside the component and is clipped by
explore-album-details's own overflow-x: hidden: "Shuffle album"
is cut by 53px and "Add to queue" by 50px
, and neither can be reached
by any gesture. Two of the album page's three primary actions are
unavailable on a phone, silently.

The interesting number is album-info at 78px inside a 390px
viewport. It carries min-width: 0 correctly, so it is shrinking as
asked — it is being squeezed by the art beside it, and the buttons
inside it then overflow their own 78px column rather than wrapping.
That says the Direction's min-width: 0 sweep is necessary and not
sufficient here: at phone width the header has to stack (art over
info) rather than share a row, or the info column has nothing to be
wide with.

Same 390px, same seed: make dev-headless SEED=default, open any
library album, read getBoundingClientRect().right on the header's
buttons against a 390px viewport.

Worth noting for whoever takes this: #69's page-header actions API
does not apply — this header is the component's own markup, not
<page-header>, so the collapse-into-a-menu behaviour is not inherited
and the fix here is layout.

**Measured, while verifying #72 at 390×780 — and the "cut off" half is worse than "slightly wider".** Found by reading a screenshot; no assertion in any tier reports it. On `explore-album-details` for a library album, with the cover art beside the info column: | box | width | right edge | scrollWidth | |---|---|---|---| | `body` | 390 | 390 | 390 | | `main.main-panel` | 390 | 390 | 390 | | `explore-album-details` | 390 | 390 | **443** | | `div.album-header` | 390 | 390 | **443** | | `div.album-info` | **78** | 370 | 151 | | `div.album-actions` | **78** | 370 | 151 | | `wa-button` "Shuffle album" | 151 | **443** | 151 | | `wa-button` "Add to queue" | 148 | **440** | 148 | So the *shell* is fine — `body.scrollWidth === clientWidth === 390`, which is what `layout-overflow.spec.ts` asserts and why it passes. The overflow is entirely inside the component and is clipped by `explore-album-details`'s own `overflow-x: hidden`: **"Shuffle album" is cut by 53px and "Add to queue" by 50px**, and neither can be reached by any gesture. Two of the album page's three primary actions are unavailable on a phone, silently. The interesting number is `album-info` at **78px** inside a 390px viewport. It carries `min-width: 0` correctly, so it is shrinking as asked — it is being squeezed by the art beside it, and the buttons inside it then overflow their own 78px column rather than wrapping. That says the Direction's `min-width: 0` sweep is necessary and not sufficient here: at phone width the header has to *stack* (art over info) rather than share a row, or the info column has nothing to be wide with. Same 390px, same seed: `make dev-headless SEED=default`, open any library album, read `getBoundingClientRect().right` on the header's buttons against a 390px viewport. Worth noting for whoever takes this: #69's `page-header` actions API does not apply — this header is the component's own markup, not `<page-header>`, so the collapse-into-a-menu behaviour is not inherited and the fix here is layout.
logan self-assigned this 2026-08-21 08:33:06 +00:00
logan added the
Status
In Progress
label 2026-08-21 08:33:07 +00:00
Author
Collaborator

Picking this up. Branch fix/66-album-page-scrolls-as-one.

Approach, from the two measurements already on this issue rather
than from first principles:

  • The scroll half: explore-album-details is :host { overflow: hidden }
    with a flex-shrink: 0 header over a .content that owns the only
    scroller — the desktop arrangement. Below 600px the host becomes the
    scroller and .content stops being one, so the header scrolls away
    and the page moves as one. The tracklist is plain DOM, not a
    virtualizer, so nothing else inside wants its own scroll window.
  • The width half: the header's flex row stacks (art over info) at phone
    width, which is what the 2026-08-19 comment concluded — .album-info
    measured 78px inside a 390px viewport with min-width: 0 already
    doing its job, so another min-width: 0 is not the fix. Stacked, the
    info column has the row's whole width and "Shuffle album" / "Add to
    queue" stop overflowing their 78px column.

Both live in the component's own stylesheet as a @media (max-width: 599px) block, since a media query inside a shadow root is answered by
the viewport.

Verification is a browser at the reference device's 424x439 plus a
screenshot and an e2e spec measuring each header control against the
component's box — not a device, which I do not have. The engine half of
this (Chrome 113) is a plain flex-direction change and a scroll
container, nowhere near that engine's documented gaps, but I will say
so on the PR rather than claim the device tier.

Picking this up. Branch `fix/66-album-page-scrolls-as-one`. **Approach**, from the two measurements already on this issue rather than from first principles: - The scroll half: `explore-album-details` is `:host { overflow: hidden }` with a `flex-shrink: 0` header over a `.content` that owns the only scroller — the desktop arrangement. Below 600px the host becomes the scroller and `.content` stops being one, so the header scrolls away and the page moves as one. The tracklist is plain DOM, not a virtualizer, so nothing else inside wants its own scroll window. - The width half: the header's flex row stacks (art over info) at phone width, which is what the 2026-08-19 comment concluded — `.album-info` measured 78px inside a 390px viewport with `min-width: 0` already doing its job, so another `min-width: 0` is not the fix. Stacked, the info column has the row's whole width and "Shuffle album" / "Add to queue" stop overflowing their 78px column. Both live in the component's own stylesheet as a `@media (max-width: 599px)` block, since a media query inside a shadow root is answered by the viewport. Verification is a browser at the reference device's 424x439 plus a screenshot and an e2e spec measuring each header control against the component's box — not a device, which I do not have. The engine half of this (Chrome 113) is a plain flex-direction change and a scroll container, nowhere near that engine's documented gaps, but I will say so on the PR rather than claim the device tier.
Author
Collaborator

PR: #179 — CI green on both engines (run 17385).

The fix is one @media (max-width: 599px) block in the component's own stylesheet: the host becomes the scroller and .content stops being one, so the header scrolls away and the page moves as one, and the header's flex row stacks so .album-info has the row's whole width instead of 112px of it.

Both halves this issue reports are measured before and after at 424×439, in the PR body. The clipped actions were the worse half: "Shuffle album" ended at x=443 inside a 424px component and could not be reached by any gesture.

Not done, deliberately: the sticky compact title bar this issue offers as an "optionally" (the header scrolling away is what the report asks for), and the device tier — Chrome 113 is unverified, which is why this stays open until somebody can look at it on the phone. explore-artist-details is the same shape one page over and is untouched here; it deserves its own measurement rather than a wider diff.

Leaving Status/In Progress on, per the workflow — not merging.

PR: https://git.ljones.me/yonlu/yellowjacket/pulls/179 — CI green on both engines (run 17385). The fix is one `@media (max-width: 599px)` block in the component's own stylesheet: the host becomes the scroller and `.content` stops being one, so the header scrolls away and the page moves as one, and the header's flex row stacks so `.album-info` has the row's whole width instead of 112px of it. Both halves this issue reports are measured before and after at 424×439, in the PR body. The clipped actions were the worse half: "Shuffle album" ended at x=443 inside a 424px component and could not be reached by any gesture. Not done, deliberately: the sticky compact title bar this issue offers as an "optionally" (the header scrolling away is what the report asks for), and the **device tier** — Chrome 113 is unverified, which is why this stays open until somebody can look at it on the phone. `explore-artist-details` is the same shape one page over and is untouched here; it deserves its own measurement rather than a wider diff. Leaving `Status/In Progress` on, per the workflow — not merging.
logan closed this issue 2026-08-21 14:36:20 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-21 14:36:29 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#66