Übersicht
Verwenden Sie das native <dialog>-Element für modale und nicht modale Dialogfelder. Es bietet die Semantik für Barrierefreiheit, die Fokusbehandlung, das Rendering der obersten Ebene und die Behandlung von Esc, die benutzerdefinierte Overlays manuell neu erstellen müssen.
Overlay-Typ auswählen
Wählen Sie die erste Regel aus, die zutrifft. Wenn Sie sich nicht sicher sind, verwenden Sie Regel 1.
Regel 1: Das Overlay unterbricht den Nutzer und blockiert die Seite (Standardeinstellung)
Verwenden Sie diese Option für Bestätigungen, Formulare, Anmeldungen, destruktive Aktionen und alle „modalen“, „Dialog-“, „Lightbox-“ oder „Pop-up-“Anfragen, sofern nicht anders angegeben.
Verwenden Sie <dialog> als modales Dialogfeld, deklarativ mit command="show-modal" (siehe Deklarative Dialog- und Popover-Steuerung) oder aus einem Skript mit showModal().
<button type="button" command="show-modal" commandfor="confirm">Delete</button>
<!-- MANDATORY: no role="dialog" or aria-modal; the native modal already provides them -->
<dialog id="confirm" aria-labelledby="confirm-title">
<h2 id="confirm-title">Delete this item?</h2>
<form method="dialog">
<button value="cancel" autofocus>Cancel</button>
<button value="delete">Delete</button>
</form>
</dialog>
Der Browser bietet: inerte Seite, Fokus-Eingrenzung, oberste Ebene, ::backdrop, Esc und Schließen per Zurück-Geste sowie Rückgabe des Fokus an den Aufrufer.
- Fügen Sie KEINE
role="dialog",aria-modal, Fokusfalle,inert-Umschaltung oderEsc-Handler hinzu. - Öffnen Sie sie NICHT mit
show()oder dem Attributopen. - DO stop the page scrolling behind it with the CSS below. Weitere Informationen finden Sie unter Verhindern, dass die Seite unter einem modalen Dialogfeld gescrollt wird.
- NICHT das Scrollen mit
overflow: hiddenoderposition: fixedaufhtmloderbodysperren.
/* MANDATORY (both rules): without them, scrolling over the backdrop or inside the dialog scrolls the page behind it */
dialog:modal {
overscroll-behavior: contain; /* stops scroll gestures in the dialog chaining to the page */
}
dialog::backdrop {
overflow: hidden; /* ::backdrop is not a scroll container by default, so it would ignore overscroll-behavior */
overscroll-behavior: contain;
}
Nicht für Menüs oder Kurzinfos: siehe Regel 3.
Regel 2: Der Nutzer arbeitet auf der Seite weiter, während der Bereich geöffnet ist.
Wird für Suchleisten, Prüfer, Paletten und Erstellungsbereiche verwendet, die semantisch noch Dialogfelder sind.
Verwenden Sie <dialog popover> für ein oberstes Steuerfeld mit Pop-over-Schließung (auto schließt leicht, manual nicht). Verwenden Sie dialog.show() nur, wenn sich das Steuerfeld nicht in der obersten Ebene befinden darf.
<button type="button" popovertarget="find">Find</button>
<dialog id="find" popover="manual" aria-labelledby="find-title">
<h2 id="find-title">Find</h2>
<input type="search" aria-label="Search">
<button type="button" popovertarget="find" popovertargetaction="hide">Close</button>
</dialog>
Die Seite bleibt interaktiv und wird nicht inaktiv.
- DO include a visible close button.
- Verwenden Sie NICHT
showModal(), da dadurch die Seite blockiert wird.
Nicht für Bestätigungen von Blockierungen: siehe Regel 1.
Regel 3: Das Overlay ist kein Dialogfeld
Für Menüs, Drop-down-Menüs, Kurzinfos, Hovercards und Toasts verwenden.
Verwenden Sie ein semantisches Element mit dem Attribut popover, nicht <dialog>.
<button type="button" popovertarget="menu">Actions</button>
<ul id="menu" popover>
<li><a href="/rename">Rename</a></li>
</ul>
Ausschaltmethode auswählen
Die Standardeinstellung hängt davon ab, wie das Dialogfeld geöffnet wird. Wenn closedby fehlt oder ungültig ist, verhält sich showModal() wie closerequest und jede andere Art des Öffnens wie none.
- Nur Esc- und Zurück-Geste (modaler Standard): Legen Sie nichts oder
closedby="closerequest"fest. - Auch durch Klicken außerhalb schließen: Verwenden Sie
closedby="any". - Nur der Entwickler schließt es (z. B. ein Formular mit nicht gespeicherten Änderungen): Verwenden Sie
closedby="none"und fügen Sie einen Bestätigungspfad hinzu. Siehe „Schluss“ unten.
Richtlinien
Barrierefreier Name und erster Schwerpunkt
- DO name every dialog with
aria-labelledbypointing at its heading, oraria-labelwhen there is no visible title. - DO put
autofocuson the primary control or first field. - RICHTIG: Setzen Sie
autofocusbei einer Bestätigung mit destruktiver Aktion auf die sichere Aktion (z. B. „Abbrechen“ oder „Behalten“). - Schreiben Sie KEINEN Code, um den Fokus beim Schließen zurückzugeben. Der Browser gibt sie an den Aufrufer zurück.
Opening
- RICHTIG: Deklarativ mit
command="show-modal"undcommandforauf einem<button>öffnen. Weitere Informationen finden Sie unter Declarative Dialog and Popover Control. - Verwenden Sie
dialog.showModal(), wenn Sie die Datei über ein Skript öffnen.
Closing
- DO close with
<form method="dialog">buttons orcommand="close". - DO choose
closedbydeliberately:any(light dismiss andEsc),closerequest(Esconly, the modal default) ornone(developer-controlled close only). - VERWENDEN Sie
command="request-close"oderrequestClose()für Abbrechen-Schaltflächen, die die Logik für nicht gespeicherte Änderungen berücksichtigen müssen. Sie lösen zuerstcancelaus und ignorierenclosedby. - DO treat
closedbyas progressive enhancement.Escund die Zurück-Geste auf Mobilgeräten schließen Modals bereits ohne diese Taste.
Hooking into close
- DO: Lies
dialog.returnValuein einemclose-Listener, um zu erfahren, welche Taste verwendet wurde, und setze sie dann mitdialog.returnValue = ''zurück. BeiEscund anderen ähnlichen Anfragen wird kein Wert festgelegt. Browser unterscheiden sich darin, ob sie den vorherigen Wert löschen. Ohne das Zurücksetzen kann also ein veralteter Wert weitergegeben werden.''als „geschlossen, ohne eine Auswahl zu treffen“ behandeln. - DO use
cancelto interceptEscandrequest-close, but do not rely onpreventDefault()for repeatedEscpresses without user activation. - EMPFOHLEN: Verwenden Sie
closedby="none"mit einem expliziten Bestätigungsschritt für geänderte Formulare. - DO: Informieren Sie den Nutzer, warum das Dialogfeld geöffnet geblieben ist, wenn
cancelverhindert wird. Zeigen Sie eine sichtbare Meldung an und verweisen Sie mitaria-describedbyauf das Steuerelement, das den Fokus erhält (z. B. „Verwerfen“), damit ein Screenreader die Meldung liest, wenn sich der Fokus verschiebt. Ohne diese Funktion wird beim Verschieben des Fokus auf eine neue Schaltfläche nur der Name dieser Schaltfläche angesagt. - NICHT die Bestätigung die Steuerelemente unter den Mauszeiger verschieben lassen. Reservieren Sie den Platz für die Nachricht (blenden Sie sie mit
visibilityaus, nicht mitdisplayoderhidden) und fügen Sie die destruktive Schaltfläche so hinzu, dass sich vorhandene Schaltflächen nicht verschieben (z. B. als erste in einer rechtsbündigen Zeile). So kann ein zweiter Klick an derselben Stelle die Änderungen des Nutzers nicht verwerfen.
Nicht modale Dialogfelder
- VERWENDEN Sie
<dialog popover>(autoodermanual) für ein Panel auf der obersten Ebene unddialog.show(), wenn Sie die oberste Ebene nicht möchten. - DO include a visible close button in every non-modal dialog.
Hintergrund und Styling
- EMPFOHLEN: Gestalten Sie
::backdropund verwenden Siedialog:modal, um nur modale Stile anzusprechen. - DO leave animation to Animate Elements To and From Top Layer, dragging to Build a Draggable Element, and reparenting to Persistent Top Layer UI.
- Legen Sie
displayfürdialogNICHT bedingungslos fest. Der Status „Geschlossen“ wird überschrieben. Legen Sie den Bereich mitdialog[open]fest.
Bedienungshilfen
- Fügen Sie einem nativen modalen Dialogfeld KEINE
role="dialog",aria-modal, manuelle Fokusfalle oderinert-Umschaltung hinzu. - DO use a native
<button>with an accessible name for close controls. - EMPFOHLEN: Verwenden Sie für Dialogfeldtitel eine einheitliche Überschriftenebene.
Fallback-Strategien
closedby (dialog-closedby)
Ohne closedby wird ein modales Fenster weiterhin mit Esc und der Zurück-Geste geschlossen. Nur das Klicken außerhalb des Fensters geht verloren. Es handelt sich also um eine graduelle Fehlertoleranz. closedby="none" wird auf die gleiche Weise ignoriert: Esc löst cancel aus und schließt dann das Dialogfeld. Behalten Sie den Schutz für nicht gespeicherte Änderungen in einem cancel-Handler bei, nicht nur in closedby="none", damit er auch dort schützt, wo closedby fehlt. Wenn für Ihr Baseline-Ziel ein Schließen durch Tippen erforderlich ist, fügen Sie Folgendes hinzu, wo closedby fehlt:
if (!('closedBy' in HTMLDialogElement.prototype)) {
// Test the pointer position, not event.target: the <dialog> is also the target for its own
// padding and the gaps between its children, so a target check would close on those clicks
const outside = (e, dialog) => {
const r = dialog.getBoundingClientRect();
return e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom;
};
for (const dialog of document.querySelectorAll('dialog[closedby="any"]')) {
let pressedOutside = false;
dialog.addEventListener('pointerdown', (e) => { pressedOutside = outside(e, dialog); });
// Require the press and the release outside, so a text-selection drag that ends on the backdrop does not dismiss
dialog.addEventListener('pointerup', (e) => { if (pressedOutside && outside(e, dialog)) dialog.close(); });
}
}
overscroll-behavior
Behandeln Sie die Scroll-Eingrenzung als progressive Verbesserung. Verwenden Sie keine Feature-Erkennung: @supports meldet das Attribut als unterstützt, auch wenn es von nicht scrollbaren Containern ignoriert wird. Wenn die Funktion nicht erzwungen wird, kann im Dialogfeld weiterhin gescrollt werden, wenn der Inhalt überläuft. Außerdem wird die Seite durch showModal() weiterhin inaktiv. Verwende nicht overflow: hidden oder position: fixed für html oder body, da dadurch die Stamm-Scrollleiste entfernt und das Layout verschoben wird.
Aufruferbefehle (command / commandfor)
Wenn Ihr Baseline-Ziel keine Invoker-Befehle enthält, behalten Sie das Markup bei und laden Sie das Polyfill bedingt (dieselbe Funktionalität, als progressive Verbesserung). Erkenne die Schnittstelle und lade sie nie bedingungslos:
// MANDATORY: feature detect on HTMLButtonElement.prototype and load only when native support is missing
if (!('commandForElement' in HTMLButtonElement.prototype)) {
import('invokers-polyfill'); // npm install invokers-polyfill
}
Nur die Regeln 2 und 3 verwenden Popover.
Fallbacks und Browserunterstützung für Popover
Die Popover API ist größtenteils eine progressive Verbesserung, aber ihre definierenden Verhaltensweisen – die Hochstufung auf die oberste Ebene, das Schließen durch Klicken außerhalb des Popovers und der Aufruf von popovertarget – haben kein CSS-Äquivalent. Ältere Browser benötigen ein Polyfill oder ein manuelles Fallback, wenn Sie kein Polyfill bereitstellen möchten.
Polyfill: Damit das Attribut popover in älteren Browsern unterstützt wird, lade @oddbird/popover-polyfill bedingt. OBLIGATORISCH: Prüfe, ob die Funktion unterstützt wird, indem du nach der Property popover in HTMLElement.prototype suchst, und lade das Polyfill nur, wenn die native Unterstützung fehlt. Lade es NICHT bedingungslos.
Mit einem Bundler oder einer Importzuordnung:
// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
import("@oddbird/popover-polyfill");
}
Ohne Bundler importieren Sie aus einem CDN in einem <script type="module">:
<script type="module">
if (!("popover" in HTMLElement.prototype)) {
import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
}
</script>
Einschränkung beim Styling: Das Polyfill kann die echte Pseudoklasse :popover-open nicht definieren, daher wird stattdessen die Klasse .\:popover-open angewendet. OBLIGATORISCH: Kombinieren Sie die beiden mit :is() oder :where(). Andernfalls verwerfen Browser, die :popover-open nicht unterstützen, die gesamte Regel:
[popover]:is(:popover-open, .\:popover-open) {
display: block;
}
Alternativ können Sie für einen Legacy-Fallback ohne Polyfill position: fixed verwenden und die Koordinaten manuell über getBoundingClientRect() berechnen oder die Standardpositionierung mit inset: auto verwenden, wenn das für den Anwendungsfall akzeptabel ist.