Responsive design et media queries : rendre son site mobile

De l'ordre de six visites sur dix sur un site grand public se font depuis un téléphone. Pourtant, quand on apprend le HTML et le CSS, on travaille presque toujours sur un grand écran, et le premier passage sur mobile est une déception : texte minuscule, mise en page écrasée, ascenseur horizontal. Le responsive design, c'est l'ensemble des techniques qui font qu'une même page s'adapte à toutes les largeurs d'écran. Et contrairement à sa réputation, il tient en trois briques : une balise, une règle CSS, une méthode.

Méthode habituelle sur ce blog : tous les comportements décrits ont été mesurés pendant la rédaction, le 31 août 2026, dans Chrome 151 avec l'émulation mobile d'un écran de 390 pixels de large (la taille d'un iPhone récent). Les chiffres cités sont les valeurs relevées, pas des valeurs théoriques.

Le mystère du site minuscule : 980 pixels fantômes

Commençons par le symptôme que tout débutant a rencontré. J'ai créé une page toute simple (un titre, un paragraphe en 16 px, une carte de 300 px de large) et je l'ai ouverte dans un téléphone émulé de 390 px. Verdict mesuré : window.innerWidth répond 980. Pas 390. Le navigateur mobile a dessiné la page comme si l'écran faisait 980 px de large, puis a tout réduit photographiquement pour la faire rentrer dans les 390 px réels. Résultat : mon paragraphe de 16 px est affiché à environ 40 % de sa taille, soit l'équivalent de 6,4 px. Illisible.

Ce comportement est volontaire et date de l'arrivée de l'iPhone : le web de l'époque n'était pensé que pour le bureau, alors les navigateurs mobiles ont choisi de simuler un grand écran plutôt que d'afficher des mises en page cassées. Pour dire au navigateur « mon site sait s'adapter, ne simule rien », il faut une ligne dans le <head> :

<meta name="viewport" content="width=device-width, initial-scale=1" />

Même page, même téléphone émulé, avec cette seule ligne en plus : window.innerWidth répond 390, le texte s'affiche à sa vraie taille. Cette balise fait déjà partie du squelette HTML5 présenté dans les bases pour bien débuter en HTML ; vous savez maintenant pourquoi elle n'est pas optionnelle : sans elle, tout le reste de cet article est sans effet.

Sans la balise Texte dessiné sur 980 px puis réduit : illisible 980 px simulés écran réel : 390 px Avec la balise Texte dessiné sur 390 px : lisible 390 px réels écran réel : 390 px
Sans meta viewport, le mobile dessine sur 980 px virtuels puis réduit tout ; avec, il dessine sur la vraie largeur.

Les media queries : du CSS sous condition

Deuxième brique : la media query, une règle CSS qui ne s'applique que si une condition sur l'écran est vraie. La syntaxe se lit presque comme une phrase :

/* « Si l'écran fait au moins 600 px de large, alors applique ceci » */
@media (min-width: 600px) {
  .menu {
    flex-direction: row;
  }
}

Tout ce qui est écrit entre les accolades de @media est du CSS parfaitement normal ; il est simplement ignoré tant que la condition n'est pas remplie, et appliqué dès qu'elle l'est. Le navigateur réévalue en continu : redimensionnez la fenêtre et les règles s'activent ou se désactivent au pixel près. J'ai vérifié au banc : à 390 px de large, matchMedia("(min-width: 600px)") répond false ; à 700 px, il répond true. Les seuils que vous choisissez (600, 900...) s'appellent des points de rupture, ou breakpoints ; ils ne sont pas imposés, on les place là où sa propre mise en page commence à être à l'étroit.

La méthode mobile-first : une démo mesurée à trois largeurs

Troisième brique, la méthode. Écrire d'abord les styles pour le petit écran, sans aucune media query : c'est la version de base, celle que tout le monde reçoit. Puis ajouter des media queries en min-width qui enrichissent la mise en page quand la place augmente. C'est ce qu'on appelle l'approche mobile-first. Voici l'essentiel de la feuille de style de ma page de démonstration, un menu et six cartes :

/* Styles de base = mobile (aucune media query) */
.menu {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.grille {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}
img {
  max-width: 100%;
  height: auto;
}

/* Écran d'au moins 600 px : menu en ligne, grille en 2 colonnes */
@media (min-width: 600px) {
  .menu {
    flex-direction: row;
    gap: 24px;
  }
  .grille {
    grid-template-columns: 1fr 1fr;
  }
}

/* Écran d'au moins 900 px : 3 colonnes */
@media (min-width: 900px) {
  .grille {
    grid-template-columns: 1fr 1fr 1fr;
  }
}

J'ai mesuré cette page à trois largeurs dans Chrome. À 390 px : menu empilé (flex-direction: column), une seule colonne, cartes de 358 px. À 700 px : menu en ligne, deux colonnes, cartes de 326 px. À 1440 px : trois colonnes, cartes de 458,7 px. Trois mises en page réellement différentes, zéro duplication de contenu, et une vingtaine de lignes de CSS. Le menu s'appuie sur Flexbox pour passer de colonne à ligne d'un seul mot-clé.

La grille mérite le même détour : grid-template-columns: 1fr 1fr décrit deux colonnes égales qui se partagent la place disponible, et c'est le guide CSS Grid qui explique d'où sortent ces fr. L'important ici est la logique d'empilement : à 1440 px, les trois blocs de styles s'appliquent en même temps (la base, le palier 600 et le palier 900), chaque palier écrasant seulement ce qu'il redéfinit. C'est pour cela que l'ordre mobile-first est si confortable : on n'annule jamais rien, on ajoute.

0 600 px 900 px styles de base : appliqués partout + @media (min-width: 600px) + min-width: 900 390 700 1440 Les couches s'empilent : à 1440 px, les trois s'appliquent.
Mobile-first : les styles de base valent partout, chaque palier min-width ajoute une couche par-dessus.

Le piège mesuré : l'élément plus large que l'écran

Ma page de démonstration cachait un intrus : un bloc avec width: 600px en dur. Sur le téléphone émulé de 390 px, la mesure est nette : le viewport de mise en page s'étire pour loger l'intrus, et window.innerWidth se met à répondre 632 au lieu de 390. Le zoom, lui, ne bouge pas (visualViewport.scale reste à 1) : les cartes s'affichent à leur taille réelle, mais la page déborde et offre un vrai défilement horizontal de 242 px (un scrollWidth de 632 contre 390 de largeur visible). Le plus sournois est ailleurs : les media queries, elles, continuent de raisonner sur les 390 px de l'appareil (mon matchMedia à 600 px répondait toujours false). L'affichage se comporte donc comme un écran de 632 px pendant que les règles CSS restent calées sur 390 : une incohérence qu'aucun breakpoint ne peut rattraper. Précision utile : sur un vrai téléphone (ou dans le device mode complet des DevTools), ce même débordement se manifeste typiquement autrement, par un dézoom de la page entière où tout le contenu, même les colonnes bien sages, s'affiche réduit. C'est un comportement connu des navigateurs mobiles, que mon banc en émulation ne reproduit pas. Dans les deux cas, un seul élément trop large suffit à ruiner l'affichage de toute la page, et c'est la cause numéro un des sites cassés sur mobile malgré une meta viewport bien en place.

La correction tient en une ligne, appliquée au banc avec re-mesure immédiate : max-width: 100% sur le bloc fautif, et la page est revenue à 390 px pile, sans plus aucun débordement horizontal. D'où la règle d'hygiène du responsive, déjà croisée dans le guide sur les dimensions et le positionnement en CSS : les largeurs fixes en pixels sont des promesses qu'un petit écran ne peut pas tenir ; préférez max-width, les pourcentages et les unités fr, et posez systématiquement img { max-width: 100%; height: auto; } pour que les images suivent.

Comment tester sans collection de téléphones

Tout ce que cet article mesure, vous pouvez le reproduire sans matériel : dans Chrome ou Firefox, F12 ouvre les outils de développement, et l'icône téléphone/tablette (ou Ctrl+Maj+M) bascule en mode appareil. Vous y choisissez une largeur au pixel près, un appareil prédéfini, et vous voyez vos media queries s'activer en direct en redimensionnant. Deux réflexes de vérification pour finir un projet : balayer lentement de 320 px (les plus petits téléphones encore courants) jusqu'à votre grand écran en guettant les cassures, et chercher l'ascenseur horizontal fantôme, celui qui trahit un élément trop large. Un dernier fait pour la route : les media queries ne parlent pas que de largeur. Ce blog change par exemple de palette selon @media (prefers-color-scheme: dark), la préférence sombre ou claire de votre système ; c'est la même mécanique de CSS sous condition.

Ce qu'il faut retenir

Le responsive n'est pas une couche magique qu'on ajoute à la fin, c'est une façon d'écrire son CSS depuis le début. La balise meta viewport pour que le téléphone arrête de simuler un écran de 980 px ; des styles de base pensés pour le petit écran ; des media queries min-width qui enrichissent quand la place le permet ; et aucune largeur fixe qui dépasse. Les chiffres de cet article valent ce qu'ils mesurent : une page précise, dans Chrome 151, le 31 août 2026 ; mais la mécanique, elle, est standardisée et stable depuis des années. Prochaine étape logique si vous suivez le parcours du blog : reprendre votre page d'exercice et lui offrir ses deux premiers points de rupture.