Non-modal Dialog
Surfaces
Open in Storybook: Non-modal Dialog (opens in a new tab)
Overview
A native dialog for a box in the page. Nothing has to stop: the person can ignore it and keep working. It has no ::backdrop, no inert page, no focus trap, no Esc and no top layer, unless it is a popover. The element gives the role, the name, form method="dialog" and returnValue; focus, stacking and closing are the component’s.
The rule. Never showModal() and never aria-modal. Markup open at load: focus stays put. Raised at runtime: focus goes into it, to the heading; focus entering is what assistive tech speaks. No trap, no wrap. A visible, named dismiss button inside, and every exit leaves focus on a real control.
Opened by the open attribute, show() or the Popover API (<dialog popover>). showModal() is the Modal Dialog.
Anatomy
Open each part to see what makes it. The one you are on lights up in the figure.
Page
Nothing inert · “Cookie settings” link
The person keeps working. When the bar closes, focus lands on the link.
<main>…</main> <!-- not inert, no aria-modal --> <footer><a href="#" id="cookie-settings">Cookie settings</a></footer>bar.addEventListener('close', () => { const on = document.activeElement if (bar.contains(on) || on === document.body) cookieLink.focus() })Dialog
<dialog open>first in<body>·position: fixed+scroll-paddingRole, name, met first in reading order. No script. Pinned to the bottom, it never hides the focused link.
<dialog open class="bar" aria-labelledby="ck-h"> <!-- first in body: met first --> <h2 id="ck-h">Cookies on this site</h2> <p>Essential ones keep the bag working. Others help us improve.</p> <form method="dialog"> <button value="essential">Essential only</button> <button value="all">Accept all</button> </form> </dialog>.bar { position: fixed; inset: auto 0 0 0; } html { scroll-padding-block-end: var(--bar-h); } body { padding-block-end: var(--bar-h); }
Usage
Use cases
Six that stay in the page and four that are popovers, each the real component and each working. Each stands in a page in small, so what is pinned to the window is pinned to it.
Consent bar
Present from first paint; nothing behind it is inert
<dialog open>first in body · focus: none ·closedbynone · after: “Cookie settings” linkFocus stays where it is at load. Answering it leaves nothing to restore, so focus goes to the link that stands for it.
Maintenance notice
One acknowledgement, one button
<dialog open>in the flow (position: static) · focus: none · one acknowledgement · after: next controlThe title says what and the consequence. Got it is the only answer.
Toast
Raised at runtime, with a way out and no timer
Halden Coffee
New coffees
Last action: none yet
show()after Save draft · focus: the heading (autofocus) ·closerequest· no timer · after: back to Save draftFocus entering the heading is what is spoken. Esc closes the newest toast, wherever focus is.
Toast stack
One dialog for each toast
Halden Coffee
New coffees
Last action: none yet
One dialog per toast, own dismiss · focus: the newest toast’s heading · out-of-order dismiss: next open toast, else the trigger
Dismiss one out of order: the browser would restore focus to a toast that is gone, so it is decided before focus leaves.
Panel tethered to its button
An optional panel in the top layer
Halden Coffee
Coffees
Last action: none yet
popover="auto"·popovertargettoggles it · focus: the heading · Esc and outside click close · Close ispopovertargetaction="hide"· after: back to FiltersThe opener’s expanded state is exposed by the browser: no
aria-expandedto keep in step.Toggletip on a figure
A note that opens on click
Halden Coffee
Ristretto
Intensity 9
Opens on click (1.4.13 hover rules never arise) · button named by what it does (“About intensity”), 24×24 · focus: the heading · Esc, outside click or Close
The button says what it does, not what the panel says.
Tour card (coach mark)
Unasked, so light dismiss is right
Halden Coffee
Your coffees
Last action: none yet
popover="auto"· unasked, so light dismiss is right · cost: clicking the ♡ it points at is outside and ends the tour · Skip tour named by the resultShowing an
autopopover closes any other open one.Toast stack as one popover
Manual: it survives Esc and an outside click
Halden Coffee
New coffees
Last action: none yet
Showing an auto popover closes the others: a toast stack is one popover ·
manualsurvives Esc and outside click, so each toast keeps its own CloseEach toast owes a Close, because Esc and an outside click no longer close it.
Props
| Prop | Does | Default |
|---|---|---|
id | Required. The opener’s popovertarget or commandfor points to it, and the id of its heading comes from it | — |
title, ariaLabel | title is the h2 that names it and takes focus when it is raised. ariaLabel names one with no heading of its own | — |
way | markup: <dialog open> from first paint. show: raised with show(). popover: <dialog popover>, opened by an invoker | show |
placement | flow: part of the page. bar: pinned to the bottom, full width. toast: pinned bottom-end. anchored: tethered to its opener | flow |
closedby | none for what holds an answer, closerequest for a toast. Never any: every click on the page is outside | none |
popover | auto closes with Esc and a click outside. manual with neither: each toast owes its own Close | auto |
open, onClose(returnValue) | Raise and lower it from state; for markup it is how it comes back after it was answered. onClose gets the returnValue of the exit, read once and reset. A popover opened by popovertarget needs neither | — |
showClose, closeLabel | The 44 × 44 Close inside, and its name. A popover gets popovertargetaction="hide", the rest the close command: no handler | false, Close |
standIn | The id of the control that stands for a markup-open dialog: focus goes there when it closes, because the browser restores nothing | — |
initialFocus | A selector for what takes focus when it is raised, such as the newest toast of a stack | The heading |
stack | Dialogs of one stack hand focus to the nearest open one when one is dismissed out of order, and to the control that raised the first when none is left | — |
tone, compact, bare | notice is the --wash fill. compact has less padding. bare has no box of its own, for a popover that holds toasts | plain, false, false |
NonModalLevel, anchorStyle(id) | The level of its heading. The style that makes a button the anchor of the popover whose id it is given | 2 |
Example
A button raises it with show() through open. Focus goes to the heading, Undo answers with returnValue, and Close and Esc leave it with focus back on the button.
import { useState } from 'react'
import { ModalDialogButton } from '@/components/ModalDialogButton'
import { NonModalDialog } from '@/components/NonModalDialog'
export const SaveDraft = ({ onUndo }: { onUndo: () => void }) => {
const [open, setOpen] = useState(false)
return (
<>
<ModalDialogButton onClick={() => setOpen(true)}>
Save draft
</ModalDialogButton>
<NonModalDialog
id="saved"
title="Draft saved"
placement="toast"
closedby="closerequest"
showClose
open={open}
onClose={(answer) => {
setOpen(false)
if (answer === 'undo') onUndo()
}}
>
<form method="dialog" className="nmd-actions">
<ModalDialogButton type="submit" value="undo">
Undo
</ModalDialogButton>
</form>
</NonModalDialog>
</>
)
}Specs
Measurements
| Part | Value |
|---|---|
| Box | 1.5px edge in ink, 12px corner, 0 6px 9px shadow at 18%. A bar has no corner, and a box in the flow no shadow |
| Padding | 1.25rem. A bar 1.125rem, a note or a card tethered to a control 1rem, a toast in a stack 0.875rem |
| Heading | h2, 15px bold, line 1.45, in ink |
| Text | 13px, line 1.45, in ink-soft |
| Close | 44 × 44, 8px corner, an 18px glyph in a 3px line (Dialog / Close button) |
| Answers | Dialog / Button: 47px tall, never under 44px. Every answer the same size |
| Toggletip button | 24 × 24, the least that 2.5.8 allows, 2px edge, a 12px corner |
| Bar | Full width, pinned to the bottom, up to half the height of the viewport |
| Toast | 300px wide, pinned bottom-end, 1rem from the edges. Cookie settings 340px, a toast of a stack 270px |
| Popover | A panel 320px, a note or a card 300px, tethered under its button |
Ways to open
| Way | Effect on focus | Use when |
|---|---|---|
<dialog open> in markup | None (focus on body at load). | Present from first paint. |
dialog.show() | Moves to the first focusable child, else the dialog; a real autofocus picks the target. | Raised at runtime. |
<dialog popover> + popovertarget or showPopover() | Same as show(); returns to the previous element on close. | Optional panel in the top layer. |
show() on an open non-modal is a no-op; on an open modal it throws InvalidStateError. The component guards with if (!dialog.open).
Placement
| Rule | How |
|---|---|
| UA default | position: absolute and centred with margin: auto. |
| In the flow | position: static. |
| Floating | position: fixed, as a direct child of <body>: no top layer, so a transformed or clipped ancestor traps it and a later sibling’s z-index covers it. Need the top layer? <dialog popover>. |
| A fixed bar never hides focus | scroll-padding-block-end equal to its height on html, and the same padding-block-end on body. The component sets both while the bar is open and pinned to the window. |
| 320 px and 400% | A fixed bar must not fill the viewport. Put it in the flow, or cap its height and let it scroll. |
| Focus ring | 3:1 against the bar’s own fill. Forced colors: the edge stays, 1px solid CanvasText. |
| Motion | What is raised moves 0.5rem and fades in 0.2s; what is in the markup at first paint does not move. Reduced motion keeps display and the fade at 1ms. |
Adapting to viewports
| Width | Bar | Popover |
|---|---|---|
| 1280 CSS px · 100% | Fixed to the bottom, full width; scroll padding = its height | Tethered to its button with anchor positioning. |
| 640 CSS px · 200% | Fixed, text above the answers | flip-block when there is no room below. |
| 390 · mobile | Fixed, capped height, scrolls inside | Full width under the button, max-block-size, scrolls inside. |
| 320 CSS px · 400% (1.4.10) | In the flow (position: static), first in body | Full width, never wider than the viewport. |
| Text 200% (1.4.4) | Height from content | Height from content; text wraps. |
Markup
The consent bar, present from first paint, and a popover with its anchor.
<body>
<dialog open class="bar" aria-labelledby="ck-h"> <!-- first in body: met first -->
<h2 id="ck-h">Cookies on this site</h2>
<p>Essential ones keep the bag working. Others help us improve.</p>
<form method="dialog">
<button value="essential">Essential only</button>
<button value="all">Accept all</button>
</form>
</dialog>
<header>…</header>
<main>…</main>
<footer><a href="#" id="cookie-settings">Cookie settings</a></footer>
</body><button popovertarget="filters">Filters</button>
<dialog id="filters" popover aria-labelledby="f-h">
<h2 id="f-h" tabindex="-1" autofocus>Filters</h2>
<button popovertarget="filters" popovertargetaction="hide" aria-label="Close">×</button>
…
</dialog>
[popovertarget="filters"] { anchor-name: --filters; }
#filters { position-anchor: --filters; position-area: block-end span-inline-end;
position-try-fallbacks: flip-block; inset: auto; margin: 0; }
dialog[open], dialog:popover-open { display: grid; } /* never on the base rule */Script
Markup first. Script only for what is raised at runtime and for stacks.
const toast = document.getElementById('saved')
const cookieLink = document.getElementById('cookie-settings')
const bar = document.querySelector('dialog.bar') // markup-open consent bar
// Raised at runtime: focus enters the heading, which is what AT speaks
let trigger = null
function showToast() {
trigger = document.activeElement // for out-of-order dismiss
if (!toast.open) toast.show()
}
toast.addEventListener('close', () => {
if (toast.returnValue === 'undo') undoSave() // the exact word
toast.returnValue = '' // reset after reading
}) // show() returns focus to the trigger
// Markup-open bar: nothing to restore, so move focus to its stand-in
bar.addEventListener('close', () => {
const on = document.activeElement
if (bar.contains(on) || on === document.body) cookieLink.focus()
})
// Out-of-order dismiss in a stack: decide before focus leaves
document.querySelectorAll('dialog.toast').forEach((dialog) => {
dialog.addEventListener('beforetoggle', (event) => {
if (event.newState !== 'closed' || !dialog.contains(document.activeElement)) return
const next = [...document.querySelectorAll('dialog.toast[open]')].find(
(other) => other !== dialog,
)
queueMicrotask(() => (next ? next.querySelector('h2') : trigger).focus())
})
})Tokens
Component tokens
The values of the Figma page. Dialog / Button and Dialog / Close button are the ones of the Modal Dialog.
| Part | Property | Token | Value |
|---|---|---|---|
| Box | Edge · corner | — | 1.5px · 12px (a bar has no corner) |
| Box | Fill · text | white · ink | #ffffff · #16161a |
| Box | Shadow | — | 0 6px 9px at 18% |
| Notice | Fill | wash | #edebf4 (the Figma draws #efedf6) |
| Heading | Size · weight · line | — | 15px · 700 · 1.45 |
| Text | Size · line · colour | ink-soft | 13px · 1.45 · #4b4b55 |
| Padding | Box · bar · compact · toast of a stack | — | 20px · 18px · 16px · 14px |
| Close | Size · corner | radius | 44 × 44 · 8px |
| Answers | Dialog / Button | — | 47px, never under 44px · 6px corner (as in the Modal Dialog) |
| Toggletip button | Size · edge · corner | bw | 24 × 24 · 2px · 12px |
| Checkbox | Size · accent | lilac | 24px · #5e548e |
| Motion | Rise · fade | — | 0.5rem · 0.2s |
| Focus ring | Colour · width | focus-ring-primary · focus-ring-width | The site’s, not redeclared |
Guidelines
Non-modal or something else?
| Need | Use |
|---|---|
| Present from first paint (consent, locale, maintenance) | <dialog open> in the markup: no script has to run; showModal() can’t |
| Raised at runtime (click, key, timer, result): toast, notice | dialog.show() |
| Optional panel or note tethered to its button, light dismiss wanted, must not be clipped | <dialog popover> |
| Must block (age gate, terms): the page behind becomes inert | Not a non-modal. A non-modal never makes the page inert: Modal Dialog |
| Must block and wait | showModal() · Modal Dialog |
| Text only, no controls, no dismiss | Not a dialog: a plain section |
closedby
Dismissing is not an answer. Pick the smallest close that fits.
| closedby | Esc | Outside click | Use |
|---|---|---|---|
none (default) | nothing | nothing | Bars that hold an answer: dismissing is not an answer. |
closerequest | closes the newest shown, wherever focus is | nothing | Transient: toasts. |
any | same | closes: every click on the page is outside | Never. |
Answers and timing
| Rule | How |
|---|---|
returnValue | form method="dialog" + submit value works as in a modal. Compare the exact word, reset to '' after reading. |
| A refusal that must not close | type="button". |
| An answer bar | Every answer one click, same size, same step. Refusing never costs an extra screen. |
| No timer on text to read or a control | 2.2.1. If it must expire, pause on hover and focus. |
Stacks
Each toast is its own dialog with its own dismiss. Focus goes to the newest toast’s heading.
| Rule | How |
|---|---|
| One dialog per toast | Named by its title, with its own dismiss button. Focus goes to the newest toast’s heading. |
| Out-of-order dismiss | In beforetoggle (newState === "closed") read dialog.contains(document.activeElement): by close, focus has left. Then focus the next open toast, else the trigger. |
| As popovers | auto popovers close each other: a toast stack is one popover. |
| Under an open modal | A non-modal or popover outside an open modal takes no focus and no clicks. Raise it inside the modal, or hold it until the modal closes. |
Popover
The one non-modal in the top layer: no clipping ancestor, no z-index fights. For an optional panel or note tethered to its button. What stays in the page uses show().
| Rule | How |
|---|---|
| Free | popovertarget toggles it with no handler or state; auto closes on Esc and outside click; focus goes in on open and back to the invoker on every exit; the invoker’s expanded state is exposed: no aria-expanded to sync. |
| Close inside | A second invoker: popovertarget + popovertargetaction="hide", no handler. |
| Focus in | The heading: tabindex="-1" + a real autofocus. Without it focus lands on the first control. |
auto or manual | auto for panels, notes and tours: Esc and outside click are how a person leaves. manual survives both and owes a replacement for each. |
| One at a time | Showing an auto closes any other open auto that is not its ancestor. Cost: any click on the page is outside. Right for something unasked, wrong for a form. |
| Tab past the end | Leaves it open. To close: hide on blur when relatedTarget is outside the panel; note pointerdown inside first. |
| Invoker gone | Focus falls to body: send it to the page’s own heading, only if nothing else took it. |
| CSS | Never gets open: display on :popover-open, not [open]. Clear the UA inset: 0; margin: auto. |
| Tether | anchor-name on the button, position-anchor + position-area on the panel, position-try-fallbacks: flip-block. Without anchor positioning, place it on scroll and resize. |
| Not a menu | role="menu" is the arrow-key contract for commands; controls you Tab between are not one. |
::backdrop | A popover gets one and makes nothing inert: the page stays live. A dim means nothing unless everything else carries inert. |
| AX tree | Chromium marks an open dialog popover as modal: true though the page stays live. Not checked in AT. |
Copy
Title: what and the consequence. Body: one or two sentences. Buttons named by the result.
| Part | Don’t | Do |
|---|---|---|
| Title | Important notice | Ordering pauses Saturday 02:00–04:00 UTC |
| Body | We are performing scheduled maintenance to improve your experience… | Your bag stays saved. Checkout opens again at 04:00. |
| Buttons | OK · Yes · No · Cancel | Essential only · Accept all · Got it |
| Acknowledgement | Got it + Close + Dismiss | One acknowledgement is one button. |
| Toggletip button | “Intensity is how strong the cup tastes” | “About intensity”: what the button does, not what the panel says. |
Controls by scenario and context
Each row is one use case. Each column shows the controls that dialog gets at that size. Bars are scaled-down copies of the real components (Dialog / Button, Dialog / Close button). Zoom uses the reflow of Adapting to viewports.
- Solid: required in that context, never dropped
- Opener · Stand-inOutside the dialog: where focus returns, or the control that stands for a markup-open bar
Zoom 400% · 320 CSS px. At 200% text bars go in the flow.
Consent bar
<dialog open> · first in body
- Desktop · 640 px and upBar, toast or panel at full width
Essential only · Accept all (same size, same step)
Stand-in
Focus stays at load. Pinned bottom with scroll padding. After: focus to “Cookie settings”.
- Smaller viewport · 400–639 pxNarrow column
Essential only · Accept all
Text above, answers below; still one click each.
- Zoom 400% · 320 CSS pxReflow
Stacked, in the flow
At 320 px it goes in the flow (position: static), never filling the viewport.
Maintenance notice
<dialog open> · in the flow
- Desktop · 640 px and upBar, toast or panel at full width
Got it
One acknowledgement, one button. Title says what and the consequence.
- Smaller viewport · 400–639 pxNarrow column
Got it
Same.
- Zoom 400% · 320 CSS pxReflow
Got it
Text wraps; in the flow at every size.
Locale banner
<dialog open> · first in body
- Desktop · 640 px and upBar, toast or panel at full width
Italiano · Stay in English
Buttons named by the result, never Yes / No.
- Smaller viewport · 400–639 pxNarrow column
Italiano · Stay in English
Same.
- Zoom 400% · 320 CSS pxReflow
Stacked
In the flow; full-width buttons.
Toast
show() · closerequest
- Desktop · 640 px and upBar, toast or panel at full width
Heading (focused) · Undo · Close
Opener
Focus to the heading on show(); back to the trigger on close. Esc closes the newest. No timer.
- Smaller viewport · 400–639 pxNarrow column
Undo · Close
Opener
Bottom, full width minus 16px.
- Zoom 400% · 320 CSS pxReflow
Undo · Close
Height capped; never covers the focused control.
Toast stack
One dialog per toast
- Desktop · 640 px and upBar, toast or panel at full width
Close per toast · Undo where it applies
Opener
Focus to the newest toast’s heading. Out-of-order dismiss: next open toast, else the trigger.
- Smaller viewport · 400–639 pxNarrow column
Close per toast
Opener
Bottom, full width minus 16px. Each toast keeps its own Close.
- Zoom 400% · 320 CSS pxReflow
Close per toast
Bottom, full width minus 16px; height capped.
Cookie settings
show() from a link
- Desktop · 640 px and upBar, toast or panel at full width
Close · Save choices
Opener
Focus to the heading (autofocus). On close the browser returns focus to the link.
- Smaller viewport · 400–639 pxNarrow column
Close · Save choices
Opener
Same.
- Zoom 400% · 320 CSS pxReflow
Close · Save choices
In the flow under the link; no trap.
Popover panel
<dialog popover> · auto
- Desktop · 640 px and upBar, toast or panel at full width
Close (popovertargetaction="hide") · Show 24 coffees
Opener
Tethered under Filters with anchor positioning. Esc and outside click close; focus back to Filters.
- Smaller viewport · 400–639 pxNarrow column
Close · Show 24 coffees
Opener
position-try-fallbacks: flip-block when there is no room below.
- Zoom 400% · 320 CSS pxReflow
Close · Show 24 coffees
Full width under the button; max height, scrolls inside; never wider than the viewport.
Toggletip
<dialog popover> · on a figure
- Desktop · 640 px and upBar, toast or panel at full width
Close
Opener
Opens on click; the 24×24 button says what it does (“About intensity”).
- Smaller viewport · 400–639 pxNarrow column
Close
Opener
Same.
- Zoom 400% · 320 CSS pxReflow
Close
Full width under the figure; text wraps.
Tour card
<dialog popover> · auto · unasked
- Desktop · 640 px and upBar, toast or panel at full width
Skip tour · Next · Close
Opener
Light dismiss is right for something unasked. Clicking what it points at ends the tour.
- Smaller viewport · 400–639 pxNarrow column
Skip tour · Next · Close
Opener
Same.
- Zoom 400% · 320 CSS pxReflow
Stacked
Card below its target, full width.
Accessibility
Requirements
| Rule | WCAG |
|---|---|
Never showModal() and never aria-modal="true" here: it tells AT the page is inert when it isn’t. | 4.1.2 |
Name with aria-labelledby (the heading); aria-label only without one. | 4.1.2 · 1.3.1 |
| Markup open at load: focus stays put, or it steals the caret from someone reading. Raised at runtime (toast, notice, result): focus travels into it, to the heading. Focus entering is what AT speaks. | 2.4.3 · 4.1.2 |
Every exit leaves focus on a real control. Raised by show(), focus returns to the trigger. A markup-open dialog restores nothing: focus fell to body. | 2.4.3 |
| No focus trap, no Tab wrap: a trap on a page that is still live is a keyboard trap. | 2.1.2 |
| A visible, named dismiss button inside, reachable by focus. Esc is a bonus, never the only way out. | 2.1.1 |
Closed means out of the tab order: display only on [open] (:popover-open on a popover), never on the base rule. | 2.4.3 |
| Contents follow the accessible frontend rules. | — |
Focus management
Who raised it decides where focus goes.
| Case | Focus |
|---|---|
Page load, markup open | Nowhere. The bar waits in the tab order. |
| Anything raised at runtime (button, “Cookie settings” link, timer, result) | The heading: tabindex="-1" and a real autofocus attribute (show() reads it; React’s autoFocus prop never sets it). |
Closed after show() or a popover | The browser returns focus to the element that had it, if it still exists. |
| Markup open, or the trigger is gone | Move it yourself: to the control that now stands for the bar (a “Cookie settings” link), else the next sensible control. Never body. |
| DOM position | Is tab order and reading order. A bar the person must meet goes first in <body>, pinned with CSS. Last in the DOM means after the whole page. |
Keyboard
No wrap, no trap: the page is still live.
| Key | Does |
|---|---|
| Tab · Shift+Tab | In, through and out in DOM order. No wrap. Tab past the last control of a popover leaves it open. |
| Esc | Nothing, unless closerequest (the newest toast) or popover="auto". |
| Enter · Space | Activates the focused button. Enter in a method="dialog" form submits and closes. |
WCAG criteria
What each success criterion asks of a non-modal dialog, and what meets it.
| Criterion | Met by |
|---|---|
| 1.3.1 Info and Relationships (A) | Named by its heading; form method="dialog" answers |
| 1.3.2 Meaningful Sequence (A) | DOM position = reading order: a bar to meet goes first in body |
| 1.4.3 Contrast (Minimum) (AA) | Text 4.5:1 against the bar’s own fill |
| 1.4.10 Reflow (AA) | At 320 px the bar goes in the flow; a popover fits the viewport |
| 1.4.11 Non-text Contrast (AA) | Focus ring 3:1 against the bar’s fill; forced colors keep a border |
| 1.4.13 Content on Hover or Focus (AA) | Not triggered: toggletips open on click |
| 2.1.1 Keyboard (A) | Every answer and dismiss is a button |
| 2.1.2 No Keyboard Trap (A) | No trap, no wrap: in, through and out |
| 2.2.1 Timing Adjustable (A) | No timer on text or a control; pause on hover and focus |
| 2.4.3 Focus Order (A) | Runtime: focus to the heading, back to the trigger; load: none; every exit on a real control |
| 2.4.7 Focus Visible (AA) | The site’s ring on the dialog’s controls |
| 2.4.11 Focus Not Obscured (Minimum) (AA) | scroll-padding-block-end = the fixed bar’s height |
| 2.5.8 Target Size (Minimum) (AA) | Answers 44 × 44; toggletip button at least 24 × 24 |
| 3.2.4 Consistent Identification (AA) | Same title and buttons wherever the bar appears |
| 4.1.2 Name, Role, Value (A) | Native dialog role; never aria-modal; popover expanded state exposed by the invoker |
Related
References
This section is still in progress.