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 :
- block :
div,p,h1,h2,ul,ol,section,article,header,footer,nav,main,form,blockquote, et mêmehr; - inline :
span,a,strong,em,code,label,img,br; - inline-block :
input,button,select,textarea; - valeurs spéciales :
lirenvoielist-itemettablerenvoietable.
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.
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 :
- L'espace fantôme. Si votre code HTML contient un espace ou un saut de ligne entre deux éléments inline-block, cet espace est rendu : j'ai mesuré un écart de 4,45 px entre deux boîtes séparées par un espace dans le code source, et 0 px quand les balises se touchent. Votre mise en page dépend alors de l'indentation de votre fichier, ce qui est très fragile.
- L'alignement sur la ligne de base. Deux inline-block de même hauteur ne sont pas forcément alignés : avec une boîte contenant une ligne de texte et une autre en contenant deux, j'ai mesuré un décalage vertical de 18 px entre leurs sommets, soit exactement une hauteur de ligne, car le navigateur aligne par défaut leurs lignes de base (
vertical-align: baseline). En ajoutantvertical-align: top, les deux sommets se retrouvent exactement à la même position. - La boîte est insécable. Contrairement à un inline, un inline-block ne se coupe pas en fin de ligne : ma boîte de 150 px placée au milieu d'un texte dans un conteneur de 220 px est passée entière à la ligne suivante.
Les cas qui ne rentrent dans aucune case
Quelques éléments méritent une mention à part, car les idées reçues circulent :
- Les commentaires HTML (
<!-- comme ceci -->) ne sont pas des éléments : ils n'ont aucun rendu et servent uniquement à annoter votre code. brest techniquement un élément inline (mesuré), mais son seul rôle est de forcer un retour à la ligne dans le texte.hrest souvent présenté comme un cousin debr; c'est faux. Mesuré dans Chrome :hrrenvoiedisplay: blocket occupe toute la largeur de son parent (640 px sur 2 px de haut dans mon test). C'est un vrai bloc, une ligne de séparation thématique.
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
- Chaque balise a une valeur de
displaypar défaut fournie par le navigateur ; block et inline sont des comportements CSS, pas des natures figées. - Un block occupe toute la largeur disponible (640 px mesurés dans un conteneur de 640 px, quel que soit le contenu) et repousse le suivant à la ligne.
- Un inline ne mesure que son contenu, reste dans la ligne et peut se couper en fin de ligne ;
width,heightet les marges verticales y sont ignorés. - Un inline-block accepte les dimensions tout en restant dans la ligne, au prix de l'espace fantôme, de l'alignement baseline et de l'insécabilité.
- Pour la mise en page moderne, ces notions sont le socle sur lequel flexbox s'appuie.
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.