Composant modal natif basé sur l'élément HTML <dialog>.
S'ouvre via un bouton déclencheur, se ferme via un bouton de fermeture ou programmatiquement.
Supporte l'enchaînement de modals, les variantes colorées et trois modificateurs de taille.
L'animation d'ouverture et de fermeture repose sur des transitions CSS.
Chaque data-target doit correspondre exactement à l'id
du dialog associé. Utilisez le format #id — le JS utilise
document.querySelector() pour les boutons déclencheurs.
⚠️ L'élément cible doit obligatoirement être un <dialog> natif.
Le JS vérifie instanceof HTMLDialogElement avant
d'ouvrir.
Les attributs suivants sont ajoutés automatiquement par Dialog.init() :
aria-modal="true" (indique aux lecteurs
d'écran que le contenu derrière le dialog est inerte)
aria-label="Close" (sur chaque bouton
portant data-dismiss="dialog", uniquement si l'attribut
est absent)
Les attributs suivants doivent être posés manuellement dans le HTML :
aria-labelledby (lie le dialog à son
titre via un id)
aria-hidden="true" (sur les icônes
décoratives)
| CLASSE | ÉLÉMENT | DESCRIPTION |
|---|---|---|
.dialog |
<dialog> |
Classe de base. Positionne le dialog en centré fixe, applique bordure, border-radius, fond et transitions CSS
d'ouverture/fermeture.
|
.dialog-sm |
<dialog> |
Largeur et hauteur max réduites via --dialog-width-sm
et --dialog-max-height-sm.
|
.dialog-md |
<dialog> |
Largeur et hauteur max intermédiaires. |
.dialog-lg |
<dialog> |
Largeur et hauteur max augmentées. |
.dialog-header |
<header> |
En-tête du dialog avec padding via variables CSS. |
.dialog-body |
<div> |
Corps du dialog avec flex: 1 1 auto et overflow-y: auto pour les contenus longs.
|
.dialog-footer |
<footer> |
Pied du dialog avec padding via variables CSS. Optionnel. |
.dialog-{color} |
<dialog> |
Variante colorée générée pour chaque couleur de la map $colors.
Applique background-color, border-color et color via --content-on-{color}.
|
.show |
<dialog> |
Ajoutée par le JS à l'ouverture. Déclenche la transition CSS vers l'état visible (opacity: 1, scale(1)).
Active également le fond du ::backdrop.
|
Un bouton ouvre un dialog. Le bouton de fermeture à l'intérieur le referme.
Un bouton à l'intérieur d'un dialog peut porter simultanément data-dismiss="dialog"
et data-ui="dialog" avec un data-target
pointant vers un autre dialog. Le JS ferme le dialog courant puis ouvre le suivant, en respectant la
transition CSS.
Les variantes sont générées automatiquement pour chaque entrée de la map $colors. La classe suit le pattern .dialog-{name}.
Les modificateurs de taille s'appliquent directement sur l'élément <dialog>
et surchargent --dialog-width et --dialog-max-height.
| ÉTAT | ÉLÉMENT | DESCRIPTION |
|---|---|---|
| Fermé | :not([open]) |
État initial. Le dialog n'est pas dans le DOM visuel. Aucune classe n'est présente. |
| En ouverture | [open] |
Le JS appelle dialog.showModal(). Le dialog est rendu
avec opacity: 0 et scale(0). La classe .show est ajoutée immédiatement après pour déclencher
la transition.
|
| Ouvert | [open].show |
Transition terminée. Le dialog est pleinement visible (opacity:
1, scale(1)). Le backdrop est actif.
|
| En fermeture | [open]:not(.show) |
La classe .show est retirée. La transition CSS ramène
le dialog à opacity: 0 et scale(0). dialog.close()
est appelé à la fin de la transition.
|
| En animation | [data-animating="true"] |
Attribut posé pendant toute la durée d'une transition. Bloque les ouvertures ou fermetures parasites. |
Le composant est piloté par la classe Dialog. Elle s'appuie
sur la délégation d'événements (un seul listener sur document),
les transitions CSS pour les animations, et l'API native HTMLDialogElement.
import Dialog from './Dialog.js';
// A single instance is enough for the entire page.
new Dialog();
La classe implémente un guard Dialog.initialized :
instancier Dialog plusieurs fois n'entraîne aucun
doublon de listeners.
| ATTRIBUT | ÉLÉMENT | DESCRIPTION |
|---|---|---|
data-ui="dialog" |
<button> |
Identifie le bouton comme déclencheur d'ouverture. Peut aussi être combiné avec data-dismiss="dialog" pour enchaîner deux dialogs.
|
data-target |
<button> |
Sélecteur du dialog cible. Doit être au format #id.
Requis sur tout bouton portant data-ui="dialog".
|
data-dismiss="dialog" |
<button> |
Identifie le bouton de fermeture. Le JS remonte au <dialog>
parent le plus proche et appelle Dialog.hide().
|
| MÉTHODE | DESCRIPTION |
|---|---|
Dialog.init() |
Parcourt tous les boutons [data-ui="dialog"] et
ajoute aria-modal="true" sur leur dialog cible si
absent.
|
Dialog.open(dialog) |
Appelle dialog.showModal(), ajoute .show et gère la fin de transition via transitionend. Émet dialog:beforeOpen et dialog:afterOpen.
|
Dialog.hide(dialog) |
Retire .show, attend la fin de la transition puis
appelle dialog.close(). Émet dialog:beforeClose et dialog:afterClose.
|
Dialog.handleOpen(button) |
Résout le dialog cible via data-target. Gère le cas
d'enchaînement : si le bouton porte aussi data-dismiss, ferme le dialog courant avant d'ouvrir le
suivant.
|
Dialog.emitEvent(target, name,
dialog) |
Émet un CustomEvent avec bubbles: true et detail: { element: dialog }. Utilisé pour les quatre
événements du cycle de vie.
|
| ÉVÉNEMENT | DÉCLENCHEMENT |
|---|---|
dialog:beforeOpen |
Émis sur le dialog juste avant l'appel à showModal().
|
dialog:afterOpen |
Émis sur le dialog à la fin de la transition d'ouverture. |
dialog:beforeClose |
Émis sur le dialog juste avant le retrait de .show.
|
dialog:afterClose |
Émis sur le dialog à la fin de la transition de fermeture, après dialog.close().
|
// Open a dialog programmatically
const dialog = document.getElementById('my-dialog');
Dialog.open(dialog);
// Close a dialog programmatically
Dialog.hide(dialog);
// Listen to lifecycle events
dialog.addEventListener('dialog:afterOpen', (e) => {
console.log('Dialog opened', e.detail.element);
});
| TOKEN | RÔLE |
|---|---|
--dialog-width |
Largeur par défaut du dialog |
--dialog-max-height |
Hauteur maximale par défaut |
--dialog-width-sm |
Largeur — taille sm |
--dialog-max-height-sm |
Hauteur maximale — taille sm |
--dialog-width-md |
Largeur — taille md |
--dialog-max-height-md |
Hauteur maximale — taille md |
--dialog-width-lg |
Largeur — taille lg |
--dialog-max-height-lg |
Hauteur maximale — taille lg |
--dialog-border-width |
Épaisseur de la bordure |
--dialog-border-style |
Style de bordure (solid, dashed…)
|
--dialog-border-radius |
Rayon des coins |
--dialog-border-color |
Couleur de la bordure |
--dialog-background-color |
Couleur de fond du dialog |
--dialog-backdrop-background |
Couleur du backdrop affiché derrière le dialog (défini par thème) |
--dialog-header-padding-x |
Padding horizontal de l'en-tête |
--dialog-header-padding-y |
Padding vertical de l'en-tête |
--dialog-body-padding-x |
Padding horizontal du corps |
--dialog-body-padding-y |
Padding vertical du corps |
--dialog-footer-padding-x |
Padding horizontal du pied |
--dialog-footer-padding-y |
Padding vertical du pied |
--content-on-{color} |
Couleur du texte sur fond coloré. Garantit le contraste sur chaque variante de la map. |
/* Global variables */
:root {
--dialog-border-radius: 8px;
--dialog-width:
40rem;
--dialog-max-height: 80vh;
}
/* Light theme */
:root[data-theme="light"] {
color-scheme:
light;
--dialog-backdrop-background: rgba(0, 0, 0, 0.5);
}
/* Dark theme */
:root[data-theme="dark"] {
color-scheme:
dark;
--dialog-backdrop-background: rgba(225, 225, 225, 0.5);
}
| PRATIQUE | DÉTAIL |
|---|---|
aria-modal="true" |
Ajouté automatiquement par Dialog.init(). Indique aux
technologies d'assistance que le contenu derrière le dialog est inerte.
|
aria-labelledby |
À poser manuellement sur le <dialog>. Doit
pointer vers l'id du titre dans .dialog-header. Permet aux lecteurs d'écran d'annoncer le libellé du
dialog.
|
aria-label="Close dialog" |
À poser manuellement sur le bouton de fermeture. Fournit un libellé explicite aux technologies d'assistance. |
aria-hidden="true" |
À poser manuellement sur .close-indicator. Masque l'icône
décorative aux lecteurs d'écran.
|
Élément <dialog> |
Utilisez toujours l'élément HTML natif <dialog>.
Il gère nativement le piège du focus, la touche Échap et l'inertie du contenu derrière le modal.
|
Élément <button> |
Utilisez toujours un <button> pour les
déclencheurs et les boutons de fermeture afin d'assurer la navigation clavier native.
|
Ne pas fermer le dialog via dialog.close() directement
sans passer par
Dialog.hide() : la transition CSS de fermeture serait
ignorée et
l'événement dialog:afterClose ne serait pas émis.
Toujours fournir un id unique à chaque dialog et le
référencer
dans aria-labelledby (titre) et data-target (bouton déclencheur).
Pour écouter les événements du cycle de vie, branchez-vous sur le dialog lui-même — les événements
remontent (bubbles: true) et peuvent aussi être écoutés
sur document.
Ne pas instancier Dialog avant le chargement du DOM.
Utilisez DOMContentLoaded ou placez le script en bas de
page.
Ne pas utiliser dialog.showModal() directement — le JS
gère
la séquence complète (animation, événements, guard data-animating).
Passez toujours par Dialog.open().
L'élément <dialog> est supporté nativement par tous
les navigateurs modernes.
Pour IE11 ou les navigateurs anciens, un polyfill comme dialog-polyfill
est nécessaire.
Dialog · WEBDEV-UI DOCS / V1