Skip to content
design system
Thème d’affichage, actuellement Auto

molecule · stable · since 2026-09

Dialog

Purpose

A modal dialog on the native dialog element and nothing else. The element gives, correctly and for free, the four behaviours every hand-made modal gets wrong at least once: the focus is held inside, Escape closes, the page behind is inert, and the focus goes back to the button that opened it. The component adds the name, a labelled close button, a bindable open and the value of the button that closed it.

When to use, when not

Use it for a question the user must answer before going on, a short form, a list to pick from. Do not use it for a message that needs no answer (Alert, or LiveRegion for one that appears after an action), for content that could sit on the page, or for a confirmation of a destructive action, which ConfirmDialog composes from this one.

Anatomy

  1. The heading, which names the dialog.
  2. The body.
  3. The actions, optional: buttons of type submit inside a form method="dialog", each with a value.
  4. The close button, last in the DOM and drawn top right, labelled “Fermer”.
  5. The backdrop, the one veil the system allows.

Props

Props of Dialog
NameTypeDefaultWhat it does
open?boolean | undefinedShown or not; absent reads as closed. Bindable, and set back to false when the dialog closes by any means: Escape, the close button, an action.
titlestringThe heading, which names the dialog. A question or a noun phrase, French.
size?'sm' | 'md' | 'lg''sm'`sm` for a question, `md` for a short form, `lg` for a list or a table.
onclose?(returnValue: string) => voidCalled after the dialog closed, with the value of the button that closed it, or an empty string for Escape and the close button.
closeLabel?string'Fermer'The close button's label.
class?string''Layout classes only: margins, never appearance.
childrenSnippetThe body.
actions?SnippetThe actions at the foot: buttons of type submit in a `form method="dialog"`, whose value comes back through `onclose`.

States

Closed, then open as a question or as a form; the outcome is read from the value of the button that closed it.

form

A form inside: the first field takes the initial focus, since the close button is last in the DOM.

Light

Renommer l'audit

Dark

Renommer l'audit

320 px, light

Renommer l'audit

320 px, dark

Renommer l'audit

216 px, light

Renommer l'audit

Forced colours, captured
Dialog, story form, as Chromium renders it with forced colours active

question

Opened by the button; Escape, the close button and the two actions all close it, and the outcome is read from the value of the button that did.

Light

Retirer la page de l'audit ?

Ses vérifications déjà tranchées seront conservées, mais la page ne comptera plus dans le taux de conformité.

Dark

Retirer la page de l'audit ?

Ses vérifications déjà tranchées seront conservées, mais la page ne comptera plus dans le taux de conformité.

320 px, light

Retirer la page de l'audit ?

Ses vérifications déjà tranchées seront conservées, mais la page ne comptera plus dans le taux de conformité.

320 px, dark

Retirer la page de l'audit ?

Ses vérifications déjà tranchées seront conservées, mais la page ne comptera plus dans le taux de conformité.

216 px, light

Retirer la page de l'audit ?

Ses vérifications déjà tranchées seront conservées, mais la page ne comptera plus dans le taux de conformité.

Forced colours, captured
Dialog, story question, as Chromium renders it with forced colours active

Accessibility contract

  • Role and name: dialog, named by the heading through aria-labelledby. The heading is an h2; the page’s own structure stops at the dialog.
  • Keyboard, focus: showModal holds Tab inside. The initial focus goes to the first focusable element of the body, or to an element marked autofocus; the close button is last in the DOM for that reason. Escape closes. On close, the browser returns the focus to the opener.
  • Live regions: none. The outcome, when it must be said, is the page’s, through LiveRegion.
  • Forced colours: the surface and the line take the system’s colours; the backdrop takes Canvas.

Content rules

French. The title is a question (“Retirer la page de l’audit ?”) or a noun phrase (“Renommer l’audit”). An action button says what it does with a verb (“Retirer la page”, “Enregistrer”), never “OK” or “Oui”. The cancel button says “Annuler”.

Do and don’t

  • Do bind open and set it from the page; never call showModal yourself.
  • Do give every action button a value: it is what onclose hands back.
  • Do not put a second dialog inside a dialog.
  • Do not open a dialog at page load or after a delay: it takes the focus from wherever the user was.

Examples

<Button onclick={() => (open = true)}>Retirer la page</Button>
<Dialog bind:open title="Retirer la page de l'audit ?" onclose={(value) => value === 'confirm' && remove()}>
	<p>Ses vérifications seront conservées, mais la page ne comptera plus.</p>
	{#snippet actions()}
		<form method="dialog">
			<Button variant="secondary" type="submit" value="cancel">Annuler</Button>
			<Button type="submit" value="confirm">Retirer la page</Button>
		</form>
	{/snippet}
</Dialog>

Surfaces

  • App: the confirmation panel of the audit page and the confirm() calls, the page switcher, the export dialog; not ported yet.
  • Extension: the page switcher and the export and sync dialogs of the panel, when the panel is on Svelte.

Related

ConfirmDialog, Button, LiveRegion.

Decisions

  • 2026-09-24: the native element, no focus trap of our own. The site’s modale demo proved the four behaviours come from showModal; a trap written by hand was the extension’s second most patched file.
  • 2026-09-24: the close button is last in the DOM. A dialog whose first stop is “Fermer” announces its exit before its question.
  • 2026-09-24: the outcome travels as the closing button’s value through onclose, not as one handler per button, so a confirmation is one function and reads the same whether the user pressed Escape, the close button or “Annuler”.

Criteria held by construction

  • 7.1, Chaque script est-il, si nécessaire, compatible avec les technologies d'assistance ?
    The native element carries the dialog role and the name comes from its heading through aria-labelledby; nothing is declared by hand, so nothing can be declared wrong.
  • 7.3, Chaque script est-il contrôlable par le clavier et par tout dispositif de pointage ?
    showModal holds the focus inside, Escape closes, the close button is a real button: the whole dialog is driven from the keyboard by the browser itself.
  • 12.8, Dans chaque page web, l'ordre de tabulation est-il cohérent ?
    When the dialog closes, the browser puts the focus back on the element that opened it, so the tab order resumes where the user left it.
  • 12.9, Dans chaque page web, la navigation ne doit pas contenir de piège au clavier. Cette règle est-elle respectée ?
    The focus is held inside while the dialog is open, and Escape always releases it: a trap with an exit is not a trap.

Tokens it reads

  • Colour: --rl-backdrop, --rl-ink, --rl-ink-soft, --rl-line-control, --rl-line-strong, --rl-surface
  • Typography: --rl-weight-bold, --rl-text-xl, --rl-tracking-tight
  • Spacing: --rl-space-12, --rl-space-3, --rl-space-4, --rl-space-5, --rl-space-8, --rl-control-sm
  • Layout: --rl-radius, --rl-border, --rl-border-strong

And one custom property of its own, not tokens: --rl-dialog-width.

Seen in patterns