## What a screen reader says Every control on the phone is named by what it does, in the app's own words, and where the desktop already has a word for something it is the word used here too — Seek, View album artwork, Close artwork viewer. An icon-only control carries an `aria-label`; one with visible text is named by the text it shows. The nav is a tab set: `role="tablist"` on the bar, `role="tab"` and `aria-selected` on the five destinations, and the count badge round seventeen added is part of the tab's own name — "You, 3 transfers", "Community, 2 unread" — rather than a digit read out before the word it belongs to. The badge itself is `aria-hidden`, because the number is in the name now. The two sheets are dialogs: `role="dialog" aria-modal="true"`, the player sheet named for what is playing, the half sheet reading its own first title back out of the DOM so that twenty-seven kinds of sheet need no list of names to keep in step. Opening one puts the focus somewhere sensible — the player sheet on its Close, the half sheet on its grab handle, which is a real button now ("Close this sheet") and the first dismissal a screen reader could ever reach; the scrim behind it never was one. A sheet that is down is `inert` and `aria-hidden`. Neither sheet is unmounted — they are translated off the bottom of the screen, and the half sheet deliberately keeps its last body rendered through the slide down — so before this the closed sheets' rows, the hidden mini bar and the nav under an open sheet were all still in the accessibility tree, and TalkBack could swipe into every one of them. There is one live region, `.sr-live`, mounted for the life of the app and clipped to a single pixel, because a region that appears with its words already inside it is announced reliably by nobody; an announcement here is a change of text in a node that was always there. That is what carries an error and a notice, so an Undo offer is actually read out — and the Undo button itself stays reachable, only the sentence beside it is hidden from the tree so it is not said twice. Artwork that opens the viewer is a control, not decoration. Artwork that is decoration carries `alt=""` and every icon is `aria-hidden`, so the tree never reads a filename out. The waveform is `role="slider"` with a value and arrow keys where it can seek, and `aria-hidden` on the mini bar where it is only a picture of the position. Switches say on or off, choice chips say pressed, and the two long descriptions and the gap line say expanded. The check, so this does not quietly rot: the walk's probe sweeps every interactive element in the live DOM and counts four things — the ones with no accessible name (`a11yAnon`, with `a11yWho` naming the first three offenders so one run tells you what to add), the ones whose name is junk (`a11yBad`: bare digits, a filename, a lone glyph, an essay), the ones sitting in a layer that is off-screen but still in the tree (`a11yGhost`) and the ones that look chosen without saying so (`a11yState`). All four are asserted at zero on Home, the library, a record, a catalog record, the artist page, You, the taste page, a half sheet, the player sheet and the viewer — and the anonymous count on a room and on both shapes of prompt besides, because a room's message box and the prompt's own field are where the fields this round finally named actually live: assert it only where they are not and the round guards everything except its own work. One known exception, left alone on purpose: the per-row ⋯ is a `role="button"` inside the row's own `