docs(player): record the crop, the reflow, and the audit's null result
The audit #51 asks for, at 424x439 on the reference device with a real 1,577-track library: all ten primary views plus the queue. What it did *not* find is worth recording, because it is the promise plan 018 makes -- the shell does not overflow on any view, nothing is stranded outside a scrollable ancestor, and a hit test at each control's centre reaches the control. The width work of #57, #62, #55 and #59 holds; what was left was vertical. What it found is filed rather than fixed here: #186, every control that is not the transport is under the 44px floor, and #187, the seek bar's drag target is 6px. Also the two device traps that cost time despite being written down -- a fresh install downloads the real catalog and the job band then eats 103px of a 439px screen, and it *restarts* after being stopped; and the first-run wizard does not re-check for a library it did not create, so adding one over the bridge leaves it up with a correctly disabled button. Closes #51
This commit is contained in:
@@ -4785,3 +4785,109 @@ broken build. The specs assert the *mechanism* — that the surface is a
|
||||
native `<dialog>` at phone width — which is the same move
|
||||
`queue-as-a-screen.spec.ts` makes about containment and for the same
|
||||
reason.
|
||||
|
||||
## Now Playing was not drawing a small square, it was drawing a crop (measured 2026-08-21, TLP301 / Chrome 113 / 424x439)
|
||||
|
||||
#172 handed #51 a design question — whether the album art gets a floor
|
||||
with the block scrolling, or whether the screen reflows below some
|
||||
height. Measuring it first turned up a defect underneath the question,
|
||||
and the defect is bigger than the phone.
|
||||
|
||||
**The art was never square.** `aspect-ratio` is specified not to
|
||||
re-derive the width when `max-height` clamps the height — unlike an
|
||||
intrinsic ratio, which CSS2.1 10.4 preserves under both bounds. So
|
||||
`width: min(100%, 60vh)` made the width definite, the ratio derived a
|
||||
height from it, `max-height: 100%` clipped that height, and the width
|
||||
stayed where it was. `object-fit: cover` then cropped a square cover
|
||||
into the band. On the device: **264x53**, a 5:1 strip. #172's own table
|
||||
called it "39px of art" and the missing half is that those 39px were
|
||||
263 wide.
|
||||
|
||||
**And it is not only the phone.** The leftover exceeds the width only
|
||||
above ~843px of viewport, so every height from ~500 to ~843 drew a
|
||||
crop too — most phones, and any short window. The e2e spec written for
|
||||
this fails on the old build at 424x439 (263x39), 390x700 (358x315) and
|
||||
900x500 (300x36), and *passes* at 412x869, which is the boundary
|
||||
falling exactly where the arithmetic says it should.
|
||||
|
||||
**Both maxes with auto sizes is the whole fix**, and it was chosen by
|
||||
asking Chrome 113 rather than by reasoning: a probe shadow root at
|
||||
column heights of 288, 300, 451, 600 and 800 measured four candidate
|
||||
rules. `max-width/max-height: 100%` with `width/height: auto` is square
|
||||
at all five; the shipped rule cropped at four; `aspect-ratio` on the
|
||||
box cropped at the tallest. A corollary that makes it free: `auto` will
|
||||
not upscale past the natural size, and the largest tier `saveCoverArt`
|
||||
keeps is 400px, so nothing is lost by never exceeding it.
|
||||
|
||||
**The placeholder cannot use that rule and needed its own**, which is
|
||||
the part that would have shipped broken. It is not a replaced element,
|
||||
so with no intrinsic size auto/auto collapses it to its icon —
|
||||
measured at **13x58**, neither square nor the art's size. Three things
|
||||
about the rule it did get:
|
||||
|
||||
- It is driven from the **height**, which is the axis that binds
|
||||
everywhere this view is reached from.
|
||||
- A flex item's automatic minimum is its content, so without
|
||||
`min-width: 0` the icon's own width becomes a floor and the box goes
|
||||
wider than it is tall the moment the row is shorter than the icon —
|
||||
which is exactly the state a job band puts this screen in.
|
||||
- **A non-replaced box cannot express "the largest square that fits" at
|
||||
all**, because whichever max clamps does not re-derive the other. The
|
||||
height-driven rule alone went **380x484** at 412x869 — a tall phone,
|
||||
#51's other named device — and `max-height: calc(100vw - 2rem)` is
|
||||
what closes it. That is sound here for the reason `60vh` was not: this
|
||||
is a phone-width detail view, so its content box really is the
|
||||
viewport less the host's own gutters, and it is a *max*, so if that
|
||||
ever stopped being true the failure is a square bounded early rather
|
||||
than a crop. `rem` and not `em` — the box sets `font-size: 3rem` for
|
||||
the icon, so `2em` there is 96px.
|
||||
|
||||
**Then the design question, and the reflow is the answer.** The
|
||||
stacked layout's budget is fixed — 48px of header, 143px of transport
|
||||
since #64, 78px of names, 68px of padding and gaps — so the art gets
|
||||
`height - 386`, which is 53px at 439. A floor on the art scrolls the
|
||||
transport off the bottom, and "controls never scroll off" is #51's own
|
||||
Direction and plan 018's promise. So below 500px the art and the names
|
||||
share a row, where the art is bounded by the row's height rather than
|
||||
by the column's leftover: **53px to 143px on the device**, measured on
|
||||
the shipped build, with nothing scrolling and the transport untouched.
|
||||
|
||||
**500 is where the two layouts cross, not a round number.** In a row
|
||||
the art is `height - 296` and the names get what is left of 392px, so
|
||||
the names hold 176px at exactly 500 and less above it; stacked, the art
|
||||
is `height - 386`, which passes 176px at 562. It is keyed on height
|
||||
alone rather than on the phone's width because it is an answer to
|
||||
vertical room — a 900x450 window has the same problem and the same fix.
|
||||
|
||||
Two things the audit found that are *not* this, and are filed:
|
||||
**#186**, every control that is not the transport is under the 44px
|
||||
floor (the sort direction arrow is 28x21, and `search-trigger` — which
|
||||
exists only on a phone — is 40x40), and **#187**, the seek bar's drag
|
||||
target is 6px tall.
|
||||
|
||||
**What the audit did not find is a reachability failure**, which is
|
||||
worth recording because it is the promise plan 018 makes. At 424x439,
|
||||
on all ten primary views plus the queue, `documentElement.scrollWidth`
|
||||
is 424 against a 424 viewport, no control sits outside a scrollable
|
||||
ancestor, and a hit test at each control's centre reaches the control.
|
||||
The width work of #57, #62, #55 and #59 holds; what was left was
|
||||
vertical, and it was this screen.
|
||||
|
||||
## Two traps that cost time on the device, both already written down (2026-08-21)
|
||||
|
||||
Recorded because both are in `android-tier.md` and I met them anyway.
|
||||
|
||||
**A fresh install downloads the real catalog**, so `job-band` is 103px
|
||||
of a 439px screen and every vertical measurement is wrong. Worse, it
|
||||
**restarts**: `explore.Service.StopIndexBuild` returns cleanly and the
|
||||
job is `running` again within seconds, so it has to be stopped again
|
||||
immediately before a measurement rather than once at the start.
|
||||
`YJ_CORE_INDEX_URL` is stubbed in `dev-headless.sh` and in CI and is
|
||||
real on a device.
|
||||
|
||||
**The first-run wizard does not re-check for a library it did not
|
||||
create.** Adding one through `library.Library.AddLibrary` over the
|
||||
bridge leaves the wizard up with its "Get Started" button correctly
|
||||
disabled — it gates on a directory chosen *in the wizard*, and the
|
||||
existing-library check runs once, on mount. A reload clears it. Nothing
|
||||
is broken; it cost twenty minutes of believing a tap had been swallowed.
|
||||
|
||||
@@ -2120,6 +2120,54 @@ toggled from `index.ts` would be a second expression of the same fact.
|
||||
The view therefore carries its own queue button, because that button
|
||||
lives in the bar it hides.
|
||||
|
||||
**And below 500px of height its art and its names share a row** (#51).
|
||||
The stacked arrangement's budget is fixed — 48px of header, 143px of
|
||||
transport since #64, 78px of names, 68px of padding and gaps — so the
|
||||
art gets `height - 386`, which at the reference device's 424x439 is
|
||||
**53px**: the one thing a Now Playing screen exists to show, smallest
|
||||
on it. #172 named the two ways out and this is the second, because the
|
||||
first — a floor on the art with the block scrolling — scrolls the
|
||||
transport off the bottom, and *controls never scroll off* is #51's own
|
||||
Direction and plan 018's promise. Sideways the art is bounded by the
|
||||
row's height instead of by the column's leftover: **53px to 143px**,
|
||||
measured on the device, nothing scrolling, the transport untouched.
|
||||
|
||||
Three things about it are load-bearing.
|
||||
|
||||
**500 is where the two layouts cross rather than a round number.** In a
|
||||
row the art is `height - 296` and the names get what is left of 392px,
|
||||
so the names hold 176px at exactly 500 and less above it; stacked, the
|
||||
art is `height - 386`, which passes 176px at 562. Below 500 the row is
|
||||
the bigger art *and* the readable one — above it the column is, which
|
||||
is why a tall phone keeps the arrangement it has. It is keyed on height
|
||||
alone and not on the phone's width, because it answers vertical room: a
|
||||
900x450 window has the same problem and the same fix.
|
||||
|
||||
**The art was not a small square, it was a crop, and that was never
|
||||
only the phone.** `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 a definite `width: min(100%,
|
||||
60vh)` kept its width while the height was clipped, and `object-fit:
|
||||
cover` cropped a square cover into the band: **264x53** on the device.
|
||||
The leftover exceeds the width only above ~843px of viewport, so every
|
||||
height from ~500 to ~843 drew one too. `max-width`/`max-height: 100%`
|
||||
with `width`/`height: auto` is the fix and is the replaced-element
|
||||
path; it also never upscales past the natural size, and the largest
|
||||
tier `saveCoverArt` keeps is 400px, so nothing is lost.
|
||||
|
||||
**The placeholder needs its own rule, and a non-replaced box cannot
|
||||
express this one.** With no intrinsic size, auto/auto collapses it to
|
||||
its icon (13x58, measured). It is driven from the height instead, with
|
||||
`min-width: 0` because a flex item's automatic minimum is its content —
|
||||
without it the icon's width becomes a floor the moment the row is
|
||||
shorter than the icon, which is exactly what a job band does to this
|
||||
screen. And since whichever max clamps does not re-derive the other, a
|
||||
height-driven box goes **380x484** on a tall phone; `max-height:
|
||||
calc(100vw - 2rem)` closes it, which is sound here for the reason
|
||||
`60vh` was not — this is a phone-width detail view, so its content box
|
||||
really is the viewport less the host's gutters, and it is a *max*, so
|
||||
the failure mode is a square bounded early rather than a crop.
|
||||
|
||||
**The playing row is a shape, not a hue.** `track-list` and
|
||||
`queue-panel` draw a `::before` triangle in each row's own left
|
||||
padding, plus `aria-current` — before, both rows were a background tint
|
||||
|
||||
Reference in New Issue
Block a user