Mises en page CSS et responsive design

  1. 1 Principes de base
    1. Quel mode de mise en page utiliser ?
    2. Principes de fonctionnement
  2. 2 Flexbox
  3. 3 Grille et sous-grille
    1. Exemple de code : grille et sous-grille
  4. 4 Requêtes de conteneur
    1. Exemple de code : typographie fluide utilisant des unités de requête de conteneur
  5. 5 Superpositions natives, positionnement des ancres et contextes d'empilement
  6. 6 Suivi du dépassement et stabilité de la mise en page
  7. 7 Mécanismes de la fenêtre d'affichage et distribution des pistes
  8. 8 lignes de grille (ou "masonry")

1 Principes de base

Dans la mesure du possible, appuyez-vous sur le moteur de mise en page du navigateur pour améliorer les performances. Utilisez le dimensionnement intrinsèque, les propriétés logiques et aspect-ratio avant de recourir à des dimensions codées en dur ou à des requêtes média complexes.

1.1 Quel mode de mise en page utiliser ?

Parcourez l'arbre de décision de haut en bas et arrêtez-vous à la première correspondance. Notez que les mises en page peuvent être imbriquées les unes dans les autres et que chaque décision est basée sur le cas d'utilisation de ce conteneur.

  1. S'agit-il d'une simple ligne OU colonne d'éléments ? Utilisez flexbox : 1D, priorité au contenu, le contenu est distribué le long d'un seul axe.
  2. Un élément imbriqué doit-il s'aligner sur les pistes de la grille de son grand-parent ? Utilisez subgrid : 2D, axé sur les relations, hérite des pistes parentes pour que les petits-enfants puissent s'aligner sur les frères et sœurs.
  3. S'agit-il d'une structure de page ou de composant complexe avec des lignes ET des colonnes ? Utilisez grid : 2D, axé sur la mise en page, vous définissez le squelette et le contenu le remplit.
  4. Le contenu est-il un long texte qui doit être divisé en colonnes équilibrées ? Utilisez le mode multicolonne : flux 1D, style journal.
  5. Les éléments sont-ils de hauteurs différentes et doivent-ils être emballés de manière compacte ? Utilisez grid avec grid-auto-flow: dense dès aujourd'hui. N'utilisez la disposition en maçonnerie native (alias "grid lanes") que lorsqu'elle est disponible dans votre cible Baseline (voir §8).
  6. Un élément doit-il flotter au-dessus de la page et rester lié spatialement à un déclencheur, même au-delà des limites du DOM ou des contextes d'empilement ? Utilisez le positionnement d'ancrage : anchor-name sur le déclencheur, position-anchor sur la superposition (voir §5).

1.2 Principes de fonctionnement

  • Utilisez des propriétés logiques (inline-size, block-size, margin-inline, padding-block, inset-inline-start) pour les dimensions et l'espacement de la mise en page. Pour en savoir plus, consultez CSS : architecture moderne.
  • Appliquez le modèle mental "contenu d'abord" ou "mise en page d'abord" : flexbox lorsque les éléments dictent le flux, grille lorsque vous définissez d'abord le squelette.
  • Utilisez les raccourcis place-* (place-content, place-items, place-self) pour aligner les deux axes dans une seule déclaration.
  • Utilisez le dimensionnement intrinsèque (min-content, max-content, fit-content()) et les pistes flexibles (fr, minmax()) avant les pistes fixes width/height : moins de requêtes média, des mises en page plus résilientes.
  • N'utilisez PAS overflow: hidden pour masquer les problèmes de mise en page. Corrigez-les à la source. Par exemple, certains éléments remplacés comme <canvas>, <img>, <svg>, <video> et <iframe> utilisent display: inline par défaut, ce qui peut ajouter un espace dans certaines mises en page. Au lieu de masquer l'espace avec overflow, supprimez-le en appliquant une valeur d'affichage externe de bloc (par exemple, display: block, grid, flex, etc.).
  • Utilisez aspect-ratio pour réserver de l'espace pour les contenus multimédias et éviter les décalages de mise en page avant le chargement des éléments.
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

Pour en savoir plus sur calc-size() et le dimensionnement intrinsèque tenant compte des contraintes, consultez Calculer avec des tailles intrinsèques.

2 Flexbox

Mise en page unidimensionnelle : les éléments s'écoulent le long d'un seul axe principal avec un alignement sur l'axe transversal. Utilisez-le pour les barres de navigation, les barres d'outils, les lignes d'éléments et toute distribution sur une seule ligne ou colonne.

À faire :

  • Établissez un contexte avec display: flex et définissez l'axe principal avec flex-direction (row par défaut).
  • Utilisez flex-wrap: wrap chaque fois qu'un dépassement est possible. nowrap sans overflow: auto/hidden débordera sur les fenêtres d'affichage étroites.
  • Superposez flex-wrap: balance après flex-wrap: wrap sur les lignes encapsulées d'éléments compacts (comme les nuages de mots clés, les pastilles de filtre, les groupes de boutons ou les liens de navigation) pour répartir les éléments de manière uniforme sur les lignes et éviter qu'un seul élément ne se retrouve isolé sur la dernière ligne. Les navigateurs non compatibles reviennent à wrap.
  • Utilisez la propriété abrégée flex <grow> <shrink> <basis> (par exemple, flex: 1 1 250px) sur les éléments plutôt que de définir flex-grow/flex-shrink/flex-basis individuellement.
  • Utilisez gap (ou la version longue row-gap/column-gap) pour l'espacement entre les éléments au lieu des marges enfants.
  • Ajoutez le préfixe safe à l'alignement positionnel (par exemple, align-items: safe center) pour que le contenu sélectionnable ne soit pas tronqué lorsque le conteneur est plus étroit que son contenu.
  • Poussez un seul élément à l'extrémité de l'axe principal avec margin-inline-start: auto (ou margin-block-start: auto). C'est la solution standard.
  • Remplacez l'alignement transversal par élément par align-self.
  • Utilisez align-items pour centrer tous les éléments sur l'axe transversal, margin: auto sur un seul élément pour le centrer sur les deux axes indépendamment, et align-content uniquement lorsque le conteneur s'ajuste et dispose d'un espace supplémentaire entre les lignes.
  • Définissez min-inline-size: 0 (ou min-width: 0) sur les éléments flex qui contiennent du contenu long et insécable (URL, code, chaînes longues). Par défaut, les éléments flex ne se réduisent pas en dessous de la taille de leur contenu, ce qui entraîne un dépassement.

À ne pas faire :

  • N'utilisez pas justify-self sur les éléments flex. Cette propriété ne fonctionne que sur les mises en page de type grille, bloc et positionnement absolu. Utilisez plutôt des marges automatiques.
  • N'utilisez pas order ni flex-direction: *-reverse pour réorganiser le contenu interactif. Elles ne modifient que l'ordre visuel. L'ordre du DOM détermine toujours la sélection séquentielle. Par conséquent, le flux de tabulation au clavier ne correspondra pas à ce que l'utilisateur voit.
  • Ne confondez pas space-around (demi-espace aux extrémités) avec space-evenly (espaces égaux avant, entre et après).
  • N'oubliez pas l'inversion des axes : lorsque flex-direction: column, justify-content s'aligne sur l'axe de bloc et align-items s'aligne sur l'axe intégré, soit l'inverse de la valeur par défaut.
  • Ne dimensionnez pas à la fois le conteneur et ses enfants pour qu'ils se remplissent mutuellement. C'est une source courante de dépassement et de résultats inattendus. Donnez une taille définie à un côté.
  • Ne définissez pas à la fois flex-basis et width/inline-size sur le même élément. flex-basis est prioritaire dans un contexte Flex, et width est ignoré. Utilisez flex-basis (ou le raccourci flex) comme source unique de vérité pour dimensionner les éléments flex.
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 Grille et sous-grille

Mise en page bidimensionnelle : définissez explicitement les lignes ET les colonnes, ou laissez le moteur les déduire. La sous-grille permet à une grille imbriquée d'hériter des pistes de son parent afin que les petits-enfants s'alignent sur les frères et sœurs.

Choisir les caractéristiques de la grille :

  • Savez-vous exactement de combien de colonnes vous avez besoin ?
    • Oui : utilisez des pistes explicites (grid-template-columns: 200px 1fr, repeat(3, 1fr), etc.).
    • Les différentes colonnes ont-elles besoin de tailles différentes (barre latérale + contenu principal, en-tête couvrant l'ensemble) ? → Utilisez grid-template-areas pour les régions nommées et lisibles.
    • Toutes les colonnes sont-elles uniformes ou positionnées uniquement par numéro de ligne ? → Utilisez repeat(N, ...) ou des lignes nommées
    • Non (responsive, nombre d'éléments inconnu) : utilisez repeat(auto-fit, minmax(min, 1fr))
    • Les éléments de la dernière ligne doivent-ils s'étirer pour remplir l'espace restant ? → auto-fit
    • Les pistes vides de la dernière ligne doivent-elles conserver leur taille minimale (en préservant les emplacements fantômes des colonnes) ? → auto-fill
  • Vous devez placer un élément à un endroit précis ?
    • Oui : utilisez grid-column: <start> / <end> ou grid-area: <name>.
    • Non (juste sur plusieurs pistes, la position du flux n'a pas d'importance) : utilisez grid-column: span <n>
  • Les éléments enfants doivent-ils hériter des tailles de pistes de la grille parente (alignement des bords irréguliers entre les éléments frères) ?
    • Oui : utilisez la sous-grille sur l'axe concerné.
    • Le nombre d'enfants par cellule est-il variable ? → sous-grille sur un seul axe ; utilisez grid-auto-rows/grid-auto-columns pour l'autre
    • Le nombre d'enfants est-il fixe ? → La sous-grille sur les deux axes est correcte
    • Non : grille standard, aucune sous-grille n'est nécessaire

À faire :

  • Établissez un contexte avec display: grid.
  • Utilisez grid-template-areas pour les mises en page complexes au niveau de la page. Les noms des zones sont explicites, et la déclaration peut être alignée en lignes et en colonnes pour une lisibilité immédiate.
  • Utilisez repeat(auto-fit, minmax(200px, 1fr)) pour les grilles de fiches responsives qui étirent les pistes remplies pour remplir la ligne, ou auto-fill pour conserver les pistes répétées vides à leur taille minimale.
  • Utilisez fr pour une distribution proportionnelle des pistes et minmax(min, max) pour des pistes flexibles, mais limitées.
  • Positionnez les éléments avec grid-column: span <n> pour les dimensionner sur plusieurs pistes, grid-column: <start> / <end> pour les placer sur des lignes spécifiques ou grid-area: <name> pour les régions nommées.
  • Utilisez une sous-grille (grid-template-columns: subgrid ou grid-template-rows: subgrid) pour résoudre le problème de "bord irrégulier" dans les listes de fiches. Les éléments internes tels que les titres, les métadonnées et les incitations à l'action s'alignent sur les éléments frères.
  • Associez une déclaration de sous-grille à une déclaration grid-template-rows/-columns explicite précédente en tant que solution de remplacement en cascade pour les anciens navigateurs.

À ne pas faire :

  • Ne vous attendez pas à ce que la taille de la piste auto-fit/auto-fill provienne du contenu de l'élément. Elle provient de l'argument de taille repeat().
  • N'utilisez pas grid-auto-flow: dense sur du contenu interactif. Il regroupe efficacement les éléments, mais les réorganise visuellement, ce qui perturbe l'ordre de navigation au clavier dans le DOM.
  • N'appliquez pas de sous-grille aux deux axes lorsque le nombre d'enfants est variable. Les éléments supplémentaires sont placés dans la dernière piste. Utilisez plutôt grid-auto-rows/grid-auto-columns pour l'axe implicite.
  • Ne confondez pas justify-items/align-items (aligne le contenu de l'élément dans sa piste) avec justify-content/align-content (aligne les pistes de la grille dans le conteneur). Si vous utilisez la mauvaise, elle n'aura aucun effet.
  • N'utilisez pas repeat(auto-fit/auto-fill, ...) sans un inline-size défini sur le conteneur. Dans display: inline-grid ou un élément flex sans taille, le conteneur n'a pas de largeur à diviser, ce qui rend le nombre de pistes imprévisible.

3.1 Exemple de code : grille et sous-grille

Shell de la page : <main class="page-layout"> contient <header>, <aside>, un <section class="card-grid"> avec <div class="card"> enfants et <footer>.

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 Requêtes de conteneur

Interrogez la taille (ou le style calculé) d'un conteneur ancêtre plutôt que de la fenêtre d'affichage. Modèle mental : requêtes de conteneur = contexte du composant ; requêtes média = mise en page globale et préférences de l'utilisateur (prefers-color-scheme, prefers-reduced-motion).

À faire :

  • Établissez un contexte de confinement avec container-type: inline-size (requêtes de largeur uniquement) ou container-type: size (les deux axes) sur un wrapper avant que ses descendants puissent être interrogés.
  • Nommez les conteneurs avec container-name (ou l'abréviation container : container: card / inline-size) lorsque des contextes imbriqués peuvent entrer en conflit.
  • Incluez les unités de requête de conteneur dans le calcul du type et de l'espacement fluides : cqi/cqb (inline/bloc logique), cqw/cqh (physique), cqmin/cqmax.
  • Attribuez une block-size définie au conteneur chaque fois que container-type: size est utilisé. Sans cela, les descendants se réduisent, car le confinement de la taille force le conteneur à ignorer son contenu.

À ne pas faire :

  • N'utilisez pas block-size comme valeur container-type, car elle n'est pas valide. Utilisez size pour les deux axes.
  • Ne vous attendez pas à ce que la taille intrinsèque des enfants influence le conteneur après avoir déclaré container-type. Le conteneur est calculé comme s'il n'avait pas d'enfants une fois que le confinement est actif.
  • Ne vous fiez pas aux unités de requête de conteneur dans les descendants d'un ancêtre non éligible. Elles reviennent à la petite fenêtre d'affichage (svw/svh).

4.1 Exemple de code : typographie fluide utilisant des unités de requête de conteneur

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

Pour en savoir plus sur les modèles de style responsif basés sur les composants, consultez Style basé sur la taille et Scaling fluide.

5 Superpositions natives, positionnement des ancres et contextes d'empilement

Quand utiliser chaque primitive de superposition :

  • Utilisez popover pour l'UI temporaire non modale (menus contextuels, toasts, info-bulles) : elle se trouve dans la couche supérieure, aucune gestion de z-index n'est nécessaire.
  • Utilisez <dialog> avec .showModal() pour les interactions modales qui nécessitent un piégeage du focus et un arrière-plan inerte.
  • Ne combinez pas popover et .showModal() sur le même élément, car il s'agit d'états d'exécution mutuellement exclusifs.

Positionnement des ancres (disposition spatiale des superpositions) :

  • Utilisez position-area (ou anchor() sur les encarts) et anchor-size() pour positionner et dimensionner une superposition par rapport à son déclencheur.
  • Utilisez position-try-fallbacks: flip-block (ou flip-inline) pour permettre au navigateur de repositionner l'overlay lorsqu'il dépasse la fenêtre d'affichage.
  • Ne mélangez pas les mots clés physiques et logiques dans une même valeur position-area. Choisissez un système de coordonnées.
  • Détectez la fonctionnalité avec @supports (anchor-name: --x) et fournissez une solution de remplacement en position absolue.

Pour en savoir plus sur l'implémentation, les stratégies de polyfill et la référence de valeur popover, consultez Contrôle déclaratif des boîtes de dialogue et des popovers et Info-bulles sensibles à la position. Pour en savoir plus sur le positionnement des ancres appliqué aux menus déroulants, aux menus contextuels et aux indicateurs d'onglets, consultez Menus déroulants adaptés à la fenêtre d'affichage et Soulignement des onglets de positionnement des ancres.

6 Suivi du dépassement et stabilité de la mise en page

Gérez les décalages de mise en page, les barres de défilement et le rognage de manière prévisible.

À faire :

  • Utilisez overflow: auto pour que les barres de défilement n'apparaissent que lorsque le contenu dépasse réellement.
  • Utilisez overflow: clip pour couper le contenu sans établir de conteneur de défilement. Optez pour le débordement avec overflow-clip-margin.
  • Utilisez scrollbar-gutter: stable pour réserver de l'espace pour les barres de défilement et éviter les décalages de mise en page lorsque le contenu s'agrandit.
  • Utilisez overscroll-behavior: contain (ou none) sur les zones de défilement imbriquées (comme les barres latérales, les fenêtres modales et les listes) pour empêcher les chaînes de défilement de se propager dans la page, ou overscroll-behavior: chain sur les wrappers de défilement intermédiaires (comme les pistes de balayage pour afficher) lorsque le défilement excessif doit se propager à un défilement parent sans déclencher de rebond élastique local.
  • Utilisez la triade -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical pour la troncature multiligne. Malgré le préfixe, ce modèle est entièrement spécifié et n'est pas obsolète. Déclarez la propriété abrégée line-clamp sans préfixe à côté. Les navigateurs qui ne la prennent pas encore en charge l'ignoreront sans problème. À ne pas faire :

  • N'utilisez pas overflow: scroll lorsque auto suffit. scroll force l'affichage des barres de défilement même lorsqu'il n'y a rien à faire défiler.

  • N'utilisez pas overflow: hidden lorsque vous souhaitez uniquement rogner : hidden établit un conteneur de défilement qui peut être défilé de manière programmatique.

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

Pour en savoir plus sur overflow: clip et overflow-clip-margin, consultez Contrôle du rognage du dépassement. Pour en savoir plus sur le transfert de défilement imbriqué avec overscroll-behavior: chain dans une UI de balayage pour afficher, consultez Balayer pour afficher. Pour en savoir plus sur la couleur, la taille et le thème de la barre de défilement, consultez Personnaliser la couleur ou l'épaisseur d'une barre de défilement, Mode sombre et Adapter la barre de défilement aux préférences de contraste élevé.

7 Mécanismes de la fenêtre d'affichage et distribution des pistes

  • Utilisez dvh/dvw pour les conteneurs de mise en page mobile qui doivent tenir compte des changements d'interface utilisateur du navigateur (réduction/développement de la barre d'URL).
  • N'utilisez pas 100vw pour une mise en page pleine largeur, car il ignore la largeur de la barre de défilement et entraîne un dépassement horizontal. Utilisez plutôt 100%, 100dvw ou 100svw.

Pour en savoir plus sur le point d'entrée du responsive design (unités de fenêtre d'affichage dynamiques, réservation d'espace pour les contenus multimédias, typographie fluide), consultez Responsive design.

8 voies de grille (ou maçonnerie)

La spécification est en cours de développement sous le nom "grid lanes" (par exemple, display: grid-lanes), tandis que les prototypes expérimentaux précédents utilisaient grid-template-rows: masonry.

À faire :

  • Utilisez une grille avec grid-auto-flow: dense pour un empaquetage serré dès aujourd'hui, en acceptant que l'ordre du DOM ne corresponde pas à l'ordre visuel.
  • Utilisez plusieurs colonnes (columns: 3; column-gap: 1rem) pour les flux de type "masonry" contenant beaucoup de contenu lorsque les éléments sont des fragments de document plutôt que des fiches de même importance.
  • Considérez grid-template-rows: masonry comme une amélioration progressive uniquement. Détectez les fonctionnalités avec @supports.

À ne pas faire :

  • N'exigez pas grid-template-rows: masonry tant que votre cible Baseline n'est pas à jour.
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}