Retour au cours

frontend / css

Positionnement et stacking context

Leçon 91 exercice

Explication

Ce que vous allez apprendre

  • Distinguer relative, absolute, fixed et sticky par leur point de référence
  • Positionner un badge dans le coin d'une carte avec un parent relative et un enfant absolute
  • Comprendre pourquoi un z-index: 9999 peut rester invisible derrière un autre élément
  • Identifier les propriétés qui créent silencieusement un nouveau stacking context
  • Utiliser isolation: isolate pour contenir un z-index sans effet visuel parasite

Dans quel contexte ?

Un développeur ajoute une infobulle (tooltip) avec z-index: 9999 sur fiche-produit.html, mais elle reste cachée derrière la barre de navigation du site, qui n'a pourtant qu'un z-index: 10. En inspectant le DOM, il découvre que le parent de l'infobulle a un transform: translateY(0) pour une animation, ce qui crée un nouveau stacking context : le z-index: 9999 de l'enfant est alors comparé uniquement à l'intérieur de ce contexte, jamais directement à la navbar.

Étape 1 : le flux normal, la règle par défaut

Par défaut, chaque élément HTML occupe sa place dans le "flux normal" de la page, comme des briques empilées les unes après les autres. Le positionnement CSS permet de déroger à cette règle.

Étape 2 : quatre valeurs, quatre besoins différents

Faire flotter un badge dans le coin d'une carte, garder un bouton visible en permanence, ou figer un menu pendant le défilement : chaque valeur de position répond à un besoin précis. Les confondre est une source d'erreurs très fréquente.

Étape 3 : la vraie différence, le point de référence

Ce qui distingue ces valeurs, c'est leur POINT DE RÉFÉRENCE. relative se décale par rapport à sa propre position d'origine, en laissant un vide à sa place initiale.

Étape 4 : et pour les trois autres valeurs

absolute se positionne par rapport au plus proche ancêtre lui-même positionné et sort complètement du flux. fixed se réfère au viewport et reste visible même en scrollant. sticky est un hybride : il se comporte normalement jusqu'à un seuil de défilement, puis devient fixe, mais seulement dans les limites de son parent.

ValeurPoint de référenceReste dans le flux ?
relativeSa propre position d'origineOui, espace réservé
absoluteLe plus proche ancêtre positionnéNon
fixedLe viewportNon
stickySon parent, après un seuil de scrollOui, jusqu'au déclenchement

Étape 5 : le vrai piège de cette leçon

Une fois le positionnement compris, voici ce qui frustre même des développeurs expérimentés : un z-index: 9999 ne garantit RIEN si l'élément est enfermé dans un "contexte d'empilement" créé par un parent.

Étape 6 : comment ce contexte se crée, sans qu'on le veuille

Ce contexte apparaît par exemple avec position: relative combiné à un z-index, mais aussi simplement avec opacity ou transform. Une fois créé, tous les z-index internes sont comparés ENTRE EUX uniquement, sans jamais pouvoir rivaliser avec l'extérieur.

Piège fréquent

Un z-index: 9999 placé sur un élément dont le parent a opacity: 0.99, transform: none changé dynamiquement, ou un simple filter, reste piégé dans le stacking context de ce parent. Avant d'augmenter un z-index, vérifiez toujours si un ancêtre ne crée pas déjà un contexte d'empilement qui limite sa portée.

Et ensuite ?

Comprendre ce mécanisme évite des heures de débogage à augmenter des z-index sans effet. Une fois ce point maîtrisé, la prochaine étape consiste à adapter la mise en page à la taille de l'écran avec les media queries.

Commandes & code

Positionnement et stacking context

Sortir un élément du flux normal, et comprendre pourquoi un z-index: 9999 ne suffit parfois pas.

css
.statique   { position: static; }   /* défaut : suit le flux normal, top/left ignorés */

.relatif {
    position: relative;
    top: 10px;   /* décalé par rapport à SA PROPRE position d'origine,
                    l'espace initial reste réservé dans le flux */
    left: 20px;
}

.absolu {
    position: absolute;
    top: 0;
    right: 0;
    /* positionné par rapport au plus proche ANCÊTRE positionné
       (position != static). Sinon : relatif au <html>. Sort complètement du flux. */
}

.fixe {
    position: fixed;
    bottom: 16px;
    right: 16px;
    /* positionné par rapport au VIEWPORT, reste visible même en scrollant
       (sauf si un ancêtre a `transform`, qui crée un nouveau contexte -- piège classique) */
}

.collant {
    position: sticky;
    top: 0;
    /* hybride : relatif jusqu'à un seuil de scroll, puis se comporte comme fixed
       dans les limites de son PARENT (pas de la fenêtre) */
}
css
/* Pattern classique : élément absolu positionné dans un parent relatif */
.carte {
    position: relative;
}
.badge {
    position: absolute;
    top: 8px;
    right: 8px;
}

/* Pattern : centrer un élément absolument positionné */
.modale {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* transform plutôt que margin négatif : fonctionne même si la taille est inconnue/dynamique */
}
css
/*
Stacking context : un empilement indépendant, où le z-index interne
n'a AUCUN effet sur l'empilement externe une fois le contexte créé.

Créent un nouveau stacking context :
- position != static + z-index != auto
- opacity < 1
- transform, filter, will-change, perspective (différent de none)
- isolation: isolate (créé un contexte SANS aucun autre effet visuel)
*/

.parent-a { position: relative; z-index: 1; }
.enfant-a { position: relative; z-index: 9999; } /* piégé DANS le contexte de .parent-a */

.parent-b { position: relative; z-index: 2; }
/* même avec z-index: 9999, .enfant-a ne passera JAMAIS devant .parent-b :
   il est confiné à l'intérieur du stacking context de .parent-a,
   qui lui n'a qu'un z-index de 1 face à .parent-b */

/* isolation: isolate : crée volontairement un stacking context "propre",
   sans transform/opacity parasite, pour contenir du z-index localement */
.widget-isole {
    isolation: isolate;
}

Résumé

  • absolute se positionne par rapport au plus proche ancêtre non-static ; fixed, par rapport au viewport.
  • sticky a besoin d'un top/bottom explicite et reste contraint à son parent.
  • Un z-index élevé ne sort jamais un élément de son stacking context parent : c'est LA cause n°1 des bugs de superposition.
  • transform/opacity/filter créent silencieusement un nouveau stacking context.

Exercices pratiques

1 disponible
1

Mission : une infobulle piégée derrière la navbar

Objectif : Diagnostiquer un stacking context créé par accident et corriger le positionnement sans casser l'animation existante.

Contexte

Sur fiche-produit.css, la navbar a position: sticky; z-index: 10;. Le conteneur du bouton d'info produit a position: relative; transform: translateY(0); transition: transform 0.2s; (pour une animation d'apparition), et l'infobulle .tooltip à l'intérieur a position: absolute; z-index: 9999;. Malgré ce z-index énorme, l'infobulle reste visuellement cachée derrière la navbar dès qu'elle s'ouvre près du haut de la page.

Résoudre l’exercice →