Skip to main content

Chapitre 25 : Le positionnement fixe et sticky

Ancrage par rapport à la fenêtre ou au conteneur de défilement

Le positionnement en CSS permet de sortir un élément de son flux naturel pour répondre à des besoins d'interface utilisateur complexes. Si le positionnement relative et absolute gère les relations entre parents et enfants, le positionnement fixed et sticky gère la relation entre l'élément et le viewport (la fenêtre d'affichage) ou le conteneur de défilement.

Le Positionnement Fixe

1. Quoi

Le positionnement position: fixed retire complètement l'élément du flux normal du document. L'élément est positionné relativement à la fenêtre d'affichage (viewport).

Cela signifie que même si l'utilisateur fait défiler la page, l'élément reste immobile à la position exacte définie par les propriétés top, right, bottom et left. Contrairement au positionnement absolu, il ne cherche pas le premier parent positionné, mais s'ancre directement aux bords de l'écran.

2. Pourquoi

Le positionnement fixe est indispensable pour créer des composants d'interface qui doivent rester accessibles en permanence, indépendamment de la position de lecture de l'utilisateur.

Les cas d'usage professionnels les plus fréquents sont :

  • Barres de navigation (Navbar) : Pour permettre un accès rapide aux menus.
  • Boutons d'action flottants (FAB) : Très courants sur mobile pour ancre un bouton "Ajouter" ou "Contact".
  • Modales et Overlays : Pour bloquer l'écran et centrer un contenu au milieu du viewport.
  • Bannières de cookies ou d'alertes : Pour forcer la visibilité d'une information légale ou critique.

3. Comment

A. Syntaxe de base

.element-fixe {
position: fixed;
top: 0;
left: 0;
width: 100%;
}

B. Cas concret : Une barre de navigation moderne

Dans un projet réel, un élément fixe peut masquer le contenu qui se trouve juste après lui car il ne prend plus de place dans le flux. Il faut donc prévoir une marge ou un padding sur le corps du document.

/* Le header reste collé en haut */
.main-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
background-color: #ffffff;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
z-index: 1000; /* Assure que le header passe au-dessus du contenu */
display: flex;
align-items: center;
padding: 0 20px;
}

/* On ajoute un padding au body pour éviter que le contenu
ne commence sous le header */
body {
padding-top: 60px;
font-family: sans-serif;
}

.content {
padding: 20px;
line-height: 1.6;
}

C. Limitations

  1. Sortie du flux : L'élément ne prend plus de place. Si vous placez un élément fixed sans compenser l'espace dans le parent, le contenu suivant remontera et sera caché derrière.
  2. Z-index : Comme l'élément est sorti du flux, il peut se retrouver derrière d'autres éléments positionnés. L'usage de z-index devient alors obligatoire.
  3. Performance : Sur certains navigateurs mobiles, un abus d'éléments fixed peut provoquer des saccades lors du scroll (problèmes de "repaint").

4. Zone de Danger

Erreur commune : Utiliser position: fixed pour centrer un élément sans définir les coordonnées. Résultat : L'élément reste en haut à gauche par défaut.

Bonne pratique : Pour centrer parfaitement un élément fixe (comme une modale), combinez top: 50%, left: 50% et transform: translate(-50%, -50%).

.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 2rem;
border-radius: 8px;
}

Le Positionnement Sticky

1. Quoi

Le positionnement position: sticky est un hybride entre le positionnement relative et fixed.

L'élément se comporte comme un élément relatif jusqu'à ce qu'un seuil défini (via top, bottom, etc.) soit atteint lors du défilement. À ce moment précis, il "colle" à l'écran et se comporte comme un élément fixe, mais uniquement à l'intérieur des limites de son parent.

2. Pourquoi

Le sticky est extrêmement puissant pour améliorer l'expérience utilisateur (UX) en gardant des éléments de contexte visibles pendant que l'utilisateur explore une section longue.

Exemples types :

  • En-têtes de tableaux : Garder les titres des colonnes visibles lors du scroll d'un grand tableau.
  • Sommaires latéraux : Un menu de navigation qui suit l'utilisateur tant qu'il est dans l'article.
  • Barres de tri/filtres : Garder les options de filtrage accessibles en haut d'une liste de produits.

3. Comment

A. Syntaxe de base

.element-sticky {
position: sticky;
top: 0; /* L'élément collera au bord supérieur du viewport */
}

B. Cas concret : Sommaire d'article

Imaginons un article avec un texte long à gauche et un sommaire à droite.

.article-container {
display: grid;
grid-template-columns: 3fr 1fr;
gap: 2rem;
align-items: start; /* Crucial : évite que le sticky s'étire sur toute la hauteur */
}

.main-text {
font-size: 1.1rem;
}

.sidebar-toc {
position: sticky;
top: 20px; /* Laisse un petit espace avec le haut de l'écran */
background: #f9f9f9;
padding: 1rem;
border: 1px solid #ddd;
}

C. Limitations (Les pièges classiques)

C'est ici que le sticky devient frustrant pour les débutants. Pour qu'un élément soit sticky, deux conditions strictes doivent être remplies :

  1. Définition d'un seuil : Vous devez spécifier au moins une propriété d'ancrage (top, bottom, left ou right). Sans cela, l'élément reste relative.
  2. L'absence d'overflow restrictif : Si l'un des parents de l'élément sticky possède overflow: hidden, overflow: auto ou overflow: scroll, le comportement sticky sera désactivé. Le parent "capture" le défilement, et l'élément ne peut plus s'ancrer au viewport.
  3. La hauteur du parent : Un élément sticky ne peut pas "coller" s'il n'a plus de place pour bouger dans son parent. Si le parent a la même hauteur que l'élément sticky, celui-ci ne bougera jamais.

4. Zone de Danger

Erreur commune : Mettre position: sticky sur un élément dont le parent a overflow: hidden. Résultat : L'élément se comporte comme un élément relative classique et disparaît au scroll.

Bonne pratique : Vérifiez toujours la chaîne des parents. Si le sticky ne fonctionne pas, inspectez vos parents pour trouver un overflow parasite.

Logique de choix Fixed vs Sticky


Questions clés

1. Quelle est la différence fondamentale entre fixed et sticky concernant le flux du document ?

Découvrir la réponse

L'élément fixed est totalement retiré du flux (il ne prend plus de place), tandis que l'élément sticky reste dans le flux et conserve son espace initial. Il ne devient "fixe" que lorsqu'il atteint le seuil de défilement défini.

2. Pourquoi mon position: sticky ne fonctionne-t-il pas alors que j'ai bien mis top: 0 ?

Découvrir la réponse

La cause la plus probable est la présence d'un overflow: hidden, auto ou scroll sur l'un des éléments parents. Le sticky nécessite que tous les parents aient un overflow visible (ou non défini) pour pouvoir calculer la position par rapport au viewport.

3. Comment éviter que le contenu de ma page ne soit caché par un header position: fixed ?

Découvrir la réponse

Il faut ajouter une marge supérieure (margin-top) ou un remplissage supérieur (padding-top) au corps du document (body) ou au premier élément de contenu, dont la valeur doit être égale ou supérieure à la hauteur du header.

4. Un élément sticky peut-il sortir de son conteneur parent ?

Découvrir la réponse

Non. L'élément sticky est limité par les frontières de son parent. Une fois que le bas du parent atteint le bas de l'élément sticky, celui-ci "remonte" avec son parent et disparaît de l'écran.


Mise en pratique

Exercice 1 : Reproduction guidée (Niveau Facile)

Objectif : Créer un bouton "Retour en haut" qui reste toujours visible en bas à droite de l'écran.

Quel positionnement choisir ?

Utilisez position: fixed : le bouton doit rester ancré au viewport (la fenêtre du navigateur) à une position constante, quelle que soit la position de défilement. position: sticky ne conviendrait pas ici car il suit le flux du document et ne "colle" que lors du scroll d'un conteneur parent.

  • Le bouton doit être un cercle de 50px.
  • Il doit être positionné à 20px du bord droit et 20px du bord inférieur.
  • Il doit avoir un z-index élevé pour être toujours au-dessus.
Découvrir la solution commentée
.back-to-top {
position: fixed; /* Ancrage au viewport */
bottom: 20px; /* Distance du bas */
right: 20px; /* Distance de la droite */
width: 50px;
height: 50px;
border-radius: 50%; /* Forme circulaire */
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
z-index: 9999; /* Toujours au premier plan */
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}

Exercice 2 : Adaptation (Niveau Intermédiaire)

Objectif : Créer une liste de catégories où chaque titre de catégorie reste collé en haut de l'écran pendant que l'on parcourt les éléments de cette catégorie.

  • Structure : Un conteneur avec plusieurs sections. Chaque section a un h2 (le titre) et plusieurs p (le contenu).
  • Le h2 doit devenir sticky.
  • Ajoutez un fond coloré aux h2 pour bien visualiser l'effet.
Découvrir la solution commentée
.category-section {
margin-bottom: 2rem;
}

.category-title {
position: sticky;
top: 0; /* Colle au sommet du viewport */
background-color: #eee;
padding: 10px;
margin: 0;
border-bottom: 2px solid #ccc;
z-index: 10; /* Évite que le contenu des paragraphes passe par-dessus */
}

.category-content {
padding: 10px 0;
}

Exercice 3 : Conception (Niveau Avancé)

Objectif : Concevoir une mise en page de "Dashboard" avec :

  1. Une barre latérale (Sidebar) fixe à gauche (largeur 250px, hauteur 100%).
  2. Une barre supérieure (TopBar) fixe en haut (hauteur 60px).
  3. Une zone de contenu principale qui défile, mais dont le titre de page reste sticky juste en dessous de la TopBar.

Contrainte : Le contenu principal ne doit pas être caché par la Sidebar ni par la TopBar.

HTML de départ :

<body>
<aside class="sidebar">Sidebar</aside>
<header class="topbar">TopBar</header>
<main class="main-content">
<h1 class="page-title">Titre de page</h1>
<p>Contenu long qui défile...</p>
<!-- Répétez plusieurs paragraphes pour tester le scroll -->
</main>
</body>
Découvrir la solution commentée
body {
margin: 0;
font-family: sans-serif;
}

/* Sidebar fixe à gauche */
.sidebar {
position: fixed;
top: 0;
left: 0;
width: 250px;
height: 100vh;
background: #2c3e50;
color: white;
padding: 1rem;
box-sizing: border-box;
}

/* TopBar fixe en haut, décalée pour ne pas recouvrir la sidebar */
.topbar {
position: fixed;
top: 0;
left: 250px; /* Commence après la sidebar */
right: 0;
height: 60px;
background: #ecf0f1;
z-index: 100;
border-bottom: 1px solid #bdc3c7;
display: flex;
align-items: center;
padding: 0 1rem;
}

/* Zone de contenu principale
⚠️ Ne pas mettre width: 100% ici !
Avec margin-left: 250px, width: 100% calcule 100% du viewport
puis y ajoute 250px de marge → débordement horizontal garanti. */
.main-content {
margin-left: 250px; /* Espace pour la sidebar */
margin-top: 60px; /* Espace pour la topbar */
padding: 20px;
}

/* Titre de page sticky — reste visible lors du scroll du contenu */
.page-title {
position: sticky;
top: 0;
background: white;
padding: 10px 0;
font-size: 2rem;
border-bottom: 1px solid #eee;
z-index: 50;
}