Skip to content

Name the thing focus lands on

Two components put a tab stop on a scroll container and told screen readers nothing about where they had arrived.

View as Markdown

A code block that scrolls sideways needs a tab stop, or a keyboard user cannot reach the rest of the line. Both of ours had one. Neither said what it was.

Finding it by class, not by instance

The interesting move was not fixing the code block. It was asking what else had the same shape. Every element in the package that renders tabindex="0":

Component Role Named
scroll-area/root region yes, required
table/container region yes, required
tabs/content tabpanel yes
context-menu/trigger group yes
code-block/body none no
carousel/content group no

Two gaps, not one. The carousel was the better find: its root names itself and carries aria-roledescription="carousel", each slide names itself and carries slide, and the focusable scroll viewport between them announced a group and nothing else.

The worst case is the empty one

For the code block, the case that turns this from polish into a real gap is the one with no title, no language, and copying switched off. The header is omitted entirely, so the figure has no figcaption and therefore no name either. The name on the body is then the only name anywhere in the subtree.

The rule that came out of it

The outer element carries the consumer’s name; the inner scroll container says what is inside it. A code block’s figure is named by its title, and the body is named “Code” or “bash code”. A carousel is named “Featured products” by whoever placed it, and the viewport is named “Slides”.

That is why the inner name is derived rather than required. Ask the consumer for a second name and there are only two things they can write: the outer name again, which is a duplicate announcement, or the default spelled out by hand. Requiring it buys redundancy or boilerplate, never information.