Put the phone's Now Playing button above the artwork #158

Merged
logan merged 1 commits from fix/150-expand-button-under-the-art into main 2026-08-20 05:47:47 +00:00
Collaborator

Filed as an e2e flake (#150). It is not one — it depends on which
track is playing, and it is a user-facing bug on the platform it
affects.

The mechanism

.expand is the phone's only route into <now-playing-view>. It is
position: absolute; inset: 0 inside .cover-art-wrapper, and
.cover-art is a later sibling. Both have z-index: auto, so they
tie on paint order and the later one wins. An <img> costs nothing
there; with no artwork the placeholder wa-icon renders and takes every
click aimed at the button underneath it.

Measured at 390px, elementFromPoint at the button's centre:

playing track hit test
has artwork button.expand
no artwork wa-icon
no artwork, with z-index: 1 button.expand

So on a phone the only way into the full-screen player stopped working
whenever the current song had no cover. Nothing to do with the fixture:
any library has untagged files.

z-index over pointer-events: none on the art, which would take the
cover preview's mouseenter with it; and over reordering the DOM, which
leaves the same tie to be won by the same accident in the other
direction.

Why it read as a flake, which is the more useful lesson

It failed on both engines three times across two branches that could
not have caused it
, and passed on re-run each time. The spec starts
the first row of the track list, so which track it plays is the order
the scan inserted rows in — the same root cause as #156, one spec
over, found the same day.

Two earlier hypotheses on this issue were wrong and both were plausible:
a custom element's upgrade replacing its own contents, and the preview
popup opening under the pointer. Neither survived one elementFromPoint
probe, which took a minute and would have saved two of the three cycles.

The new spec

It picks a track for having no artwork and asserts the placeholder
is rendered rather than assuming it, so it cannot quietly revert to
measuring the easy case. Two documented traps had to be respected:

  • it must be the 90-second track, or it finishes before the
    assertions run
  • library.Track.CoverArt is empty for all 31 fixture rows, so
    "the first track with no cover art" selects nothing in particular —
    it picked a 2-second one, which is how the first draft failed on the
    wrong assertion

Verification

Mutation: without the z-index the new spec fails on the click in
30s, while the pre-existing test beside it passes — which is exactly how
this survived in the suite.

  • make e2e — 179 passed (was 178)
  • make ui-test — 949
  • make css-check, npx tsc --noEmit in e2e/

Closes #150

Filed as an e2e flake (#150). **It is not one** — it depends on which track is playing, and it is a user-facing bug on the platform it affects. ## The mechanism `.expand` is the phone's only route into `<now-playing-view>`. It is `position: absolute; inset: 0` inside `.cover-art-wrapper`, and `.cover-art` is a **later sibling**. Both have `z-index: auto`, so they tie on paint order and the later one wins. An `<img>` costs nothing there; with no artwork the placeholder `wa-icon` renders and takes every click aimed at the button underneath it. Measured at 390px, `elementFromPoint` at the button's centre: | playing track | hit test | |---|---| | has artwork | `button.expand` | | **no artwork** | **`wa-icon`** | | no artwork, with `z-index: 1` | `button.expand` | So on a phone the only way into the full-screen player stopped working whenever the current song had no cover. Nothing to do with the fixture: any library has untagged files. `z-index` over `pointer-events: none` on the art, which would take the cover preview's `mouseenter` with it; and over reordering the DOM, which leaves the same tie to be won by the same accident in the other direction. ## Why it read as a flake, which is the more useful lesson It failed on both engines **three times across two branches that could not have caused it**, and passed on re-run each time. The spec starts the *first* row of the track list, so which track it plays is the order the **scan** inserted rows in — the same root cause as #156, one spec over, found the same day. Two earlier hypotheses on this issue were wrong and both were plausible: a custom element's upgrade replacing its own contents, and the preview popup opening under the pointer. Neither survived one `elementFromPoint` probe, which took a minute and would have saved two of the three cycles. ## The new spec It picks a track **for** having no artwork and asserts the placeholder is rendered rather than assuming it, so it cannot quietly revert to measuring the easy case. Two documented traps had to be respected: - it must be the **90-second** track, or it finishes before the assertions run - `library.Track.CoverArt` is **empty for all 31 fixture rows**, so "the first track with no cover art" selects nothing in particular — it picked a 2-second one, which is how the first draft failed on the wrong assertion ## Verification Mutation: without the `z-index` the new spec fails **on the click** in 30s, while the pre-existing test beside it passes — which is exactly how this survived in the suite. - `make e2e` — 179 passed (was 178) - `make ui-test` — 949 - `make css-check`, `npx tsc --noEmit` in `e2e/` Closes #150
logan added 1 commit 2026-08-20 05:36:04 +00:00
fix(player): put the phone's Now Playing button above the artwork
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m27s
CI / e2e (pull_request) Successful in 8m1s
ffc9490a32
`.expand` is the phone's only route into the full-screen now-playing
view. It is absolutely positioned with `z-index: auto` over
`.cover-art`, which is a *later* sibling with the same z-index, so the
two tie on paint order and the later one wins. An `<img>` costs nothing
there; a track with no artwork renders a placeholder `wa-icon`, which
takes every click aimed at the button underneath it.

So the control did not work whenever the current song had no cover, on
the one platform that has no other way in. Nothing to do with the
fixture: any library has untagged files.

Measured at 390px with elementFromPoint at the button's centre — the
icon with a placeholder, the button with an image, and the button
either way with the z-index. Chosen over `pointer-events: none` on the
art, which would take the cover preview's mouseenter with it, and over
reordering the DOM, which leaves the same tie to be won by the same
accident in the other direction.

This was filed as an e2e flake, and the diagnosis was wrong: it failed
on both engines three times across two branches that could not have
caused it, and passed on re-run each time, because the spec starts the
*first* row of the track list and which track that is depends on the
order the scan inserted rows — the same root cause as #156. The new
spec picks a track *for* having no artwork, and asserts the placeholder
is rendered rather than assuming it, so it cannot quietly go back to
measuring the easy case.

Two things it has to get right, both already documented traps: the
track must be the 90-second one, since a 2-second one finishes before
the assertions run; and `library.Track.CoverArt` is empty for all 31
fixture rows, so "the first track with no cover art" selects nothing in
particular and picked a short one.

Verified by mutation: without the z-index the new spec fails on the
click in 30s, and the pre-existing one beside it passes, which is
exactly how this survived.

Closes #150
logan force-pushed fix/150-expand-button-under-the-art from 7699fcd9dc to ffc9490a32 2026-08-20 05:36:04 +00:00 Compare
logan merged commit 5490b2423e into main 2026-08-20 05:47:47 +00:00
Sign in to join this conversation.