Hover-only affordances that are the *only* route to their action are unreachable on touch #137

Closed
opened 2026-08-19 17:29:51 +00:00 by yonlu · 2 comments
Owner

Finding

Turned up by #68's sweep, and deliberately left out of it because the
fix is the opposite one.

#68 hid the home card's hover play button on touch, which is right: a
phone user taps the card and plays from the detail view, so the control
is redundant there. Three other affordances are revealed the same way
and are not redundant — hiding them removes the action entirely, and
leaving them costs the same long-press flash #68 was filed for.

  • queue-panel.ts:473-486.remove-button is visibility: hidden
    until .track-item:hover. It is the only per-row remove in the queue.
  • track-details.ts:496,524.cover-art-overlay and
    .cover-art-remove, revealed on .cover-art-edit:hover. The only
    route to changing or removing a track's cover art.
  • shortcut-capture.ts:76.reset-btn, revealed on :host(:hover).
    The only way to put a rebound shortcut back.

All three are visibility: hidden / opacity: 0 rather than
display: none, so on touch they are invisible controls that still
occupy their hit area — the trap #68's commit message names.

Direction

The rule is one line and the inverse of #68's: where the affordance is
the only route to its action, it is always visible on a device
without hover, rather than hidden.

@media not all and (hover: hover) { .remove-button { visibility: visible } }

Worth doing as one pass, since the three are the same mistake, and worth
extracting the rule into styles/ once there are four call sites of it
(#68's is the fifth, in the other direction).

Sequencing

The queue one overlaps #55 (the queue becomes a real screen on Android)
and #60 (context menus become bottom sheets) — if the queue row gains a
bottom-sheet menu with Remove in it, the queue case becomes the
redundant kind and takes #68's treatment instead. So do this after
#60, or accept that the queue half may be redone.

**Finding** Turned up by #68's sweep, and deliberately left out of it because the fix is the *opposite* one. #68 hid the home card's hover play button on touch, which is right: a phone user taps the card and plays from the detail view, so the control is redundant there. Three other affordances are revealed the same way and are **not** redundant — hiding them removes the action entirely, and leaving them costs the same long-press flash #68 was filed for. - `queue-panel.ts:473-486` — `.remove-button` is `visibility: hidden` until `.track-item:hover`. It is the only per-row remove in the queue. - `track-details.ts:496,524` — `.cover-art-overlay` and `.cover-art-remove`, revealed on `.cover-art-edit:hover`. The only route to changing or removing a track's cover art. - `shortcut-capture.ts:76` — `.reset-btn`, revealed on `:host(:hover)`. The only way to put a rebound shortcut back. All three are `visibility: hidden` / `opacity: 0` rather than `display: none`, so on touch they are invisible controls that still occupy their hit area — the trap #68's commit message names. **Direction** The rule is one line and the inverse of #68's: where the affordance is the only route to its action, it is **always visible** on a device without hover, rather than hidden. ```css @media not all and (hover: hover) { .remove-button { visibility: visible } } ``` Worth doing as one pass, since the three are the same mistake, and worth extracting the rule into `styles/` once there are four call sites of it (#68's is the fifth, in the other direction). **Sequencing** The queue one overlaps #55 (the queue becomes a real screen on Android) and #60 (context menus become bottom sheets) — if the queue row gains a bottom-sheet menu with Remove in it, the queue case becomes the *redundant* kind and takes #68's treatment instead. So do this after #60, or accept that the queue half may be redone.
logan self-assigned this 2026-08-21 10:33:23 +00:00
logan added the
Status
In Progress
label 2026-08-21 10:33:23 +00:00
Collaborator

Picking this up on fix/137-touch-only-affordances.

Approach, following the issue's own Direction and its Sequencing note.

#60 has landed, and the queue row's context menu — now a bottom sheet on
a phone — carries "Remove from Queue". So the queue case is the
redundant kind the Sequencing paragraph anticipated, and takes #68's
treatment (display: none outside (hover: hover)) rather than being
made always-visible: an always-on X in every queue row would spend
horizontal space on a 424px screen for an action that is one long-press
away.

The other two are the only route to their action and become always
visible where the device has no hover:

  • track-details.cover-art-overlay / .cover-art-remove
  • shortcut-capture.reset-btn

Not extracting the rule into styles/ yet: that leaves two call sites of
the always-visible form (three selectors), under the four the issue names
as the threshold.

Picking this up on `fix/137-touch-only-affordances`. **Approach**, following the issue's own Direction and its Sequencing note. #60 has landed, and the queue row's context menu — now a bottom sheet on a phone — carries "Remove from Queue". So the queue case is the *redundant* kind the Sequencing paragraph anticipated, and takes #68's treatment (`display: none` outside `(hover: hover)`) rather than being made always-visible: an always-on X in every queue row would spend horizontal space on a 424px screen for an action that is one long-press away. The other two are the only route to their action and become always visible where the device has no hover: - `track-details` — `.cover-art-overlay` / `.cover-art-remove` - `shortcut-capture` — `.reset-btn` Not extracting the rule into `styles/` yet: that leaves two call sites of the always-visible form (three selectors), under the four the issue names as the threshold.
Collaborator

PR: #181 — CI green on
5fbe1d9 (check and e2e, the latter on both Chromium and WebKit).

Three CSS rules, in the two directions this issue argues for.
track-details's cover-art overlay and remove, and shortcut-capture's
reset, are always visible under @media not all and (hover: hover).
The queue row's remove took #68's treatment instead, per the Sequencing
paragraph: #60 has landed, so the row's context menu is a bottom sheet
carrying "Remove from Queue" and the button is redundant on touch rather
than missing.

Verified at the component tier (the parsed stylesheet, as #68's test
does, since no tier here renders as a touch device) and by measuring the
computed styles against the running app in a hasTouch context at
424x439 and a hover context at 1100x760 — the table is in the PR body.

Not extracted into styles/: that leaves two call sites of the
always-visible form, under the four named here. Left Status/In Progress on; not merged.

PR: https://git.ljones.me/yonlu/yellowjacket/pulls/181 — CI green on `5fbe1d9` (`check` and `e2e`, the latter on both Chromium and WebKit). Three CSS rules, in the two directions this issue argues for. `track-details`'s cover-art overlay and remove, and `shortcut-capture`'s reset, are always visible under `@media not all and (hover: hover)`. The queue row's remove took #68's treatment instead, per the Sequencing paragraph: #60 has landed, so the row's context menu is a bottom sheet carrying "Remove from Queue" and the button is redundant on touch rather than missing. Verified at the component tier (the parsed stylesheet, as #68's test does, since no tier here renders as a touch device) and by measuring the computed styles against the running app in a `hasTouch` context at 424x439 and a hover context at 1100x760 — the table is in the PR body. Not extracted into `styles/`: that leaves two call sites of the always-visible form, under the four named here. Left `Status/In Progress` on; not merged.
logan closed this issue 2026-08-21 16:23:43 +00:00
gitea-actions bot removed the
Status
In Progress
label 2026-08-21 16:23:53 +00:00
Sign in to join this conversation.
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yonlu/yellowjacket#137