
Tu as déjà mis un position: absolute sur un élément, et vu ton badge s'envoler à l'autre bout de la page ? Ou ton z-index: 9999 qui ne sert strictement à rien ? Rassure-toi, on est tous passés par là.
Bref, aujourd'hui on fait le tour complet. On va voir toutes les façons de placer un élément en CSS : le flux normal, float, les 5 valeurs de position, Flexbox et Grid. Pour chacune : comment elle marche vraiment, quand l'utiliser, et surtout les pièges qui te font perdre des heures. À la fin, on aura construit trois cas concrets : une carte produit avec un badge, une fenêtre modale, et une page complète avec header, sidebar et grille de cartes.
Le code complet est dispo sur GitHub : lien vers le repo. Tu peux le cloner et jouer avec pendant que tu lis.
Ce qu'il te faut
- Les bases de HTML et CSS
- Un éditeur de code et un navigateur
- C'est tout, pas de framework, pas d'installation
1. Le flux normal : là où tout commence
Avant de déplacer quoi que ce soit, il faut comprendre comment le navigateur place les éléments quand tu ne lui dis rien. Il suit ce qu'on appelle le flux normal : les éléments se posent les uns après les autres, dans l'ordre du HTML. Et la façon dont chacun se pose dépend de sa propriété display.
Les quatre valeurs de base :
block: l'élément prend toute la largeur disponible et passe à la ligne. C'est le cas dediv,p,h1,section.inline: l'élément se met à la suite du texte et ne prend que la place de son contenu.widthetheightsont ignorés. C'est le cas despan,a,strong.inline-block: il se met à la suite comme uninline, mais acceptewidth,heightet les marges verticales. Pratique pour un bouton.none: l'élément disparaît, et il ne prend plus aucune place.
.bouton {
display: inline-block;
width: 120px;
padding: 8px 0;
text-align: center;
}
Centrer dans le flux normal
Pour centrer, tout dépend de ce que tu centres :
- Du texte ou des éléments inline :
text-align: centersur le parent. - Un bloc de largeur limitée :
margin: 0 auto.
.conteneur {
max-width: 720px; /* sans largeur, margin auto ne fait rien */
margin: 0 auto; /* marges gauche et droite égales = centré */
}
C'est le réflexe pour centrer le contenu principal d'une page.
A noter
margin: 0 autone marche que sur un bloc dont la largeur est définie
(widthoumax-width). Sans ça, le bloc prend toute la largeur, donc il n'y a rien à centrer.
2. float : l'ancêtre qu'il faut quand même connaître
float sort un élément du flux horizontal et le colle à gauche ou à droite de son conteneur. Le texte qui suit vient alors l'entourer. À l'origine, c'est fait pour un seul usage : une image avec du texte autour, comme dans un magazine.
<article class="article">
<img class="avatar" src="moi.jpg" alt="Ma photo">
<p>Un long paragraphe qui va entourer l'image…</p>
</article>
.avatar {
float: left;
width: 120px;
margin: 0 16px 8px 0; /* de l'air entre l'image et le texte */
}
Pendant des années, on a détourné float pour construire des pages entières en colonnes. C'était du bricolage, et ça explique pourquoi tu verras encore du float dans de vieux thèmes WordPress. Aujourd'hui, Flexbox et Grid font ce travail bien mieux. Garde float pour son vrai rôle : l'image entourée de texte.
Le piège :
Un élément flottant sort du flux. Résultat : si le parent ne contient que des éléments flottants, sa hauteur tombe à zéro, et il ne les enveloppe plus. La solution moderne tient en une ligne :
.article {
display: flow-root; /* le parent englobe de nouveau ses enfants flottants */
}
Tu croiseras aussi clear: both (force un élément à passer sous les flottants) et le fameux « clearfix » dans du code ancien. Maintenant tu sais d'où ça vient.
3. position : static
C'est la valeur par défaut. Tous tes éléments l'ont déjà, même si tu ne l'écris jamais. L'élément reste dans le flux normal de la page : les blocs s'empilent de haut en bas, le texte s'écoule de gauche à droite.
Le point important : avec static, les propriétés top, right, bottom, left et z-index ne font rien. Si tu les écris sur un élément sans position, le navigateur les ignore.
.boite {
position: static; /* inutile à écrire, c'est déjà le cas */
top: 50px; /* ignoré */
}
4. position: relative
Avec relative, l'élément reste dans le flux (sa place d'origine est conservée), mais tu peux le décaler visuellement avec top, left, etc.
.boite {
position: relative;
top: 20px; /* descend de 20px par rapport à sa place d'origine */
left: 30px; /* se décale de 30px vers la droite */
}
Par contre, ses voisins ne bougent pas : ils font comme si l'élément était toujours à sa place d'origine. Il y a un trou à l'endroit où il était.
Mais en pratique, relative sert rarement à décaler des éléments. Son vrai usage, c'est autre chose : il crée un point de repère pour les éléments en absolute qu'il contient. Ça, on le voit juste après, et c'est LA clé pour tout comprendre.
5. position: absolute
Avec absolute, l'élément sort du flux. Ses voisins l'oublient complètement, comme s'il n'existait plus. Et il se positionne par rapport à son ancêtre positionné le plus proche, c'est-à-dire le premier parent qui a une position différente de static.
Si aucun parent n'est positionné, il se positionne par rapport à la page entière. Voilà d'où vient le fameux badge qui s'envole à l'autre bout de l'écran.
Exemple concret : un badge sur une carte
<article class="card">
<span class="badge">Nouveau</span>
<img src="casque.jpg" alt="Casque audio">
<h2>Casque audio sans fil</h2>
</article>
.card {
position: relative; /* le point de repère du badge */
width: 280px;
border: 1px solid #e5e7eb;
border-radius: 12px;
overflow: hidden;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
background: #ef4444;
color: #fff;
padding: 4px 10px;
border-radius: 999px;
font-size: 0.8rem;
}
Résultat : le badge se colle en haut à droite de la carte, pas de la page. Retire le position: relative de .card et tu verras le badge partir ailleurs. Teste, c'est le meilleur moyen de comprendre.
La règle à retenir :
Le parent enrelative, et l'enfant enabsolute. 90 % des cas d'usage tiennent dans cette phrase.
6. position: fixed
Avec fixed, l'élément se positionne par rapport à la fenêtre du navigateur (le viewport), et il ne bouge pas quand tu scrolles. C'est parfait pour un bouton « retour en haut », une bannière de cookies ou un chat en bas à droite.
.retour-haut {
position: fixed;
bottom: 24px;
right: 24px;
}
Comme absolute, l'élément sort du flux. Pense donc à lui laisser de la place si besoin, sinon il peut cacher du contenu.
7. position: sticky
C'est mon préféré. Un élément sticky se comporte comme un élément normal… jusqu'à ce que tu scrolles assez pour qu'il atteigne une limite que tu as définie. À ce moment-là, il se « colle » et reste visible.
Exemple : un header qui reste en haut
.header {
position: sticky;
top: 0; /* se colle quand il touche le haut de l'écran */
background: #fff;
z-index: 10;
}
Deux pièges classiques avec sticky :
- Oublier
top(oubottom,left,right). Sans seuil, il ne se passe rien. C'est la cause numéro 1 des « mon sticky marche pas ». - Un parent avec
overflow: hidden,autoouscroll. Dans ce cas, le sticky se base sur ce parent et non sur la page. Si ça ne marche pas, remonte l'arbre HTML et cherche unoverflowcoupable.
8. z-index : qui passe devant qui
Quand des éléments se chevauchent, z-index décide de l'ordre d'empilement : plus le nombre est grand, plus l'élément est devant.
.fond { position: absolute; z-index: 1; }
.devant { position: absolute; z-index: 2; }
Bon à savoir : z-index ne marche que sur les éléments positionnés (donc pas static) ainsi que sur les enfants directs d'un conteneur flex ou grid.
Le piège du contexte d'empilement
Voilà pourquoi ton z-index: 9999 ne marche pas parfois. Certains éléments créent un contexte d'empilement (stacking context), une sorte de petite bulle isolée. Un élément position avec un z-index, ou avec une transform, une opacity inférieure à 1, un filter, etc. en crée un.
À l'intérieur d'une bulle, les z-index ne se comparent qu'entre eux. Un enfant avec z-index: 9999 dans une bulle de niveau 1 restera derrière un élément de niveau 2 situé dans une autre bulle.
.parent-a { position: relative; z-index: 1; }
.parent-a .enfant { position: relative; z-index: 9999; } /* reste derrière parent-b */
.parent-b { position: relative; z-index: 2; }
Avant d'augmenter un z-index à l'infini, cherche plutôt quel parent crée la bulle. Souvent, c'est une transform oubliée.
9. Le fameux Flexbox
Flexbox est fait pour organiser des éléments sur une seule dimension : en ligne ou en colonne. Tu actives ça sur le parent avec display: flex, et ses enfants directs deviennent des flex items qu'on peut aligner, espacer et faire grandir.
Le truc à comprendre, ce sont les deux axes :
- L'axe principal : celui dans lequel les éléments s'enchaînent. Par défaut c'est l'horizontale (
flex-direction: row). - L'axe secondaire : celui d'à côté, perpendiculaire au premier.
justify-content agit sur l'axe principal, align-items sur l'axe secondaire. Avec flex-direction: column, les deux axes s'inversent, et c'est là que tout le monde se mélange les pinceaux.
Exemple 1 : une barre de navigation
Le logo à gauche, les liens à droite, le tout aligné verticalement. Un classique.
<header class="nav">
<a class="logo" href="/">MonBlog</a>
<nav class="liens">
<a href="#">Articles</a>
<a href="#">À propos</a>
<a href="#">Contact</a>
</nav>
</header>
.nav {
display: flex;
justify-content: space-between; /* pousse le logo et les liens aux extrémités */
align-items: center; /* centre verticalement */
padding: 12px 24px;
}
.liens {
display: flex;
gap: 16px; /* espace entre les liens, sans marges bidouillées */
}
Exemple 2 : centrer parfaitement
La question que tout le monde se pose un jour : comment centrer un élément horizontalement et verticalement ? Avec Flexbox, c'est trois lignes.
.centre {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
Exemple 3 : des colonnes qui se partagent l'espace
.rangee {
display: flex;
gap: 24px;
}
.colonne {
flex: 1; /* chaque colonne prend une part égale de l'espace */
}
Si tu passes flex: 2 sur une des colonnes, elle prend deux fois plus de place que les autres.
Les propriétés à connaître
| Propriété | Sur quoi | Rôle |
|---|---|---|
flex-direction |
le parent | row (défaut) ou column : choisit l'axe principal |
justify-content |
le parent | répartit les éléments sur l'axe principal |
align-items |
le parent | aligne les éléments sur l'axe secondaire |
gap |
le parent | espace entre les éléments |
flex-wrap |
le parent | autorise le passage à la ligne (wrap) |
flex |
un enfant | part d'espace que l'enfant prend (flex: 1) |
align-self |
un enfant | remplace align-items pour cet enfant seulement |
Un dernier piège : par défaut, Flexbox essaie de tout faire tenir sur une seule ligne et rétrécit les éléments. Si tes cartes s'écrasent, ajoute flex-wrap: wrap sur le parent.
10. Grid : placer en lignes et en colonnes
Si Flexbox gère une dimension, Grid gère les deux en même temps : des lignes ET des colonnes. C'est l'outil idéal pour une grille de cartes ou pour la structure globale d'une page. Tu l'actives avec display: grid sur le parent, puis tu décris tes colonnes.
.grille {
display: grid;
grid-template-columns: 1fr 2fr 1fr; /* 3 colonnes : 1 part, 2 parts, 1 part */
gap: 24px;
}
L'unité fr veut dire « fraction de l'espace disponible ». Ici, la colonne du milieu est deux fois plus large que les autres. Et gap met de l'espace entre les lignes et les colonnes, sans aucune marge à bricoler.
Exemple 1 : une grille de cartes responsive, sans media query
Voilà ma ligne de CSS préférée :
.cartes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
}
Ce qu'elle dit au navigateur : « mets autant de colonnes que possible, chacune d'au moins 240px, et partage le reste de la place ». Sur grand écran tu as quatre colonnes, sur tablette deux, sur mobile une seule. Tout seul, sans une seule media query.
Exemple 2 : la structure d'une page avec grid-template-areas
On nomme les zones de la page, puis on dit à chaque élément dans quelle zone il va. C'est lisible comme un plan.
<div class="page">
<header class="entete">En-tête</header>
<aside class="lateral">Sommaire</aside>
<main class="contenu">Contenu principal</main>
<footer class="pied">Pied de page</footer>
</div>
.page {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
"entete entete"
"lateral contenu"
"pied pied";
min-height: 100vh;
}
.entete { grid-area: entete; }
.lateral { grid-area: lateral; }
.contenu { grid-area: contenu; }
.pied { grid-area: pied; }
Pour une version mobile, il suffit de réécrire le plan dans une media query, sans toucher au HTML.
Exemple 3 : centrer en deux lignes
.centre {
display: grid;
place-items: center; /* centre horizontalement ET verticalement */
min-height: 100vh;
}
Les propriétés à connaître
| Propriété | Sur quoi | Rôle |
|---|---|---|
grid-template-columns |
le parent | définit les colonnes (1fr 2fr, repeat(3, 1fr)) |
grid-template-rows |
le parent | définit les lignes |
grid-template-areas |
le parent | dessine la page avec des noms de zones |
gap |
le parent | espace entre les lignes et les colonnes |
place-items |
le parent | aligne les éléments dans leur cellule |
grid-area |
un enfant | choisit la zone où va l'élément |
grid-column: span 2 |
un enfant | l'élément s'étend sur 2 colonnes |
Un piège classique :
une colonne en1frpeut déborder si elle contient un contenu long, comme une URL ou un bloc de code, parce que1frne descend jamais sous la taille de son contenu. Du coup pour contrer ce comportement ajoute :minmax(0, 1fr).
11. Choisir le bon outil
La règle d'or : Grid et Flexbox pour la mise en page, position pour les détails, et float seulement pour une image entourée de texte. Voilà le tableau à garder sous la main :
| Ton besoin | La bonne méthode | Pourquoi |
|---|---|---|
| Empiler des blocs et du texte | Le flux normal (block, inline) |
C'est gratuit, ne touche à rien |
| Centrer un conteneur de largeur limitée | margin: 0 auto |
Simple, universel |
| Faire entourer une image par du texte | float |
Son seul vrai rôle aujourd'hui |
| Aligner des éléments sur une ligne ou une colonne (nav, boutons) | Flexbox | Une dimension, alignement facile |
| Placer en lignes ET colonnes (grille de cartes, layout de page) | Grid | Deux dimensions, très lisible |
| Centrer parfaitement un élément | Flexbox ou Grid (place-items: center) |
Deux ou trois lignes |
| Superposer un petit élément sur un autre (badge, tooltip) | position: absolute dans un parent relative |
L'élément sort du flux |
| Coller à l'écran (bannière, bouton retour en haut) | position: fixed |
Se base sur la fenêtre |
| Coller au scroll (header, sommaire) | position: sticky |
Normal puis collé au seuil |
Et dans les sites et app en production, on mélange tout ça : Grid pour la structure de la page, Flexbox pour les lignes à l'intérieur des composants, position pour les petits détails par-dessus. C'est exactement ce qu'on fera dans le deuxième mini-projet.
12. Mini-projet 1 : une fenêtre modale
On va utilisé les propriétés vu plus haut pour contruire une modale. On a besoin d'un fond sombre qui couvre l'écran, et d'une boîte centrée par-dessus.
<div class="overlay">
<div class="modal">
<h2>Bienvenue !</h2>
<p>Voilà ta première modale en CSS pur.</p>
</div>
</div>
.overlay {
position: fixed;
inset: 0; /* raccourci pour top, right, bottom, left = 0 */
background: rgba(0, 0, 0, 0.6);
z-index: 100;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
padding: 24px;
border-radius: 12px;
max-width: 400px;
}
Ce qui se passe ici :
.overlayest enfixedavecinset: 0: il couvre tout l'écran, quoi qu'il arrive..modalest enabsolutedans l'overlay, qui est positionné et sert donc de repère.top: 50%+left: 50%placent le coin de la modale au centre, puistranslate(-50%, -50%)la recale de la moitié de sa propre taille. Résultat : centrée pile au milieu.
13. Mini-projet 2 : une page complète
Pour ce deuxieme mini projet on va exploiter tout les attributs pour créer une page complète. On assemble maintenant presque tout : Grid pour la structure, Flexbox pour le header, sticky pour le garder en haut, et une grille responsive de cartes (avec le badge absolute du début).
<div class="page">
<header class="entete">
<a class="logo" href="/">MonBlog</a>
<nav class="liens">
<a href="#">Articles</a>
<a href="#">À propos</a>
</nav>
</header>
<aside class="lateral">Sommaire</aside>
<main class="contenu">
<div class="cartes">
<article class="card"><span class="badge">Nouveau</span>Carte 1</article>
<article class="card">Carte 2</article>
<article class="card">Carte 3</article>
</div>
</main>
<footer class="pied">© 2026 MonBlog</footer>
</div>
/* 1. La structure de la page : Grid */
.page {
display: grid;
grid-template-columns: 220px 1fr;
grid-template-areas:
"entete entete"
"lateral contenu"
"pied pied";
min-height: 100vh;
}
/* 2. Le header : Flexbox + sticky */
.entete {
grid-area: entete;
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 24px;
position: sticky;
top: 0;
background: #fff;
z-index: 10;
}
.liens { display: flex; gap: 16px; }
.lateral { grid-area: lateral; padding: 24px; }
.contenu { grid-area: contenu; padding: 24px; }
.pied { grid-area: pied; padding: 16px 24px; }
/* 3. Les cartes : Grid responsive */
.cartes {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 24px;
}
/* 4. Le badge : relative + absolute */
.card {
position: relative;
padding: 24px;
border: 1px solid #e5e7eb;
border-radius: 12px;
}
.badge {
position: absolute;
top: 12px;
right: 12px;
background: #ef4444;
color: #fff;
padding: 4px 10px;
border-radius: 999px;
font-size: 0.8rem;
}
/* 5. Version mobile : on réécrit juste le plan */
@media (max-width: 700px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"entete"
"contenu"
"lateral"
"pied";
}
}
Regarde comme chaque outil fait son métier : Grid dessine la page, Flexbox aligne la barre du haut, sticky la garde visible au scroll, position colle le badge sur la carte. Aucun hack, aucun float.
Les pièges à retenir
- Badge qui s'envole : il manque un
position: relativesur le parent. - top / left sans effet : l'élément est encore en
static. - sticky qui ne colle pas : tu as oublié
top, ou un parent a unoverflow. - z-index ignoré : l'élément n'est pas positionné, ou il est coincé dans un autre contexte d'empilement.
- Contenu masqué par un élément fixed : il est sorti du flux, prévois un
paddingou une marge. - margin: 0 auto sans effet : le bloc n'a pas de
widthni demax-width. - Parent qui s'effondre avec float : ajoute
display: flow-rootsur le parent. - Flexbox, justify-content qui fait l'inverse : avec
flex-direction: column, les axes sont inversés. - Grid, colonne 1fr qui déborde : passe à
minmax(0, 1fr).
Pour résumer
- Flux normal :
block,inline,inline-block, etmargin: 0 autopour centrer un conteneur. - float : une image entourée de texte, pas de la mise en page.
- static : le flux normal, par défaut.
- relative : décale un peu, et sert de repère aux enfants
absolute. - absolute : sort du flux, se place par rapport au parent positionné le plus proche.
- fixed : collé à la fenêtre, il ne bouge pas au scroll.
- sticky : normal, puis se colle quand il atteint son seuil.
- z-index : gère l'empilement, mais attention aux contextes d'empilement.
- Flexbox : une dimension, pour aligner et répartir.
- Grid : deux dimensions, pour les grilles et la structure d'une page.
Voilà, tu as maintenant toutes les clés pour placer n'importe quel élément sans galérer. Clone le repo, casse le code, remets-le, c'est comme ça qu'on retient.
👉 Le code complet : lien vers le repo GitHub
Une question, un cas qui te bloque ? Dis-le-moi juste ici ! ou réponds à la newsletter, je lis tout. Et rendez-vous la semaine prochaine pour le prochain article.
