Every line is a test in three engines or a gate on every commit. The gaps are listed the same way, each with the requirement that will close it.
MeasuredARIA APG pattern in the JSDoclibs/components/tabs/src/tabs.ts, libs/components/tabs/src/tab.ts
MeasuredKeyboard map testedapps/sandbox-e2e/src/tabs.spec.ts (every row above, in three engines) and libs/components/tabs/src/tabs.spec.ts (the walk's edges, the disabled skip, the keys it leaves alone)
Measuredaxe auditapps/sandbox-e2e/src/a11y.spec.ts — the /tabs view, LTR and RTL, with all four strips of the sandbox on the page at once (horizontal, manual, vertical, overflowing); and a stage of its own in the derived list of panels — a strip's panel is a panel part the sandbox holds open, so the stage holds the claim that one stands open in the walk rather than an audit of its own (lesson-196) MeasuredVisual screenshotapps/sandbox-e2e/src/visual.spec.ts — tabs-strip, tabs-vertical, tabs-vertical-rtl and tabs-segmented
Measuredforced-colors: activeapps/sandbox-e2e/src/forced-colors.spec.ts — the chosen tab's edge in Highlight against Canvas on the ones not chosen: the mark survives because it is drawn on every tab and only its colour differs, so no palette can flatten a line into a line that is not there
Measuredprefers-reduced-motionlibs/components/tabs/src/tabs.scss — the only transition is color over --pct-motion-transition-duration, and the preference is answered by the token build (req-a11y-motion)
MeasuredTouch target ≥ 24×24 pxapps/sandbox-e2e/src/tabs.spec.ts — every tab's box measured outright; the floor is --pct-tabs-tab-target-min on the tab itself, so what a finger gets is what an eye sees
By designSize axisnone — deliberately: a tab is not a control with a height. req-api-size is about --pct-control-height-*, and a strip takes its height from the type and the padding, which a skin moves through the tokens
MeasuredRTLapps/sandbox-e2e/src/tabs.spec.ts (the arrows swapped, with the direction inherited from the document rather than written on the strip) plus the tabs-vertical-rtl screenshot, which is where the rail and the mark change sides with no rule of their own
MeasuredSSR + hydrationapps/sandbox-e2e/src/hydration.spec.ts — the /tabs view renders and hydrates with no NG05xx. Every panel is in the server's HTML, hidden by an attribute rather than by a branch
By designFormsnone — deliberately: a tab strip is not a form control. It holds no value a form owns and implements no FormValueControl
MeasuredThe hidden panel is findableapps/sandbox-e2e/src/tabs.spec.ts — hidden="until-found" computes content-visibility: hidden with display: block, and the text inside answers checkVisibility() === false; libs/components/tabs/src/tabs.spec.ts for the three states of the attribute
MeasuredThe reveal is answeredlibs/components/tabs/src/tabs.spec.ts — beforematch on a panel chooses its tab, and does not when the tab is disabled
MeasuredA tab stands in a tablisttools/check-aria.mjs point 9 — every role="tab" read off the template's tree with nothing but its tablist above it, axe's own requiredContext table being the rule (lesson-65) MeasuredThe strip is one Tab stopapps/sandbox-e2e/src/tabs.spec.ts — the roving 0, the next Tab landing on the panel, and the scroll container's tabIndex === -1 in all three engines (lesson-126) MeasuredParts in the inventorylibs/components/parts.snapshot.md, tools/check-parts.mjs (target check-parts)
MeasuredTokens + contrast.policy.jsonlibs/tokens/src/contrast.policy.json — nine entries. The rail and the raised segment are warn and say why: neither is what identifies a control or its state, and the things that do — both label pairs and the chosen tab's mark — are errors
By designStrings through PCT_TEXTSnone — deliberately: the component prints no string of its own, so there is no key to route. tools/check-texts.mjs reads its templates all the same
MeasuredSize budgetlibs/components/size.snapshot.md — the ./tabs row
MeasuredScreen-reader logRead 2026-09-28 from docs/acr/at/ — three readers, whole sandbox, and the act — ArrowRight on the selected tab. The revealed tab is announced by the two readers whose arrow reached it — NVDA with its position, Network, tab, selected, 2 of 3, Orca by name and role, Network · page tab. — and neither reads the panel it revealed, which is the pattern's promise — an arrow announces the tab and the panel waits to be entered. VoiceOver's arrow moved nothing in this pass — focus stayed on the first tab and the reader said nothing — after a walk that read one stop (General selected tab, 1 of 3 Account settings tab group) and moved no further; where its cursor stood, the record does not say. On arrival the tab and its panel are both announced: Orca General · page tab. then General · scroll pane clickable., NVDA General, property page — the panel's content, which the record of 2026-09-16 heard in full (543eacde), stayed unread this time
MeasuredThe segmented faceapps/sandbox-e2e/src/tabs.spec.ts — the track, the raised segment and the four rounded corners read computed; libs/components/tabs/src/tabs.spec.ts for the attribute the face is keyed on and its default
MeasuredA side strip stands beside itapps/sandbox-e2e/src/tabs.spec.ts — the panel's box measured against the strip's in LTR and RTL, so the rail, the inset and the order all change sides together
Measureddocs page/components/tabs on the published site — prerendered, the demo's own source is the code tab