From 359a3826c71a6d049746aabab653b1396d38403c Mon Sep 17 00:00:00 2001 From: Edy Cu Date: Thu, 10 Sep 2026 00:26:18 +0700 Subject: [PATCH] fix(a11y): the receipt tape had a name nothing could read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `
` with no role is a `generic`, and generic prohibits an accessible name, so the label was being dropped and axe flags it (`aria-prohibited-attr`). `role="group"` is the smallest role that both permits the name and is honest about what the element is — a list role would require its children to be listitems, and the children are links. It surfaced only once the live tab became the default: in live mode the tape starts empty, so the label is the only thing a screen reader would have had to go on. shipcheck audited the replay hero every previous run and never saw it. --- packages/nextjs/components/landing/Instrument.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/components/landing/Instrument.tsx b/packages/nextjs/components/landing/Instrument.tsx index 3f54d51..2ed6f88 100644 --- a/packages/nextjs/components/landing/Instrument.tsx +++ b/packages/nextjs/components/landing/Instrument.tsx @@ -424,9 +424,14 @@ export function Instrument({ {caption.mono}

- {/* the tape: receipts so far */} + {/* the tape: receipts so far. + `role="group"` because a bare div is `generic`, and generic prohibits an accessible + name — the label below was being dropped, silently, and axe flags it + (`aria-prohibited-attr`). It only showed up once the live tab became the default: + in live mode the tape starts empty, so the label is all a screen reader would get. */}
{mode === "replay"