z-index et contextes d'empilement CSS, décortiqués en 3D
Guide du z-index et des contextes d'empilement CSS
z-index CSS qui ne marche pas ? Contextes d'empilement, vue 3D | We.Inc
Pourquoi z-index est ignoré, toutes les propriétés qui créent un contexte d'empilement, une vue 3D éclatée des couches, les en-têtes sticky et menus déroulants, une échelle de z-index et une liste de vérifications pour le débogage.
z-index ordonne les éléments qui se chevauchent, mais uniquement au sein d'un même contexte d'empilement. Un enfant avec z-index: 9999 ne peut pas passer au-dessus d'un élément situé hors de son parent si ce parent forme un contexte avec un z-index plus bas. Les contextes naissent de position combinée à z-index, d'une opacity inférieure à 1, de transform, filter, isolation: isolate et de plusieurs autres propriétés.
Pourquoi z-index semble ignoré
Symptôme
Cause probable
z-index n'a aucun effet
L'élément est en position: static et n'est pas enfant d'un flex ou d'un grid
Un z-index énorme reste derrière
Un ancêtre forme un contexte d'empilement sur une couche plus basse
Menu déroulant caché sous la section suivante
Le parent a un transform, une opacity ou un filter
Menu coupé au bord
overflow: hidden sur un ancêtre, rien à voir avec z-index
La vue 3D ci-dessous sépare une petite page en couches d'empilement pour que vous voyiez quelles boîtes sont piégées dans quel contexte. Modifiez les valeurs de z-index et activez sur le parent la propriété qui crée un contexte. Ensuite : chaque déclencheur selon MDN, les en-têtes sticky avec menus déroulants, une échelle de tokens et une méthode de débogage pas à pas.
Définissez les valeurs de z-index de l'en-tête, de la carte et du menu déroulant dans la carte.
Activez un déclencheur de contexte d'empilement sur la carte et regardez le menu déroulant se retrouver piégé.
Activez la vue 3D pour voir les couches séparées et regroupées par contexte.
Utilisez isolation: isolate ou sortez le menu déroulant du contexte pour corriger le problème.
Avant d'augmenter un nombre, trouvez le contexte d'empilement. Augmenter z-index à l'intérieur d'un contexte piégé n'aide jamais.
Gardez les valeurs de z-index dans des tokens nommés espacés entre eux, pour insérer de nouvelles couches sans tout renuméroter.
Utilisez isolation: isolate sur les composants pour contenir leurs valeurs de z-index internes, afin qu'elles n'entrent pas en conflit avec le reste de la page.
Pour les modales et les popovers, l'élément natif dialog et l'attribut popover utilisent la top layer, qui passe au-dessus de tout z-index.
Questions fréquentes
Pourquoi z-index ne fonctionne-t-il pas ?
Soit l'élément n'est pas positionné (z-index exige une position autre que static, ou un parent flex ou grid), soit il est piégé dans un contexte d'empilement. C'est la position du parent dans son propre contexte qui décide du résultat, et les enfants ne peuvent pas s'en échapper, quel que soit le nombre qu'ils portent.
Qu'est-ce qui crée un contexte d'empilement ?
L'élément racine, les éléments positionnés avec un z-index, les éléments fixed et sticky, les enfants flex et grid avec un z-index, une opacity inférieure à 1, les transformations, les filtres, clip-path, les masques, mix-blend-mode, perspective, isolation: isolate, certaines valeurs de contain et de will-change, les conteneurs de taille et les éléments de la top layer.
À quoi sert isolation: isolate ?
Cela crée un nouveau contexte d'empilement et rien d'autre, sans aucun changement visuel. Placez-le sur la racine d'un composant pour que les enfants à z-index négatif restent derrière l'arrière-plan du composant au lieu de passer derrière la page, et pour que ses couches internes ne se mélangent jamais avec celles de l'extérieur.
Comment garder un menu déroulant au-dessus de la section suivante ?
Donnez à l'en-tête ou au conteneur qui contient le menu un z-index plus élevé que les sections qui le suivent, tous étant positionnés. Si le conteneur a un transform ou une opacity, rappelez-vous qu'il forme son propre contexte, donc le z-index doit aller sur ce conteneur, pas sur le menu déroulant.
Un z-index négatif est-il sûr ?
Il est valide, mais l'élément passe derrière l'arrière-plan de son contexte d'empilement. Sans contexte sur le parent, cela peut signifier derrière l'arrière-plan de la page, donc invisible. Ajoutez isolation: isolate sur le parent pour le garder à l'intérieur.
Comment fonctionne l'ordre de peinture
Dans un contexte d'empilement, le navigateur peint dans un ordre fixe : l'arrière-plan du contexte lui-même, puis les enfants à z-index négatif, puis le contenu de bloc normal, les flottants, le contenu inline, les éléments positionnés avec z-index auto ou 0 dans l'ordre du code source, et enfin les z-index positifs du plus bas au plus haut.
Un contexte enfant est peint comme un seul bloc, à la place de son parent dans cette liste. Tout ce qu'il contient n'est trié que par rapport à ses frères. C'est toute la raison pour laquelle un 9999 dans une carte en z-index: 1 perd face à un voisin en z-index: 2.
Une méthode de débogage
Vérifiez d'abord qu'il s'agit bien d'un problème d'empilement : si l'élément est coupé net le long d'un bord droit, cherchez plutôt un overflow sur un ancêtre. Remontez ensuite depuis l'élément caché dans les DevTools, en vérifiant dans les styles calculés de chaque ancêtre les déclencheurs listés sur cette page.
Les DevTools de Chromium ont un panneau Layers et Edge propose une vue 3D qui affichent visuellement les couches composées. Une fois trouvé l'ancêtre qui forme le contexte, vous pouvez soit élever cet ancêtre, soit retirer le déclencheur, soit afficher l'élément flottant en dehors.
Tout ce qui crée un contexte d'empilement
D'après la référence MDN sur les contextes d'empilement. Une seule condition suffit.
Déclencheurs de contexte d'empilement
Propriété ou état
Condition
Élément racine
html est toujours le contexte d'empilement racine
position: absolute ou relative
Uniquement avec un z-index autre que auto
position: fixed ou sticky
Toujours, z-index pas nécessaire
Enfant flex ou grid
Avec un z-index autre que auto, même si position est static
container-type: size ou inline-size
Tout élément qui est un conteneur de requête de taille
opacity inférieure à 1
Y compris opacity: 0.99
Toute valeur autre que normal
transform, scale, rotate, translate
Toute valeur autre que none
filter ou backdrop-filter
Toute valeur autre que none
Toute valeur autre que none
Toute valeur autre que none
mask, mask-image, mask-border
Toute valeur autre que none
isolation: isolate
Conçu exactement pour cela, sans effet visuel secondaire
Nomme une propriété qui créerait un contexte d'empilement si elle était définie
layout, paint, ou un raccourci qui les inclut (strict, content)
Top layer
Boîte de dialogue modale ouverte, popover, élément en plein écran, et leur ::backdrop
Le piège et les solutions
Un z-index 9999 qui perd quand même
La carte bleue est un contexte en z-index 1. Son infobulle a 9999 mais n'est comparée qu'aux éléments de la carte, donc la boîte orange en z-index 2 la recouvre.
isolation: isolate garde une couche négative à l'intérieur
Sans isolate, la décoration en z-index: -1 passerait derrière l'arrière-plan de la section et disparaîtrait. Avec, la section forme son propre contexte.
En-tête sticky avec menu déroulant
L'en-tête est sticky, donc il forme déjà un contexte. Donnez-lui un z-index supérieur au contenu et le menu déroulant à l'intérieur hérite de cette couche. Pas besoin de nombres énormes.
Contenu normal
Cartes soulevées au survol, en-têtes de tableau sticky
Menus, listes d'autocomplétion
En-tête du site sticky
Fond d'un tiroir latéral
Modales personnalisées (l'élément natif dialog utilise plutôt la top layer)
Notifications qui doivent passer au-dessus de tout
Labo d'empilement avec vue 3D éclatée
z-index de l'en-tête
z-index de la carte (ou auto)
z-index du menu déroulant
z-index de la bannière
Déclencheur de contexte sur la carte
Propriété supplémentaire sur la carte
Vue 3D éclatée (faire pivoter les couches pour les séparer)
Couches séparées : plus c'est haut, plus c'est peint tard
Vue à plat, CSS réel
En-tête z {z}
Carte z {z}
Menu déroulant z {z}
Bannière z {z}
(dans la carte)
au-dessus de la bannière
sous la bannière
La carte forme un contexte d'empilement ({why}). Le {d} du menu déroulant ne compte qu'à l'intérieur de la carte, donc il est {pos} parce que la carte l'est.
La carte n'est pas un contexte d'empilement, donc le menu déroulant est en concurrence directe : {d} contre {b} pour la bannière. Il est {pos}.
CSS de cette configuration
Copier le CSS
Essayez : carte z auto, menu déroulant 9999, puis choisissez transform. Une seule propriété piège le menu déroulant.