Found while fixing the release-blocking aria-allowed-attr regression in #5501. Split out because it is not a regression — it predates that change and is a different defect.
What happens
A Timestamp with a hover card puts its tab stop on the inner <time> (Timestamp.tsx:550, added so keyboard users can reveal the card at all), but the HoverCard trigger — and therefore every ARIA attribute expressing the popup relationship — is the Text <span> wrapping that <time>.
HoverCard takes wrapper.firstElementChild as its trigger (HoverCard.tsx:254), and Timestamp's HoverCard child is <Text>, which renders a <span> (Text.tsx:240). So the DOM is:
<span class="astryx-text … astryx-timestamp" aria-haspopup="dialog" aria-controls="…"> ← trigger
<time datetime="…" tabindex="0" aria-label="…">2 hours ago</time> ← tab stop
</span>
A screen-reader user who tabs to a timestamp lands on the <time>. The <time> says nothing about a dialog; the element that does is its parent, which the user never lands on. So the card is reachable by keyboard but its existence is never announced.
Why it is not new
Before #5419 the same span carried aria-describedby instead, with exactly the same mismatch. This has been true since the <time> tab stop was introduced.
Possible directions
Neither is obviously right, hence an issue rather than a PR:
- Make the
<time> the HoverCard's first element child — nest the HoverCard inside Text instead of outside it. HoverCard's wrapper is display: contents, so this should be layout-neutral, but it moves the positioning anchor from the span to the <time> and wants a visual check.
- Teach HoverCard to prefer a focusable descendant as its trigger when the first element child is not focusable. Correct for every consumer at once, but a much wider blast radius.
Worth noting Timestamp is currently the only component in the repo that passes label to a HoverCard, so today this affects Timestamp alone.
Found while fixing the release-blocking
aria-allowed-attrregression in #5501. Split out because it is not a regression — it predates that change and is a different defect.What happens
A
Timestampwith a hover card puts its tab stop on the inner<time>(Timestamp.tsx:550, added so keyboard users can reveal the card at all), but the HoverCard trigger — and therefore every ARIA attribute expressing the popup relationship — is theText<span>wrapping that<time>.HoverCardtakeswrapper.firstElementChildas its trigger (HoverCard.tsx:254), and Timestamp's HoverCard child is<Text>, which renders a<span>(Text.tsx:240). So the DOM is:A screen-reader user who tabs to a timestamp lands on the
<time>. The<time>says nothing about a dialog; the element that does is its parent, which the user never lands on. So the card is reachable by keyboard but its existence is never announced.Why it is not new
Before #5419 the same span carried
aria-describedbyinstead, with exactly the same mismatch. This has been true since the<time>tab stop was introduced.Possible directions
Neither is obviously right, hence an issue rather than a PR:
<time>the HoverCard's first element child — nest the HoverCard insideTextinstead of outside it. HoverCard's wrapper isdisplay: contents, so this should be layout-neutral, but it moves the positioning anchor from the span to the<time>and wants a visual check.Worth noting
Timestampis currently the only component in the repo that passeslabelto aHoverCard, so today this affects Timestamp alone.