Retour

Comment faire une application pleine page ?



Introduction

Une application pleine page est une interface sans barre de défilement au niveau de la page : l’ensemble du contenu reste visible en permanence, comme c’est le cas par exemple sur Figma ou sur la plupart des tableaux de bord conçus pour offrir une vision d’ensemble. Mettre en place un tel affichage est une tâche relativement courante, mais elle recèle tout un lot de difficultés dont il faut savoir prendre compte. Voici un petit florilège d’astuces et de pièges à éviter.

Nous verrons d’abord quelques pièges courants à éviter lorsqu’on tente de mettre en place un tel affichage, puis des approches possibles pour y arriver.

Les pièges à éviter

La tentation du 100vh

Une manière souvent employée pour forcer un élément à prendre toute la hauteur de la page est d’utiliser une unité relative à la zone d’affichage ; plus spécifiquement : vh.

Par exemple, pour faire en sorte que le contenu prenne toute la hauteur de la page, on pourrait être tenté d’utiliser : 100vh :

Code

<style>
body {
height: 100vh;
background-color: aquamarine;
}
</style>
<body>
<p>Contenu.</p>
</body>

Résultat

Le problème est que sur les écrans de téléphone portable, la zone d’affichage du navigateur est dynamique (elle possède une barre d’adresse en haut et des boutons de navigation en bas) alors que l’unité vh est statique et fait toujours référence à la hauteur maximale possible, ce qui peut mener à des résultats indésirables si l’on veut qu’un élément fasse exactement la hauteur de l’écran.

Une manière de palier ce problème est d’utiliser les unités d’affichage dynamiques (svh et dvh), mais comme ce sont des unités relativement nouvelles, vous devez faire attention si vous souhaitez assurer une compatibilité pour les plus anciens navigateurs. Voir la compatibilité de dvh sur CanIuse.

Pour une meilleure compatibilité, je préfère généralement utiliser height: 100%. Attention, la propriété height en CSS possède un fonctionnement très spécifique et il est très facile de se sentir perdu si l’on ne comprend pas comment elle fonctionne. Je vous recommande vivement cet article de Josh Comeau sur le sujet.

Les marges

Partons du principe que vous avez opté pour l’approche des pourcentages pour votre application pleine page, mais vous constatez que l’élément ne prend pas toute la hauteur comme prévu :

Code

<style>
html,
body {
height: 100%;
}
body {
margin: 8px;
background-color: aquamarine;
}
</style>
<body>
<p>Contenu.</p>
</body>

Résultat

Le décalage vient des marges du body. Contrairement au remplissage (padding) ou à la bordure, une marge s’ajoute en dehors de la boîte de l’élément. Avec height: 100 %, le navigateur calcule donc 100 % de hauteur plus les marges, d’où le débordement et la barre de défilement. Les navigateurs appliquent par défaut une marge de 8 px sur le body.

La correction la plus directe est de remettre les marges à zéro sur html et body. Si vous souhaitez conserver un espacement intérieur, préférez le padding et assurez-vous d’utiliser box-sizing: border-box : sans cela, un padding s’ajouterait lui aussi à la hauteur totale, avec le même effet indésirable.

Code

<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
padding: 8px;
background-color: aquamarine;
}
</style>
<body>
<p>Contenu.</p>
</body>

Résultat

Ici, le fond aquamarine remplit bien la page, sans barre de défilement superflue. Le padding de 8 px est absorbé grâce à border-box, et height: 100 % fixe la hauteur à celle de la zone d’affichage.

Cas pratique

Voyons ensemble un cas pratique. L’objectif est de reproduire la structure suivante : un en-tête fixe, une barre latérale de filtres et une zone principale avec des boutons et un diagramme. Seules les zones de contenu défilent en interne ; le body ne possède pas de barre de défilement. Pour y parvenir, deux approches équivalentes existent ; nous les détaillerons chacune dans leur propre sous-section.

La chaîne de hauteur

Contrairement à l’exemple du 100vh vu plus haut, nous utilisons ici height: 100 %. Pour que ce pourcentage se résolve, il faut une chaîne ininterrompue depuis la racine :

html → body → …

On commence donc par fixer explicitement la hauteur sur html et body :

html,
body {
height: 100%;
}

Difficulté : sans height: 100 % sur html, le pourcentage sur body ne se résout à rien de concret et le conteneur grandit avec son contenu. Si vous utilisez des frameworks comme React, il faut aussi appliquer height: 100 % sur l'élément d'ancrage (#root, etc.) pour prolonger cette chaîne jusqu'au composant principal.

Le body en colonne flex

Le body devient un conteneur flex en colonne qui occupe toute la hauteur disponible :

body {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 16px;
}

Le padding de 16 px est absorbé grâce au box-sizing: border-box déclaré plus haut : il est inclus dans les 100 %, et non ajouté par-dessus.

Difficulté : si le body n’a pas de hauteur fixée, il s’allonge avec son contenu et c’est lui qui défile. C’est précisément ce qu’on veut éviter.

La zone principale

L’en-tête garde sa hauteur naturelle. La zone .content reçoit flex: 1 pour occuper tout l’espace vertical restant, puis devient un conteneur flex en ligne pour accueillir la barre latérale et la colonne de droite :

.content {
flex: 1;
display: flex;
gap: 1rem;
}
.right-stuff {
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
}
.diagram {
flex: 1;
}

Difficulté : la liste de filtres compte une trentaine d’éléments. Sans intervention supplémentaire, ces éléments imposent une hauteur minimale à leurs parents flex (comportement par défaut : min-height: auto) et le body finit par déborder.

Approche avec min-height: 0

La première solution repose sur un duo de propriétés, à placer sur chaque maillon de la chaîne flex jusqu’aux zones qui doivent défiler :

.content,
.filters,
.right-stuff {
min-height: 0;
}
.filters,
.diagram {
overflow: auto;
}

min-height: 0 autorise un enfant flex à se comprimer en dessous de la taille de son contenu. Sans cela, flex: 1 ne suffit pas : l’élément refuse de rétrécir et repousse toute la page.

overflow: auto déplace ensuite le défilement au bon endroit : la barre latérale bleue et la zone rose du diagramme défilent en interne, tandis que le body reste fixe à la hauteur de la zone d’affichage.

Difficulté : min-height: 0 seul coupe le contenu sans offrir de défilement ; overflow: auto seul sur un parent qui n’a pas de hauteur contrainte ne produit aucun effet. Les deux propriétés fonctionnent ensemble.

Code

<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 16px;
}
.content {
flex: 1;
display: flex;
gap: 1rem;
min-height: 0;
}
.filters {
overflow: auto;
min-height: 0;
}
.right-stuff {
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
min-height: 0;
}
.diagram {
flex: 1;
overflow: auto;
}
</style>
<body>
<header>HEADER</header>
<div class="content">
<div class="filters"></div>
<div class="right-stuff">
<div class="toggles"></div>
<div class="diagram"></div>
</div>
</div>
</body>

Résultat

Approche avec height: 100%

Une alternative consiste à remplacer min-height: 0 par une chaîne de height: 100 % sur chaque maillon, complétée par overflow: auto là où c’est nécessaire :

.content,
.filters,
.right-stuff {
height: 100%;
}
.content,
.filters,
.diagram {
overflow: auto;
}

height: 100 % propage la hauteur du parent sur chaque niveau (.content, puis .filters ou .right-stuff). En revanche, overflow: auto n’est pas requis sur tous ces maillons : seulement là où l’élément doit à la fois se contraindre et défiler.

.content en a besoin car c’est un enfant flex (flex: 1) du body : sans overflow: auto (ou min-height: 0), sa hauteur minimale reste celle de son contenu et la chaîne de pourcentages ne démarre pas. Une fois .content contraint, .right-stuff obtient une hauteur résolue via height: 100 % seul ; le défilement est alors géré par .diagram (flex: 1 + overflow: auto).

Difficulté : la règle « height: 100 % + overflow: auto sur chaque maillon » semble séduisante, mais overflow: auto sur un conteneur intermédiaire qui ne déborde pas lui-même (comme .right-stuff) est superflu. Il faut distinguer les maillons qui propagent la hauteur de ceux qui défilent.

Code

<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body {
height: 100%;
margin: 0;
}
body {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 16px;
}
.content {
flex: 1;
display: flex;
gap: 1rem;
height: 100%;
overflow: auto;
}
.filters {
overflow: auto;
height: 100%;
}
.right-stuff {
flex: 1;
display: flex;
flex-direction: column;
gap: 1rem;
height: 100%;
}
.diagram {
flex: 1;
overflow: auto;
}
</style>
<body>
<header>HEADER</header>
<div class="content">
<div class="filters"></div>
<div class="right-stuff">
<div class="toggles"></div>
<div class="diagram"></div>
</div>
</div>
</body>

Résultat

Les deux approches produisent le même rendu visuel dans cet exemple. min-height: 0 est l’astuce flex classique pour autoriser la compression ; height: 100 % peut paraître plus intuitif si l’on raisonne déjà en chaîne de pourcentages depuis html et body.