Android: support small screens (Light Phone 3, 1080x1240) as well as normal ones (Pixel 7) #51

Closed
opened 2026-08-18 05:58:15 +00:00 by logan · 3 comments
Collaborator

Report

The app needs to work on small screens like the Light Phone 3 (1080x1240) and on normal-sized phones like a Pixel 7. The full-screen Now Playing view is the worst case.

Findings

  • The reference device this app was first run on reports a 424x439 CSS px viewport in Chrome 113. That is the shape to design against, and it is two years behind every other tier's engine: no relaxed CSS nesting (Chrome 120 — a nested rule starting with a bare element selector is silently dropped), no Popover API (114, which Web Awesome's popups set popover="manual" for), no light-dark(), no relative colour syntax.
  • "It renders at that size in Chromium" is therefore not evidence about the phone, and resizing a spec cannot recover the missing signal. make android-inspect and make android-eval ask the real page over raw CDP.
  • The phone layout kicks in below 600px: sidebar column dropped, <bottom-nav>, and the shell measures exactly 320px in a 320px viewport. Vertical space is the untested axis — 439 CSS px tall with a 4em header and a 4em bottom bar leaves very little.

Direction

Treat short viewports as a first-class case: audit every view at ~424x439, and give now-playing-view a layout that survives it (art shrinks or is dropped below a height threshold; controls never scroll off). Related Android issues — removing the top bar, slimming the transport, the progress line on the nav border — all buy back vertical space and should be sequenced together.

**Report** The app needs to work on small screens like the Light Phone 3 (1080x1240) and on normal-sized phones like a Pixel 7. The full-screen Now Playing view is the worst case. **Findings** - The reference device this app was first run on reports a **424x439 CSS px** viewport in **Chrome 113**. That is the shape to design against, and it is two years behind every other tier's engine: no relaxed CSS nesting (Chrome 120 — a nested rule starting with a bare element selector is silently dropped), no Popover API (114, which Web Awesome's popups set `popover="manual"` for), no `light-dark()`, no relative colour syntax. - "It renders at that size in Chromium" is therefore **not evidence** about the phone, and resizing a spec cannot recover the missing signal. `make android-inspect` and `make android-eval` ask the real page over raw CDP. - The phone layout kicks in below 600px: sidebar column dropped, `<bottom-nav>`, and the shell measures exactly 320px in a 320px viewport. Vertical space is the untested axis — 439 CSS px tall with a 4em header and a 4em bottom bar leaves very little. **Direction** Treat short viewports as a first-class case: audit every view at ~424x439, and give `now-playing-view` a layout that survives it (art shrinks or is dropped below a height threshold; controls never scroll off). Related Android issues — removing the top bar, slimming the transport, the progress line on the nav border — all buy back vertical space and should be sequenced together.
logan self-assigned this 2026-08-21 19:09:15 +00:00
logan added the
Status
In Progress
label 2026-08-21 19:09:16 +00:00
Author
Collaborator

Auditing every view at 424x439 on the attached Light Phone III (TLP301,
Android 14 / SDK 34, WebView Chrome 113), now that every Phase 3 item
that buys vertical space has landed — #62, #57, #55, #59, #61, #56, #58.
#73 says to verify this umbrella after those, and that condition is
satisfied for the first time.

Shape I am working to, so it is on the record before the code is:

  • Findings get filed as individual issues, with device measurements
    and reproductions. #51 becomes the index that links them, rather than
    a list of observations in a comment.
  • Violations of plan 018's promise — no action unreachable at any
    supported size — are bugs, not findings
    , and get fixed in this PR
    where the fix is cheap and unambiguous.
  • #51 closes when the promise is verified at 424x439, not when every
    child issue is closed, or it stays open behind its own children forever.

The one named design question is now-playing-view: #172 closed handing
it here explicitly — whether the art gets a floor (a min-height, with
the block scrolling) or whether the screen reflows below some height.

The other named device, a Pixel 7 (~412x869), is not attached. Anything
about tall phones will be reasoned and said to be unverified rather than
claimed.

Auditing every view at 424x439 on the attached Light Phone III (TLP301, Android 14 / SDK 34, WebView **Chrome 113**), now that every Phase 3 item that buys vertical space has landed — #62, #57, #55, #59, #61, #56, #58. #73 says to verify this umbrella *after* those, and that condition is satisfied for the first time. Shape I am working to, so it is on the record before the code is: - **Findings get filed as individual issues**, with device measurements and reproductions. #51 becomes the index that links them, rather than a list of observations in a comment. - **Violations of plan 018's promise — no action unreachable at any supported size — are bugs, not findings**, and get fixed in this PR where the fix is cheap and unambiguous. - **#51 closes when the promise is verified at 424x439**, not when every child issue is closed, or it stays open behind its own children forever. The one named design question is `now-playing-view`: #172 closed handing it here explicitly — whether the art gets a floor (a `min-height`, with the block scrolling) or whether the screen reflows below some height. The other named device, a Pixel 7 (~412x869), is not attached. Anything about tall phones will be reasoned and said to be unverified rather than claimed.
Author
Collaborator

Audited on the device, and the umbrella has a null result plus two
children.
PR #188.

Reference device: TLP301 (Light Phone III), Android 14 / SDK 34, arm64,
WebView Chrome 113, 424x439 CSS px, with a real 1,577-track
library scanned from the phone.

What the audit did not find

All ten primary views plus the queue, walked with a probe that descends
open shadow roots:

  • documentElement.scrollWidth is 424 against a 424 viewport on every
    view
    . No horizontal overflow anywhere.
  • Nothing is stranded — no control sits outside the viewport without
    a scrollable ancestor that can bring it back.
  • Nothing is occluded in place — a hit test at each control's centre
    reaches the control.

So plan 018's promise holds at this size and the width work of #57,
#62, #55 and #59 is intact. This issue's own Findings expected that
("the phone layout kicks in below 600px … vertical space is the
untested axis") and the measurement agrees: what was left was vertical,
and it was now-playing-view.

What it did find, filed rather than folded in

  • #186 — every control that is not the transport is under the 44px
    floor. #56 sized the playback controls for a thumb and nothing else
    was resized: the sort direction arrow is 28x21, the sort select
    23px tall, and search-trigger — which #57 created for a phone — is
    40x40. Twelve controls measured, on every view.
  • #187 — the seek bar's drag target is 6px tall, on the app's
    primary seeking affordance.

Neither is a reachability failure, which is why they are children
rather than blockers of this one.

Now Playing: the art was a crop, not a small square

Before anything else, a defect underneath the question #172 handed
over. aspect-ratio is specified not to re-derive the width when
max-height clamps the height — unlike an intrinsic ratio, which is
preserved under both bounds. So width: min(100%, 60vh) kept its width
while the height was clipped and object-fit: cover cropped a square
cover into the band:

before after
album art 264x53 (a 5:1 strip) 143x143
transport 143 143
scrolls? no no

That is what #172's "39px of art" actually looked like — the missing
half of that measurement is that the 39px were 263 wide.

And it was never only this phone. The leftover exceeds the width
only above ~843px of viewport, so every height from ~500 to ~843 — most
phones, any short window — drew a crop too. The new spec fails on the
old build at 424x439 (263x39), 390x700 (358x315) and 900x500
(300x36), and passes at 412x869, which is the boundary landing
exactly where the arithmetic says it should.

The fix was chosen by measuring Chrome 113 itself at five column
heights rather than by reasoning about the spec.

The design question, answered: reflow

The stacked budget is fixed — 48px header, 143px transport since #64,
78px names, 68px padding and gaps — so the art gets height - 386.
#172 named two options and a floor on the art scrolls the transport off
the bottom, which contradicts this issue's own Direction ("controls
never scroll off") and plan 018's matrix. So below 500px the art and
the names share a row
, where the art is bounded by the row's height
rather than the column's leftover.

500 is where the two layouts cross rather than a round number: in a
row the art is height - 296 and the names hold 176px at exactly 500;
stacked, the art is height - 386 and passes 176px at 562. Keyed on
height alone, not on the phone's width, because it answers vertical
room — a 900x450 window has the same problem and the same fix.

It degrades rather than breaking: with a job band up (103px of the
screen) the art is a 41px square and the transport still does not move.

Before — 264x53, cropped:

before

After — 143x143:

after

With a job band, the worst realistic case — 41px square, transport intact:

band

Not verified

A Pixel 7. This issue names it and none was attached. What is
asserted at 412x869 is the layout that viewport should get — the
column, with a square art — not that device. The tall-phone case is
also the one where the placeholder needed a second bound
(max-height: calc(100vw - 2rem)), so it is the case most worth
re-checking if one is ever plugged in.

**Audited on the device, and the umbrella has a null result plus two children.** PR #188. Reference device: TLP301 (Light Phone III), Android 14 / SDK 34, arm64, WebView **Chrome 113**, **424x439 CSS px**, with a real 1,577-track library scanned from the phone. ## What the audit did not find All ten primary views plus the queue, walked with a probe that descends open shadow roots: - `documentElement.scrollWidth` is **424 against a 424 viewport on every view**. No horizontal overflow anywhere. - **Nothing is stranded** — no control sits outside the viewport without a scrollable ancestor that can bring it back. - **Nothing is occluded in place** — a hit test at each control's centre reaches the control. So plan 018's promise holds at this size and the width work of #57, #62, #55 and #59 is intact. This issue's own Findings expected that ("the phone layout kicks in below 600px … vertical space is the untested axis") and the measurement agrees: what was left was vertical, and it was `now-playing-view`. ## What it did find, filed rather than folded in - **#186** — every control that is not the transport is under the 44px floor. #56 sized the playback controls for a thumb and nothing else was resized: the sort direction arrow is **28x21**, the sort select 23px tall, and `search-trigger` — which #57 created *for a phone* — is 40x40. Twelve controls measured, on every view. - **#187** — the seek bar's drag target is **6px** tall, on the app's primary seeking affordance. Neither is a reachability failure, which is why they are children rather than blockers of this one. ## Now Playing: the art was a crop, not a small square Before anything else, a defect underneath the question #172 handed over. `aspect-ratio` is specified **not** to re-derive the width when `max-height` clamps the height — unlike an intrinsic ratio, which is preserved under both bounds. So `width: min(100%, 60vh)` kept its width while the height was clipped and `object-fit: cover` cropped a square cover into the band: | | before | after | |---|---|---| | album art | **264x53** (a 5:1 strip) | **143x143** | | transport | 143 | 143 | | scrolls? | no | no | That is what #172's "39px of art" actually looked like — the missing half of that measurement is that the 39px were 263 wide. **And it was never only this phone.** The leftover exceeds the width only above ~843px of viewport, so every height from ~500 to ~843 — most phones, any short window — drew a crop too. The new spec fails on the old build at 424x439 (`263x39`), 390x700 (`358x315`) and 900x500 (`300x36`), and passes at 412x869, which is the boundary landing exactly where the arithmetic says it should. The fix was chosen by measuring **Chrome 113 itself** at five column heights rather than by reasoning about the spec. ## The design question, answered: reflow The stacked budget is fixed — 48px header, 143px transport since #64, 78px names, 68px padding and gaps — so the art gets `height - 386`. #172 named two options and a floor on the art scrolls the transport off the bottom, which contradicts this issue's own Direction ("controls never scroll off") and plan 018's matrix. So **below 500px the art and the names share a row**, where the art is bounded by the row's height rather than the column's leftover. **500 is where the two layouts cross rather than a round number**: in a row the art is `height - 296` and the names hold 176px at exactly 500; stacked, the art is `height - 386` and passes 176px at 562. Keyed on height alone, not on the phone's width, because it answers vertical room — a 900x450 window has the same problem and the same fix. It degrades rather than breaking: with a job band up (103px of the screen) the art is a 41px square and the transport still does not move. **Before** — 264x53, cropped: ![before](https://git.ljones.me/attachments/961cd714-4ba2-4a2a-9882-7e61f41002c2) **After** — 143x143: ![after](https://git.ljones.me/attachments/c5e53049-607b-4b9e-beae-5354fbf30b2f) **With a job band, the worst realistic case** — 41px square, transport intact: ![band](https://git.ljones.me/attachments/ec05eaaf-637f-4e6f-b755-82c7b6346a43) ## Not verified **A Pixel 7.** This issue names it and none was attached. What is asserted at 412x869 is the layout that viewport *should* get — the column, with a square art — not that device. The tall-phone case is also the one where the placeholder needed a second bound (`max-height: calc(100vw - 2rem)`), so it is the case most worth re-checking if one is ever plugged in.
Author
Collaborator

The placeholder path, and a correction about what had been verified.

The first device verification above was run against a build that was
missing two of the final declarations — I had rebuilt the APK after the
art rule and then made three more edits without redeploying. The 143px
figure was measured with a real cover, which that build already handled
correctly, so it stands; but the no-cover path had not actually
been checked on the device.

It has been now, on the shipped code. A track with no embedded art
(105 of the 1,577 in the test library):

before after
placeholder 263x143 (a wide panel) 143x143
computed min-width auto 0px
computed max-height 100% 392.31px

Both new declarations are doing the job they were added for. This is the
part that would have shipped broken twice over: a non-replaced box has
no intrinsic size, so the rule that fixes the image collapses the
placeholder to its icon (13x58), and once it is height-driven a flex
item's automatic minimum is its content, so the icon's width becomes a
floor the moment the row is shorter than the icon — which is precisely
what a job band does to this screen.

placeholder

The empty state (nothing loaded at all) is unchanged by this — it is a
separate render branch that the .stack wrapper does not touch — and is
covered by now-playing-view.test.ts in the component tier.

**The placeholder path, and a correction about what had been verified.** The first device verification above was run against a build that was missing two of the final declarations — I had rebuilt the APK after the art rule and then made three more edits without redeploying. The 143px figure was measured with a real cover, which that build already handled correctly, so it stands; but the **no-cover** path had not actually been checked on the device. It has been now, on the shipped code. A track with no embedded art (105 of the 1,577 in the test library): | | before | after | |---|---|---| | placeholder | 263x143 (a wide panel) | **143x143** | | computed `min-width` | `auto` | `0px` | | computed `max-height` | `100%` | `392.31px` | Both new declarations are doing the job they were added for. This is the part that would have shipped broken twice over: a non-replaced box has no intrinsic size, so the rule that fixes the image collapses the placeholder to its icon (**13x58**), and once it is height-driven a flex item's automatic minimum is its *content*, so the icon's width becomes a floor the moment the row is shorter than the icon — which is precisely what a job band does to this screen. ![placeholder](https://git.ljones.me/attachments/626a85a0-b85a-4ed9-a14e-18472e3f9897) The empty state (nothing loaded at all) is unchanged by this — it is a separate render branch that the `.stack` wrapper does not touch — and is covered by `now-playing-view.test.ts` in the component tier.
logan closed this issue 2026-08-21 20:19:46 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-21 20:19:59 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#51