Android: a seek bar you can actually hit, and the phone rule that never applied #193

Merged
logan merged 1 commits from 187-seek-bar-hit-area into main 2026-08-21 22:25:52 +00:00
Collaborator
commit issue
fix(player): give the seek bar a thumb-sized hit area #187

The slider on now-playing-view measured 261x6 on the reference
device. Six pixels is the whole of the drag target on the app's primary
seeking affordance, against the 44px floor #56 set and the queue panel
keeps.

The issue's premise was wrong, and that changed the fix

The Findings say the thickening below the phone breakpoint "is real but
stops well short" at 6px. It is not real. seek-bar asked for a 12px
track inside @media (max-width: 599px) and then set --track-size: 6px
in a plain wa-slider rule written after it — and a media query
adds no specificity, so the plain rule won at every width. The source
said 12, the device said 6, and the rule had never once applied.

That is index.css's own documented rule — "the phone section is last
on purpose"
— reproduced inside a component's stylesheet, where no
tier here renders differently to say so
. The block is last now.

The target is built around the track, not by thickening it

12px is still under the floor, so the painted track and the hit area
differ deliberately. A 44px-thick progress bar would be wrong-looking
and would cost the album art the vertical space #51 spent an issue
recovering.

Two things were settled by measuring on the device rather than by
picking a number:

  • The issue's untested claim, answered — and pessimistically.
    wa-slider takes the gesture on its inner div[part=slider], which
    carries the listener and touch-action: none and is exactly the
    host's size. So padding the host grows a box that does not take the
    press; the padding has to go on the part.
  • The padding is asymmetric and the margins cancel it, so the row
    does not grow. The seek row is 19px (its clocks decide that, not the
    track) and the play button's top edge is 8px below it, while
    .art above is a non-interactive div. A symmetric 44px target reaches
    into the play button; growing the row instead cost the art 25px of
    143
    when I tried it. So the target takes the space above.

Worth knowing for anything else that styles a Web Awesome part:
adoptedStyleSheets are ordered after <style> elements, so an
injected probe stylesheet loses to the component's own — which is why
the first two device experiments silently did nothing, and why
waSliderLabel's margin-block-start: 0 beat an injected margin.

Verification

  • On the device at 424x439: hit area 261x44 where it was 261x6,
    painted track 12px, seek row still 19px, album art still 143px, 7px
    clearance
    under the play button, a press 26px above the track
    seeks, and a hit test on the play button's top edge still reaches the
    play button.
  • make ui-test (1011, +4), make e2e (236, chromium — webkit is
    CI's), make css-check, make bindings-check, make skill-check,
    tsc --noEmit in frontend/ and e2e/, make lint and make test
    (formalities — no Go changed).
  • All four new assertions proved to bite, including re-proving them
    after a refactor of the test's own parser.

The desktop bottom bar is untouched: the rule is inside the phone query
and that instance is display:none below 600px anyway.

One limitation, stated rather than hidden

The test asserts the parsed stylesheet, on
hover-affordance.test.ts's precedent: no tier here lays out a real
wa-slider at a phone width, and a number measured on a phone is not a
number CI can assert. What it holds is the shape — the phone block is
last, padding plus track clears 44, the margins cancel the padding, and
the growth is upward. All four are invisible on a desktop, and the
first is precisely what a tidy-up undoes.

Closes #187

| commit | issue | |---|---| | `fix(player): give the seek bar a thumb-sized hit area` | #187 | The slider on `now-playing-view` measured **261x6** on the reference device. Six pixels is the whole of the drag target on the app's primary seeking affordance, against the 44px floor #56 set and the queue panel keeps. ## The issue's premise was wrong, and that changed the fix The Findings say the thickening below the phone breakpoint "is real but stops well short" at 6px. It is not real. `seek-bar` asked for a 12px track inside `@media (max-width: 599px)` and then set `--track-size: 6px` in a plain `wa-slider` rule **written after it** — and a media query adds no specificity, so the plain rule won at every width. The source said 12, the device said 6, and the rule had never once applied. That is `index.css`'s own documented rule — *"the phone section is last on purpose"* — reproduced inside a component's stylesheet, where **no tier here renders differently to say so**. The block is last now. ## The target is built around the track, not by thickening it 12px is still under the floor, so the painted track and the hit area differ deliberately. A 44px-thick progress bar would be wrong-looking and would cost the album art the vertical space #51 spent an issue recovering. Two things were settled by measuring on the device rather than by picking a number: - **The issue's untested claim, answered — and pessimistically.** `wa-slider` takes the gesture on its inner `div[part=slider]`, which carries the listener and `touch-action: none` and is *exactly* the host's size. So padding the host grows a box that does not take the press; the padding has to go on the part. - **The padding is asymmetric and the margins cancel it**, so the row does not grow. The seek row is 19px (its clocks decide that, not the track) and the play button's top edge is **8px** below it, while `.art` above is a non-interactive div. A symmetric 44px target reaches into the play button; growing the row instead cost the art **25px of 143** when I tried it. So the target takes the space above. Worth knowing for anything else that styles a Web Awesome part: `adoptedStyleSheets` are ordered **after** `<style>` elements, so an injected probe stylesheet loses to the component's own — which is why the first two device experiments silently did nothing, and why `waSliderLabel`'s `margin-block-start: 0` beat an injected margin. ## Verification - On the device at 424x439: hit area **261x44** where it was 261x6, painted track 12px, seek row still 19px, album art still 143px, **7px clearance** under the play button, a press 26px above the track seeks, and a hit test on the play button's top edge still reaches the play button. - `make ui-test` (1011, +4), `make e2e` (236, chromium — webkit is CI's), `make css-check`, `make bindings-check`, `make skill-check`, `tsc --noEmit` in `frontend/` and `e2e/`, `make lint` and `make test` (formalities — no Go changed). - **All four new assertions proved to bite**, including re-proving them after a refactor of the test's own parser. The desktop bottom bar is untouched: the rule is inside the phone query and that instance is `display:none` below 600px anyway. ## One limitation, stated rather than hidden The test asserts the **parsed stylesheet**, on `hover-affordance.test.ts`'s precedent: no tier here lays out a real `wa-slider` at a phone width, and a number measured on a phone is not a number CI can assert. What it holds is the shape — the phone block is last, padding plus track clears 44, the margins cancel the padding, and the growth is upward. All four are invisible on a desktop, and the first is precisely what a tidy-up undoes. Closes #187
logan added 1 commit 2026-08-21 22:13:13 +00:00
fix(player): give the seek bar a thumb-sized hit area
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m34s
CI / e2e (pull_request) Successful in 9m28s
ec64dbded0
On now-playing-view -- the screen that exists so a phone has somewhere
to seek from -- the slider measured 261x6 on the reference device. Six
pixels is the whole of the drag target on the app's primary seeking
affordance, against the 44px floor the app set for itself in #56 and
holds to in the queue panel.

**The phone rule had never applied**, which is why the issue read as
"the thickening stops short" rather than "there is no thickening".
seek-bar's stylesheet asked for a 12px track below 599px and then set
6px in a plain `wa-slider` rule *written after it*. A media query adds
no specificity, so the plain rule won at every width: the source said
12 and the device said 6. That is index.css's documented rule -- "the
phone section is last on purpose" -- met inside a component's own
stylesheet, where nothing in any tier renders differently to say so.
The block is last now, and the 12px track it always asked for is real.

**And 12px is still under the floor**, so the target is built around
the painted track rather than by thickening it. The two are allowed to
differ and a slider is the clearest case where they should: a 44px
progress bar would be wrong-looking and would cost the album art the
vertical space #51 spent an issue recovering.

Two things about how it is built, both settled by measurement on the
device rather than by choosing a number.

**The padding goes on ::part(slider), not on the host.** That is the
issue's untested claim, and the answer is the pessimistic one: the
inner div is what carries the gesture -- it holds the listener and the
touch-action: none -- and it is exactly the host's size, so padding the
host would grow a box that does not take the press.

**The padding is asymmetric and the margins cancel it**, so the row does
not grow by the difference. The seek row is 19px -- its clocks, not the
track, decide that -- and the play button's top edge is 8px below it,
while `.art` above is a non-interactive div. A symmetric 44px target
reaches into the play button, and growing the row instead cost the art
25px of 143 when it was tried. So the target takes the space above.

Verified on the device at 424x439: hit area 261x44 where it was 261x6,
painted track 12px, seek row still 19px, album art still 143px, 7px of
clearance left under the play button, a press 26px above the track
seeks, and a hit test on the play button's top edge still reaches the
play button.

The desktop bottom bar is untouched: the rule is inside the phone query
and that instance is display:none below 600px anyway.

The test asserts the parsed stylesheet, on hover-affordance.test.ts's
precedent and with the same limitation stated -- no tier here lays out a
real wa-slider at a phone width, and a number measured on a phone is
not a number CI can assert. What it holds is the shape: that the phone
block is last, that padding plus track clears 44, that the margins
cancel the padding, and that the growth is upward. All four are
invisible on a desktop, and the first is exactly what a tidy-up undoes.

Closes #187
logan merged commit 1668b9e0d2 into main 2026-08-21 22:25:52 +00:00
Sign in to join this conversation.