Inline, block, inline-block : comprendre le display des balises HTML

Quand j'ai débuté en 2017, le soir après le travail, une question m'a bloqué plus longtemps que je ne veux bien l'admettre : pourquoi certaines balises provoquent un retour à la ligne alors que d'autres restent sagement dans le texte ? La réponse tient en une propriété CSS, display, et dans la valeur par défaut que le navigateur attribue à chaque balise. C'est l'un des concepts les plus rentables à comprendre tôt : tout le placement des éléments en découle.

Dans cet article, je reprends cette notion à zéro, avec une particularité : chaque comportement décrit ici a été mesuré dans Chrome pendant la rédaction (largeurs en pixels, positions, retours à la ligne), dans une page de test de 640 px de large, police Arial 16 px. Vous n'aurez donc pas à me croire sur parole. Si vous découvrez tout juste le HTML, je vous conseille de lire d'abord les bases pour bien débuter en HTML, puis de revenir ici.

D'où vient ce comportement : la propriété display

Chaque navigateur embarque une feuille de style par défaut qui attribue une valeur de display à chaque balise. On peut la lire avec getComputedStyle(), c'est exactement ce que j'ai fait. Voici ce que Chrome renvoie, balise par balise :

Retenez surtout ceci : block et inline ne sont pas des propriétés magiques des balises, ce sont des valeurs CSS par défaut. Vous pouvez transformer un span en bloc ou un div en élément inline avec une seule ligne de CSS. Le HTML donne le sens, le CSS donne le comportement d'affichage.

Les balises block : toute la largeur, puis retour à la ligne

Un élément block occupe toute la largeur disponible de son parent, même si son contenu est minuscule, et l'élément suivant repart sur une nouvelle ligne. Voici ma page de test :

<div class="conteneur">
  <div>Premier bloc</div>
  <div>Deuxième bloc</div>
</div>
.conteneur {
  width: 640px;
}

Mesures relevées dans Chrome : chaque div occupe 640 px de large sur 18 px de haut, alors que le texte « Premier bloc » n'en remplit qu'une fraction. Le premier bloc commence à la position verticale 0, le second exactement à 18 px, c'est-à-dire juste en dessous. Deux blocs ne se placent jamais côte à côte par défaut, quelle que soit la place restante.

Autre point mesuré qui surprend souvent les débutants : certains blocs arrivent avec des marges par défaut. Un p a 16 px de marge en haut et en bas, un h2 est rendu en 24 px gras avec environ 20 px de marge verticale. C'est le navigateur qui les ajoute, pas vous. La plupart des balises que vous utiliserez pour organiser une page sont des blocs : je les détaille dans l'article sur les 6 balises d'architecture.

Les balises inline : dans le flux du texte

Un élément inline ne mesure que la largeur de son contenu et reste sur la ligne en cours. Dans ma page de test, deux span placés dans un même paragraphe occupent respectivement 93 px et 109 px de large, et partagent la même position verticale : ils sont bien côte à côte, séparés par l'espace du texte source.

Un élément inline a une autre propriété précieuse : il peut se couper en fin de ligne. J'ai placé un span contenant une phrase entière dans un conteneur de 220 px : le navigateur l'a découpé en 4 fragments répartis sur 4 lignes (c'est mesurable avec getClientRects(), qui renvoie un rectangle par fragment). C'est exactement le comportement attendu pour un lien ou une mise en emphase au milieu d'un paragraphe : le texte coule, l'élément suit. Les balises inline les plus courantes servent à enrichir le texte, comme je le montre dans l'article sur les balises pour structurer le texte.

La balise générique inline est span : elle n'a aucun sens particulier et aucune apparence par défaut, elle sert de point d'accroche pour le CSS. Son équivalent côté block est div. Cas à part : img renvoie bien display: inline, mais c'est un élément dit « remplacé » (son contenu vient d'un fichier externe), ce qui lui vaut quelques règles particulières, notamment le droit d'avoir des dimensions.

display: blockpremier bloc : toute la largeur (640 px mesurés)deuxième bloc : toujours en dessous, jamais à côtédisplay: inlineLe texte se poursuitun spanau fil de la ligne, puisun liensans provoquer de retour à la ligne.
Block empile des boîtes pleine largeur, inline insère des boîtes dans le flux du texte.

Ce que le CSS refuse d'appliquer à un élément inline

C'est le piège classique. On pose une largeur sur un span, rien ne se passe, et on perd une soirée. J'ai mesuré précisément ce qui est ignoré :

span {
  width: 200px; /* ignoré : mesuré à 171 px, la largeur du contenu */
  height: 100px; /* ignoré : mesuré à 17 px, la hauteur du texte lui-même */
}

Sur un élément inline, width et height sont purement et simplement ignorés : mon span réglé sur 200 × 100 px mesure en réalité 171 × 17 px, soit exactement la place de son texte (17 px et non 18, car le rectangle d'un élément inline suit les métriques de la police, un pixel de moins que la ligne de 18 px qui l'accueille). Les marges et le padding verticaux ne poussent pas non plus les lignes voisines : avec padding: 30px et margin: 40px sur un span, le paragraphe qui le contient garde ses 18 px de hauteur, identique au paragraphe témoin. Le fond coloré du padding se dessine bien, mais il déborde par-dessus les lignes du dessus et du dessous sans les décaler.

En revanche, les marges et le padding horizontaux fonctionnent : mon span avec margin: 40px démarre bien 40 px plus loin sur sa ligne. Pour comprendre en détail ce que largeur, padding, bordure et marge représentent sur un élément, direction le modèle de boîte CSS.

inline-block : dimensionnable, mais toujours dans la ligne

Troisième valeur à connaître : display: inline-block. L'élément reste dans le flux du texte comme un inline, mais accepte largeur, hauteur, marges et padding comme un block. Mesuré : trois span en inline-block réglés sur 150 × 80 px font exactement 150 × 80 px et s'alignent côte à côte sur la même ligne. C'est pour cela que les champs de formulaire (input, button) utilisent cette valeur par défaut.

Mais inline-block a des limites bien réelles, que j'ai toutes reproduites :

vertical-align: baseline (défaut)textesur deux lignescourtligne de base commune18 px
Deux inline-block de même hauteur : le navigateur aligne leurs lignes de base, pas leurs sommets (décalage mesuré : 18 px, une hauteur de ligne).

Les cas qui ne rentrent dans aucune case

Quelques éléments méritent une mention à part, car les idées reçues circulent :

Et pour construire une vraie mise en page ?

Pendant des années, les développeurs ont aligné des colonnes avec inline-block, en luttant contre l'espace fantôme et les décalages de ligne de base que je viens de mesurer. Ce temps est révolu : dès qu'il s'agit de placer des éléments les uns par rapport aux autres (colonnes, grilles de cartes, barres de navigation), la solution moderne est flexbox, qui règle ces trois limites d'un coup : plus d'espace parasite entre les enfants, un alignement vertical contrôlé, et une répartition de l'espace pilotée par le parent.

Comprendre inline et block reste indispensable pour autant : à l'intérieur de chaque bloc, le texte et ses balises inline continuent de suivre exactement les règles vues ici, et les valeurs par défaut du navigateur s'appliquent tant que votre CSS ne dit rien.

Ce qu'il faut retenir

Toutes les valeurs citées ont été mesurées dans Chrome pendant la rédaction de cette mise à jour. Je vous encourage à refaire ces tests vous-même : quelques div, quelques span, l'inspecteur du navigateur, et ces comportements deviendront des réflexes. À bientôt pour la suite du parcours.