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/dialog/src/dialog.ts — the pattern named in the class comment
MeasuredKeyboard map testedapps/sandbox-e2e/src/dialog.spec.ts, libs/components/dialog/src/dialog.spec.ts
Measuredaxe auditapps/sandbox-e2e/src/a11y.spec.ts — the /dialog view, and separately an open panel, which the walk over the routes cannot reach
MeasuredVisual screenshotapps/sandbox-e2e/src/visual.spec.ts — dialog-open and dialog-open-rtl, the whole viewport rather than the panel: the veil over the page is part of what this component draws — with the sandbox's navigation blanked by the stage, so a row in that list is not a pixel here (req-quality-e2e) Measuredforced-colors: activeapps/sandbox-e2e/src/forced-colors.spec.ts — the mode has no translucent system colour, so the veil says Canvas outright and the panel keeps a CanvasText edge; without it a dialog there is a rectangle of page on a rectangle of page
By designprefers-reduced-motionnone — deliberately: the dialog animates nothing. There is no duration to reduce, and an enter transition arrives with the tooltip, where hover/focus parity forces one anyway
MeasuredTouch target ≥ 24×24 pxlibs/tokens/src/component.dialog.json — the close button's box is {pct.target.min}, not the 16 px cross it draws
By designSize axisnone — deliberately: a dialog has no control height. req-api-size is about --pct-control-height-*, and the only width a modal has is --pct-dialog-panel-max-width, which a consumer overrides
MeasuredRTLapps/sandbox-e2e/src/dialog.spec.ts — the direction carried onto the panel, which an overlay severs (lesson-35); plus the dialog-open-rtl screenshot. The scroll lock's scrollbar compensation is padding-inline-end, so it gives back the edge that moved
MeasuredSSR + hydrationapps/sandbox-e2e/src/hydration.spec.ts — the /dialog view renders and hydrates with no NG05xx; a closed dialog contributes no markup at all. The inline half is read in libs/components/dialog/src/dialog.spec.ts instead, under the flag Angular's own afterNextRender reads (ngServerMode): the inline panel is in the markup, the overlay panel is not
MeasuredInline (inline)libs/components/dialog/src/dialog.spec.ts — twelve cases, and most of them read an ABSENCE: no veil, no aria-modal, no inert, no scroll lock, no capture, no Escape, and no warning about ways out that do not exist. Plus the crossing, which is the shape the input is for: a dialog that turns from a layer into a section while it is open keeps its state, hands the page back and emits no closed — it did not close, it changed where it draws
By designFormsnone — deliberately: a dialog is not a form control. It holds no value, implements no FormValueControl, and the controls inside its content are the ones bound to a form
MeasuredFocus: trap, capture, restoreapps/sandbox-e2e/src/dialog.spec.ts — Tab never leaves the panel, focus goes back to the opener, and pctAutofocus decides what takes it. Measured in a browser and nowhere else: jsdom has no layout for the interactivity checker to read. Their ABSENCE inline needs no layout and is read in the unit spec: the keyboard does not move when the panel appears
MeasuredThe background goes inertapps/sandbox-e2e/src/dialog.spec.ts (the pointer, the keyboard and a script all refused) + libs/components/core/src/core.spec.ts (which elements are marked). Two places for one behaviour, the same split PctFocusStays lives with. Inline nothing is marked at all, and the counter behind it stays at zero
MeasuredThe scroll lockapps/sandbox-e2e/src/dialog.spec.ts — the wheel moves nothing while it is up and moves again after, plus the scrollbar's width handed back to the layout (visible in webkit alone; blink and gecko run here with overlay scrollbars and a gutter of 0) — the one coin toss this case ever showed (WebKit, 2026-09-02) was the READING of the baseline and not the lock, and the item that recorded it named the wrong step (lesson-149, lesson-164) MeasuredParts in the inventorylibs/components/parts.snapshot.md, tools/check-parts.mjs (target check-parts)
MeasuredTokens + contrast.policy.jsonlibs/tokens/src/contrast.policy.json — five pairs, of which the one worth having is "the panel's edge against the dimmed page": not "is the veil readable" (it carries no text) but "can you see where the dialog ends"
MeasuredStrings through PCT_TEXTStools/check-texts.mjs + libs/components/dialog/src/dialog.spec.ts — an application's own close label replaces the default and leaves the rest of the texts alone
MeasuredSize budgetlibs/components/size.snapshot.md — the ./dialog row, and the ./core row beside it: measured the day it landed, 2026-08-20, the modal half was a service every entrypoint carried, 1041 B of it, whether or not it ever injected one
MeasuredScreen-reader logRead 2026-09-28 from docs/acr/at/ — three readers, whole sandbox, and the act that opens it. Two of the three announce the modal as it takes focus: NVDA Project settings, dialog, VoiceOver Project settings web dialog with 4 items Close button — the name, the role, and for VoiceOver the size and the control focus landed on. Orca says nothing with focus on the close button, on the desk and on the runner alike, and speaks again only on Escape (main content · Open the dialog · button.) — the one silence of the nine acts that repeats in every reading, and the plan holds it as a finding. Whether a live region behind the modal still reaches the user is a second act, not this one, and stays unread. VoiceOver's Escape closed nothing here: that reader takes the key
Measureddocs page/components/dialog on the published site — prerendered, the demo's own source is the code tab