Créer une fenêtre modale avec CSS 3

Didier Mouronval

Comme beaucoup de fonctionnalités liées à des actions de l'utilisateur, les fenêtres modales étaient jusqu'à présent l'apanage de JavaScript.
Avec CSS 3, de nombreuses fonctionnalités sont désormais accessibles uniquement grâce aux feuilles de style.

Vous pouvez voir un exemple en ligne ou télécharger l'archive.

Commentez

- Titre : Créer une fenêtre modale avec CSS 3

- Auteur : Didier Mouronval

- Parution : 14 novembre 2012

- Mise à jour : 11 janvier 2013

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2012 Didier Mouronval. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Qu'est-ce qu'une fenêtre modale ?

- Comment créer une fenêtre modale ?

- Qu'est-ce qu'une pseudoclasse ?

- La pseudoclasse :target

- Exemple d'utilisation

- Création de la fenêtre modale

- Création du masque

- Gérer le contenu de la fenêtre

- Autre méthode

- La pseudoclasse :checked

- Gérer le contenu de la fenêtre

- Conclusion et remerciements

Qu'est-ce qu'une fenêtre modale ?

Cet article met en œuvre des propriétés CSS 3. Les exemples contenus dans l'article ne sont donc fonctionnels que sur des navigateurs récents implémentant les propriétés utilisées.
En particulier, les exemples ne sont pas compatibles avec Internet Explorer 8 et inférieurs.

Pendant longtemps, les sites Web avertissaient les utilisateurs d'informations jugées importantes à l'aide d'alertes JavaScript ou de pop-ups.
Malheureusement, l'utilisation de ces « fenêtres surgissantes » a rapidement été associée à de mauvaises pratiques. Souvent à raison du reste : souvenez-vous de ces vieux sites où votre arrivée entraînait une multitude d'ouvertures de fenêtres non désirées.
Ces pratiques se font de plus en plus rares, car les utilisateurs et les navigateurs savent en empêcher l'utilisation.
Malgré tout, le principe de base reste potentiellement valable : bloquer l'utilisateur tant qu'il n'a pas validé une information importante, pris connaissance d'une donnée, validé un accord, fermé une image dont il a demandé l'agrandissement ou autres.
Une fenêtre modale est donc une information affichée en premier plan de la page et empêchant toute interaction avec le reste de celle-ci tant qu'une des actions prédéfinies n'a pas été accomplie.

Comment créer une fenêtre modale ?

Les fenêtres de type alert() en JavaScript où les pop-ups ne sont plus que rarement utilisées, car elles peuvent être facilement bloquées par l'utilisateur et surtout (lorsqu'elles sont justifiées) parce qu'on ne peut pas leur affecter de style.
La technique souvent utilisée pour les remplacer est donc de couvrir la page d'un masque opaque (en semi-transparence) et d'ajouter par-dessus un élément HTML qui devient alors le seul avec lequel l'utilisateur peut interagir du fait du masque.

Jusqu'à présent, le moyen le plus simple (le seul en fait) pour mettre cela en place était l'utilisation de JavaScript, seul composant de la page Web pouvant réagir à un événement.

Mais grâce à CSS 3, il existe désormais un autre moyen de procéder, grâce à la pseudoclasse :target.

Qu'est-ce qu'une pseudoclasse ?

CSS permet de cibler des éléments particuliers d'un document à l'aide de sélecteurs. Parmi les plus connus, element permet de cibler les balises <element>, .classe permet de cibler les balises ayant la classe donnée, de même pour #id avec un identifiant.
Bien sûr, de nombreux types de sélecteurs permettent d'affiner au mieux le ciblage.

Mais CSS permet aussi de cibler des parties d'un élément en fonction de leur nature. Ce sont les pseudoéléments. C'est-à-dire qu'ils n'ont pas besoin d'être entourés de balise pour pouvoir être récupérés. Par exemple, :first-letter ou :first-line permettent de donner un style à la première lettre ou la première ligne d'un élément sans pour autant avoir besoin d'utiliser une balise pour les isoler.
De même, il est possible de cibler un élément par rapport à une interaction, il s'agit alors de pseudoclasse. Un peu comme si l'on affectait une classe spécifique dans un contexte particulier.
Parmi les pseudoclasses connues, celles concernant les liens (:link, :visited, :hover, :focus, :active) sont couramment employées et se comportent comme si le lien possédait une classe différente liée à chacun de ces états.
Avec CSS 3, il existe de nombreuses pseudoclasses. Celle qui nous intéresse dans le cadre d'une fenêtre modale étant :target.

La pseudoclasse :target

On peut rencontrer différents types de liens dans une page Web. Bien entendu, les liens hypertextes permettent de naviguer d'une page à une autre. D'autres liens peuvent servir à envoyer un e-mail. Enfin, certains servent à naviguer dans la même page, ce sont les ancres.
Les liens de type ancres se reconnaissent au caractère '#'. Ce qui suit ce caractère est un indicateur de l'endroit où se positionner dans la page : l'ancre (qui correspond à l'attribut id de l'élément recherché).
C'est précisément l'élément correspondant à cette ancre qui est ciblé par la pseudoclasse :target. Ainsi, lorsque l'on clique sur une ancre, il est possible de donner un style particulier à l'élément ciblé.

Exemple d'utilisation

Le cadre gris ci-dessous possède l'identifiant monAncreCible. Le premier des liens pointe vers cette ancre alors que le second lien pointe vers le premier.
Voici le code :

<style>
#monAncreCible{
width: 80%;
height: 50px;
background-color: silver;
}
#monAncreCible:target{
background-color: gold;
}
</style>
<div id="monAncreCible"></div>
<p>
<a href="#monAncreCible">Changer la couleur de la boîte</a><br />
<a href="#noWhere">Rétablir la couleur</a>
</p>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Changer la couleur de la boîte
Rétablir la couleur

Comme vous pouvez le tester, lorsque vous cliquez sur le premier lien, la boîte change de couleur : l'élément ciblé par le lien prend bien le style déclaré avec :target. En revanche, lorsque vous cliquez sur le second lien, l'élément visé change, la boîte n'est plus affectée par la pseudoclasse et reprend son aspect d'origine.

Pour éviter que la page ne se repositionne au niveau de l'ancre lorsque l'on veut retirer l'effet de :target, on fait pointer le lien vers une ancre inexistante, comme dans l'exemple.

Création de la fenêtre modale

Nous savons comment utiliser la pseudoclasse :target pour interagir au clic sur un lien avec un autre élément.
Nous allons donc pouvoir utiliser cette technique pour créer une fenêtre modale. La première étape étant d'afficher le masque opaque puis d'y ajouter le message à afficher.

Création du masque

Le masque opaque ne doit être affiché que lorsque nous cliquons sur le lien souhaité. Il sera donc invisible par défaut.
De plus, nous souhaitons qu'il empêche toute interaction avec le reste de la page, il faut donc qu'il couvre totalement celle-ci.
La meilleure solution pour cela est de lui donner un display: none; et une position: fixed.
Il ne reste plus ensuite qu'à définir ses dimensions, sa couleur de fond, son opacité et si besoin son z-index.
Voici le code correspondant.

#overlay{
display: none;
position: fixed;
top:0; right:0; bottom:0; left:0;
background-color: black;
opacity: 0.5;
z-index: 1000;
}
#overlay:target{
display: block;
}
#overlay a{
font-weight: bold;
padding: 10px 25px;
background-color: white;
position: absolute;
top: 50%;
left: 50px;
}

<div id="overlay"><a href="#noWhere">Supprimer le masque</a></div>
<p><a href="#overlay">Afficher le masque</a></p>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Afficher le masque

Notez aussi le lien ajouté par-dessus le masque : c'est le seul moyen de le faire disparaître ! Le lien est positionné sous la table des matières afin de pouvoir être visible.

Cette première étape est réussie : on arrive à afficher et masquer le calque opaque grâce aux liens et à leurs ancres. Néanmoins, on commence à comprendre les problèmes auxquels on va être confrontés concernant le contenu.

Gérer le contenu de la fenêtre

On se rend compte avec l'exemple précédent que l'on va être confronté à un problème conceptuel.
En effet, étant donné que l'apparition de la fenêtre modale se fait lors du clic sur un lien, le message à afficher devra être à l'intérieur du balisage du masque, car un lien ne peut avoir qu'une seule cible. Or l'opacité d'un élément est transmise à tous ses enfants. Ce qui fait que le message sera lui aussi opaque, ce qui n'est pas acceptable en termes d'ergonomie.

En JavaScript, la solution est simple : on crée deux éléments distincts, le masque et la fenêtre. On positionne le masque par-dessus la page puis la fenêtre par-dessus le masque, ainsi, la fenêtre contenant le message n'étant pas enfant du masque, elle n'en hérite pas la transparence.

Nous avons vu que cette façon de procéder n'est pas réalisable en CSS car il est impossible de faire afficher deux éléments distincts avec :target.

Là encore, CSS 3 va nous permettre de contourner le problème.
Si l'opacité est transmise aux balises enfants, nous allons donc la gérer au niveau de la couleur de fond en utilisant le mode rgba.

Le mode rgba est une façon de définir une couleur apparue avec CSS 3. Elle reprend ce qui existait avec rgb (red, green, blue) en ajoutant un paramètre alpha correspondant à la transparence.
Pour plus de détails, vous pouvez consulter l'article de Jérôme Debray : Comprendre les couleurs en CSS3.

L'exemple précédent devient alors :

#overlay2{
display: none;
position: fixed;
top:0; right:0; bottom:0; left:0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
#overlay2:target{
display: block;
}
#overlay2 a{
font-weight: bold;
padding: 10px 25px;
background-color: white;
position: absolute;
top: 25%;
left: 50px;
}

<div id="overlay2"><a href="#noWhere">Supprimer le masque</a></div>
<p><a href="#overlay2">Afficher le masque</a></p>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Afficher le masque

On constate que maintenant, le masque est toujours opaque, mais que son contenu est correctement affiché.

Il ne reste donc plus qu'à donner un peu de style à la fenêtre contenant le message.
Le style de la fenêtre est repris de l'article Créez une fenêtre modale avec CSS et jQuery.

#overlay3{
display: none;
position: fixed;
top:0; right:0; bottom:0; left:0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
#overlay3:target{
display: block;
}
.popup_block{
background: #fff;
padding: 20px;
border: 20px solid #ddd;
position: relative;
margin: 10% auto;
width: 40%;
box-shadow: 0px 0px 20px #000;
border-radius: 10px;
}
img.btn_close {
float: right;
margin: -55px -55px 0 0;
}

<div id="overlay3">
<div class="popup_block">
<a class="close" href="#noWhere"><img alt="Fermer" title="Fermer la fenêtre" class="btn_close" src="./images/close_pop.png"></a>
<img style="float: right; margin: 0 0 0 20px;" alt="Lil bomb dude" src="./images/bomber.gif">
<h2>Popup</h2>

<p>Aliquip transverbero loquor esse ille vulputate exerci veniam fatua eros similis illum valde. Praesent, venio conventio rusticus antehabeo lenis. Melior pertineo feugait, praesent hos rusticus et haero facilisis abluo. </p>
<p>Veniam tincidunt augue abluo vero, augue nisl melior quidem secundum nobis singularis eum eum.</p>

</div>
</div>
<p><a href="#overlay3">Afficher le masque</a></p>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Afficher la fenêtre modale

Autre méthode

L'utilisation de la pseudoclasse :target me semble la plus simple à mettre en place et la plus élégante. Cependant, elle présente l'inconvénient de modifier l'URL de la page, ce qui n'est pas nécessairement souhaité.
Une autre technique, un peu plus complexe et contraignante, permet de pallier ce problème.

Lorsque l'on dit que la modification de l'URL n'est pas nécessairement souhaitée, il ne s'agit pas de considération esthétique concernant la barre d'adresse du navigateur.
Dans certaines applications Web, la partie hash de l'URL (celle qui se trouve après le signe #) est utilisée pour conserver l'état de l'affichage (ce que l'on appelle le hashtag ou hashbang). Dans ce cas, la modification de l'URL devient réellement problématique.

La pseudoclasse :checked

La pseudoclasse :checked permet de cibler des boutons radio ou des cases à cocher en fonction de leur état.
Son utilisation peut être visualisée dans l'exemple suivant :

.checkbox:checked{
box-shadow: 0 0 10px 2px gold;
}

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Vous pouvez constater qu'en cochant ou décochant la case, son contour est modifié.
En tant que telle, cette pseudoclasse ne nous sera pas particulièrement utile puisqu'elle ne permet pas de cibler d'autres éléments que les balises <input /> correspondantes. Il va donc falloir la coupler avec un autre sélecteur pour pouvoir s'en servir efficacement pour notre fenêtre modale.

En règle générale, CSS permet de cibler un élément par rapport à ses parents successifs : la recherche se fait en avançant progressivement dans l'arborescence HTML. Ce qui ne nous est pas très utile puisqu'une balise <input /> ne peut pas avoir d'enfant.
Heureusement, il existe malgré tout certains sélecteurs permettant la recherche sur le même niveau. Dans cet exemple, nous utiliserons le sélecteur de frère adjacent E + F.

Ce sélecteur permet de cibler tout élément F frère (c'est-à-dire au même niveau de l'arborescence, ou ayant le même parent) de l'élément E.

Cette particularité va avoir pour conséquence d'imposer une structure HTML figée, alors que l'exemple précédent est plus souple sur ce point.

À partir de ces informations, nous allons pouvoir élaborer une ébauche de structure pour notre fenêtre modale.

#overlay4{
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
#modalCheck4{
position: relative;
z-index: 1001;
}
#modalCheck4:checked + #overlay4{
display: block;
}

<div>
<label for="modalCheck4">Cliquer pour afficher le masque : </label><input type="checkbox" id="modalCheck4" />
<div id="overlay4">
</div>
</div>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Dans cet exemple, nous avons rajouté un positionnement et un z-index à la case à cocher afin de la mettre au premier plan et de permettre de la décocher.

Gérer le contenu de la fenêtre

Pour que l'exemple précédent soit opérationnel, nous sommes obligés de positionner la case à cocher en premier plan pour permettre de la décocher. Or dans l'absolu, c'est plutôt le contraire que l'on souhaiterait faire : que la case ne soit jamais visible !
Nous allons contourner cette obligation en utilisant la balise <label>, qui permet d'associer son contenu à la balise <input /> et de cocher cette dernière lorsque l'on clique dessus, ce qui va nous permettre de masquer la case à cocher.
D'autre part, plusieurs balises <label> peuvent être associées au même élément.

Nous adaptons donc notre exemple pour prendre en compte ces informations :

#overlay5{
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
#modalCheck5{
display: none;
}
#modalCheck5:checked + #overlay5{
display: block;
}
label{
cursor: pointer;
}
.labelButton{
border: 1px solid black;
padding: 5px 10px;
background-color: white;
position: absolute;
top: 25%;
left: 50px;
}

<div>
<label class="lienArticle simple" for="modalCheck5">Cliquer pour afficher le masque</label><input type="checkbox" id="modalCheck5" />
<div id="overlay5">
<label class="labelButton" for="modalCheck5">Cliquer pour supprimer le masque</label>
</div>
</div>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Le résultat correspond à nos attentes. Il ne reste plus qu'à ajouter du contenu à la fenêtre modale avec le même code que dans la partie précédente.

#overlay6{
display: none;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
#modalCheck6{
display: none;
}
#modalCheck6:checked + #overlay6{
display: block;
}

<div>
<label for="modalCheck6">Voir la fenêtre modale</label><input type="checkbox" id="modalCheck6" />
<div id="overlay6">
<div class="popup_block">
<label for="modalCheck6"><img alt="Fermer" title="Fermer la fenêtre" class="my_btn_close" src="./images/close_pop.png" /></label>
<img style="float: right; margin: 0 0 0 20px;" alt="Lil bomb dude" src="./images/bomber.gif" />
<h2>Popup</h2>

<p>Aliquip transverbero loquor esse ille vulputate exerci veniam fatua eros similis illum valde. Praesent, venio conventio rusticus antehabeo lenis. Melior pertineo feugait, praesent hos rusticus et haero facilisis abluo. </p>
<p>Veniam tincidunt augue abluo vero, augue nisl melior quidem secundum nobis singularis eum eum.</p>

</div>
</div>
</div>

Le contenu dynamique ne peut pas être affiché dans ce support, veuillez consulter la page en ligne.

Attention, cette technique possède les mêmes contraintes que la précédente en termes de compatibilité. Elle ne fonctionnera pas avec les versions d'Internet Explorer inférieures à 9.

Conclusion et remerciements

Vous pouvez voir un exemple en ligne ou télécharger l'archive.

Bien entendu, il ne s'agit dans cet article que de montrer la façon de procéder. Il est possible d'ajuster le code pour qu'il corresponde mieux à vos besoins.
D'autre part, CSS 3 permet beaucoup d'autres améliorations, notamment faire apparaître et disparaître la fenêtre avec des animations ou des transformations.
Enfin, gardez à l'esprit que malgré la prise en charge croissante de CSS 3 par les navigateurs, les codes présentés dans cet article ne sont compatibles pour Internet Explorer qu'à partir de la version 9.

Nous tenons à remercier zoom61, Torgar, ClaudeLELOUP et _Max_ pour leur relecture attentive de cet article.