--yj-text-xs..xl were hardcoded px and are consumed by essentially
every component, so raising the OS or browser font size changed nothing
anywhere (WCAG 1.4.4, a11y.19). The values are identical at the default
16px root, and all six ui-visual baselines pass unchanged.
Verified in the running app rather than assumed: at a 24px root a track
cell goes 12px to 18px and a nav item 16px to 24px.
The same check confirms a11y.20, which is left unfixed and now
documented where the coupling lives: the row stays 33px while its text
grows to 18px, because four virtualized lists duplicate their row height
as the layout's _itemSize hint and carry contain: strict, which clips
rather than reflows. Fixing that means deriving _itemSize from a
measured row — a change to the scroll maths of four lists, not to a type
scale.
The context menu was the only route to Play, Add to Queue, Play Next,
Add to Playlist, Favourite and Track Details, and it opened on
right-click alone: the panel had no role=menu, so its six menuitems were
orphaned, nothing moved focus into it, and nothing handled arrows or
Escape (a11y.3). Phase 1 deferred this deliberately so it would land
with the dialogs, as one focus-management implementation.
MenuKeyboard is that model. It is standalone rather than part of
ContextMenuController because playlist-view renders a menu without the
controller, and the only thing worse than a menu with no keyboard model
is two menus with two of them. Shift+F10 and the ContextMenu key open it
from a focused row, anchored to that row, and focus returns there.
Three lists had no focused row to open it from, so they gained a roving
tab stop (utils/roving-rows.ts, written once rather than three times).
track-list keeps its own: it predates this, carries selection semantics
the other three do not have, and is pinned by its own tests.
Also the ARIA tail this is one story with: aria-sort on the column
headers (role=columnheader arrived in Phase 1 without it), listbox and
option on the four selectable grids — aria-selected on role=button is
invalid and was being dropped, so the state the whole ctrl/shift
interaction exists to produce was invisible — and live regions on the
four async surfaces that changed in silence.
Two things a reproduction taught that reading could not: the
wa-dropdown-items have not set their role when the host's updateComplete
resolves, so querying by role then finds nothing and the menu opens
without taking focus; and focus() on a popup that has not positioned
itself is a silent no-op.
- New tokens.css.ts exports designTokens with --yj-icon-sm/md/lg and --yj-text-xs/sm/md/lg/xl
- Values derived from existing pixel sizes scattered across codebase
- :host scoped for component-level adoption via static styles array