Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 1021 minDébutant

Responsive design : media queries et images responsives

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • rendre une page responsive en mobile first, avec la balise viewport ;
  • écrire des media queries et des container queries ;
  • dimensionner avec des tailles fluides et clamp() ;
  • servir des images responsives et légères avec srcset, picture, WebP et AVIF ;
  • tester le responsive et lire les Core Web Vitals.

4 exercices corrigés1 schéma

Le responsive design (conception adaptative) consiste à écrire une seule page qui s'adapte à toutes les tailles d'écran, du téléphone au grand moniteur, grâce à des mises en page fluides, des media queries CSS et des images servies à la bonne taille. Ce chapitre explique la balise viewport, l'approche mobile first, la syntaxe des media queries et des container queries, les tailles fluides avec clamp(), les images responsives (srcset, <picture>, WebP, AVIF) et les Core Web Vitals, ces indicateurs de vitesse que Google mesure.

La balise meta viewport

Sans indication, un navigateur mobile suppose que la page a été conçue pour un écran de bureau : il la met en page sur une largeur fictive d'environ 980 pixels, puis la rétrécit pour qu'elle tienne dans l'écran. Résultat : un texte minuscule qu'il faut zoomer. La balise viewport, dans le <head>, lui dit d'utiliser la vraie largeur de l'écran :

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

Une media query ne sert à rien sans cette balise : sur téléphone, la « largeur » vue par le CSS resterait de 980 px.

Mobile first et points de rupture

Il y a deux façons d'écrire un CSS responsive :

  • desktop first : on écrit la version grand écran, puis on la défait pour les petits écrans avec des max-width ;
  • mobile first : on écrit d'abord la version mobile, la plus simple (une colonne), puis on l'enrichit pour les écrans plus larges avec des min-width.

L'approche mobile first est la recommandation actuelle : la version mobile est la base, elle ne reçoit aucune règle inutile ; on ajoute de la complexité seulement quand il y a de la place ; et elle oblige à se concentrer sur l'essentiel du contenu. Google, d'ailleurs, explore et classe les sites d'après leur version mobile.

Téléphone< 40rem123456asidela page défileTablette≥ 40rem123456asidela page défileOrdinateur≥ 64rem123456asideMême contenu sur les trois écrans : six cartes et l'aside.Seule la mise en page change : plus de colonnes quand la place le permet.Base, sans media queryune colonne(width >= 40rem)+ une deuxième colonne(width >= 64rem)+ une troisième colonne+ la colonne latérale
Mobile first : le même contenu, la base pour le téléphone, puis des paliers qui ajoutent des colonnes
css
/* Base : téléphone, une colonne */
.grille-ateliers {
  display: grid;
  gap: 1rem;
}

/* À partir de 40rem (640 px) : deux colonnes */
@media (width >= 40rem) {
  .grille-ateliers {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* À partir de 64rem (1 024 px) : trois colonnes */
@media (width >= 64rem) {
  .grille-ateliers {
    grid-template-columns: repeat(3, 1fr);
  }
}

Les valeurs où la mise en page change sont les points de rupture (breakpoints). Ne les choisis pas d'après des modèles de téléphones (il en sort tous les mois) mais d'après ton contenu : élargis la fenêtre du navigateur progressivement et ajoute un point de rupture là où la mise en page devient moche (lignes trop longues, cartes trop étirées). Trois ou quatre points suffisent pour la plupart des sites ; souvent autour de 40rem, 48rem et 64rem.

La syntaxe des media queries

css
@media (width >= 48rem) { … }                  /* largeur d'au moins 48rem */
@media (min-width: 48rem) { … }                /* la même chose, écriture classique */
@media (40rem <= width < 64rem) { … }          /* entre deux valeurs */
@media (orientation: landscape) { … }          /* écran plus large que haut */
@media (hover: hover) and (pointer: fine) { … }/* souris : le survol existe */
@media print { … }                             /* à l'impression */
@media (prefers-color-scheme: dark) { … }      /* l'utilisateur a choisi le mode sombre */
@media (prefers-reduced-motion: reduce) { … }  /* l'utilisateur demande moins d'animations */

L'écriture avec comparaisons (width >= 48rem) est reconnue par tous les navigateurs actuels depuis 2023 ; tu croiseras encore très souvent min-width et max-width dans le code existant, qui veulent dire la même chose (min-width: 48rem équivaut à width >= 48rem).

CaractéristiqueUsage
widthla largeur de la fenêtre : la plus utilisée
hover, pointeradapter à un écran tactile : pas d'effet réservé au survol, des cibles plus grandes (24 px au minimum selon les WCAG 2.2, 44 px conseillés)
printmasquer menu et pied de page, afficher l'URL des liens
prefers-color-schemeproposer un thème sombre
prefers-reduced-motioncouper les animations pour les personnes que le mouvement rend malades (chapitre 11)
css
@media print {
  header nav,
  footer,
  .bouton {
    display: none;
  }

  a[href^="http"]::after {
    content: " (" attr(href) ")";   /* imprime l'adresse des liens externes */
  }
}

Des tailles fluides : pourcentages, max-width et clamp()

Une mise en page responsive est d'abord fluide : pas de largeurs fixes en pixels, mais des pourcentages, des fr, des max-width.

css
img,
video {
  max-width: 100%;   /* une image ne dépasse jamais son conteneur */
  height: auto;      /* et garde ses proportions */
}

.contenu {
  width: min(100% - 2rem, 70rem);   /* 70rem au plus, et toujours 1rem d'air de chaque côté */
  margin-inline: auto;
}

La fonction clamp(minimum, idéal, maximum) donne une valeur qui suit l'idéal tout en restant entre deux bornes. Elle rend un titre fluide, sans media query :

css
h1 {
  font-size: clamp(1.75rem, 1.25rem + 2vw, 3rem);
}

Sur un téléphone de 360 px de large, 2vw vaut 7,2 px : 20 + 7,2 = 27,2 px, sous le minimum, donc 28 px (1,75rem). Sur un écran de 1 440 px : 20 + 28,8 = 48,8 px, au-dessus du maximum, donc 48 px (3rem). Entre les deux, la taille grandit progressivement.

Les container queries

Une media query regarde la largeur de la fenêtre. Mais une même carte peut être affichée dans la colonne principale, large, ou dans la colonne latérale, étroite : c'est la largeur de son conteneur qui compte. Les container queries, reconnues par tous les navigateurs actuels depuis 2023, répondent à ce besoin :

css
.zone-cartes {
  container-type: inline-size;   /* ce conteneur peut être interrogé sur sa largeur */
}

.carte {
  display: grid;
  gap: 0.5rem;
}

@container (width >= 30rem) {
  .carte {
    grid-template-columns: 10rem 1fr;   /* image à gauche, texte à droite */
  }
}

La carte passe en deux colonnes dès que sa zone dépasse 30rem, où qu'elle soit placée dans la page. C'est l'outil idéal pour des composants réutilisables.

Images responsives : srcset, sizes et picture

Une photo de 1 600 px de large convient à un grand écran, mais un téléphone qui l'affiche sur 390 px télécharge quatre fois trop de pixels. L'attribut srcset propose plusieurs tailles du même fichier, et le navigateur choisit :

html
<img src="images/facade-800.jpg"
     srcset="images/facade-400.jpg 400w,
             images/facade-800.jpg 800w,
             images/facade-1200.jpg 1200w,
             images/facade-1600.jpg 1600w"
     sizes="(width >= 64rem) 50vw, 100vw"
     alt="Façade vitrée de la médiathèque, entrée de plain-pied"
     width="1600" height="1067">
  • srcset liste les fichiers avec leur largeur réelle en pixels (400w = 400 pixels de large) ;
  • sizes dit au navigateur quelle largeur l'image occupera dans la page : la moitié de la fenêtre (50vw) à partir de 64rem, toute la largeur sinon. Il en a besoin, parce qu'il choisit l'image avant d'avoir calculé la mise en page ;
  • le navigateur multiplie cette largeur par la densité de l'écran (2 ou 3 pixels physiques par pixel CSS sur un téléphone récent) et prend en général le plus petit fichier suffisant ;
  • src reste l'image de secours.

Sur un téléphone de 390 px de large à densité 3, l'image occupe 100vw = 390 px CSS, soit 1 170 pixels physiques : le navigateur prend facade-1200.jpg. Sur un ordinateur de 1 440 px de large à densité 1, elle occupe 720 px : il prend facade-800.jpg.

Pour servir un format moderne avec une solution de secours, ou une image recadrée différemment selon l'écran, on utilise <picture> :

html
<picture>
  <!-- Le navigateur prend la première source qu'il sait lire -->
  <source type="image/avif"
          srcset="images/facade-800.avif 800w, images/facade-1600.avif 1600w"
          sizes="(width >= 64rem) 50vw, 100vw">
  <source type="image/webp"
          srcset="images/facade-800.webp 800w, images/facade-1600.webp 1600w"
          sizes="(width >= 64rem) 50vw, 100vw">
  <img src="images/facade-800.jpg"
       srcset="images/facade-800.jpg 800w, images/facade-1600.jpg 1600w"
       sizes="(width >= 64rem) 50vw, 100vw"
       alt="Façade vitrée de la médiathèque, entrée de plain-pied"
       width="1600" height="1067">
</picture>
html
<!-- Direction artistique : un cadrage serré sur mobile, large sur grand écran -->
<picture>
  <source media="(width >= 48rem)" srcset="images/salle-large.webp">
  <img src="images/salle-serree.webp" alt="La salle de lecture jeunesse" width="800" height="800">
</picture>

Le <img> à l'intérieur de <picture> est obligatoire : c'est lui qui s'affiche, qui porte le alt, les dimensions et le chargement ; les <source> ne font que proposer d'autres fichiers.

WebP, AVIF et chargement différé

Le poids des images est souvent la première cause de lenteur d'une page. Quatre réglages, à appliquer systématiquement :

  1. Le bon format : WebP ou AVIF pour les photos, nettement plus légers que JPEG à qualité visuelle égale, et reconnus par tous les navigateurs actuels ; SVG pour les logos et icônes. Les outils comme Squoosh (en ligne) ou la commande cwebp convertissent les images.
  2. La bonne taille : jamais une image plus large que deux fois sa taille d'affichage maximale.
  3. Les dimensions width et height dans le HTML, avec height: auto en CSS : le navigateur réserve la place, la page ne saute pas pendant le chargement.
  4. Le chargement différé des images situées plus bas dans la page, avec loading="lazy" : elles ne sont téléchargées que quand l'utilisateur fait défiler jusqu'à elles.
html
<!-- Image principale, visible dès l'arrivée : chargée en priorité -->
<img src="images/hero.webp" alt="…" width="1600" height="900" fetchpriority="high">

<!-- Images plus bas dans la page : chargées à la demande -->
<img src="images/atelier-1.webp" alt="…" width="800" height="600" loading="lazy">

Les Core Web Vitals

Google mesure l'expérience réelle des visiteurs de Chrome avec trois indicateurs, les Core Web Vitals (signaux web essentiels). Ils font partie des nombreux signaux de son classement, et surtout ils mesurent ce que ressentent tes utilisateurs.

IndicateurCe qu'il mesureBonMauvaisCauses fréquentes côté HTML et CSS
LCP (Largest Contentful Paint)le temps d'affichage du plus grand élément visible (souvent l'image principale ou le titre)≤ 2,5 s> 4 simage trop lourde, image principale en loading="lazy", police ou CSS qui bloquent l'affichage
INP (Interaction to Next Paint)le délai entre une interaction (clic, touche) et la mise à jour de l'écran≤ 200 ms> 500 msJavaScript trop lourd qui occupe le navigateur
CLS (Cumulative Layout Shift)les décalages inattendus de la mise en page pendant le chargement≤ 0,1> 0,25images sans width et height, bandeau ou publicité insérés au-dessus du contenu, police de secours très différente

Entre les deux seuils, la note est « à améliorer ». Google regarde le 75e centile des visites réelles : il faut que 75 % des visites soient « bonnes » pour que la page le soit. L'INP a remplacé en mars 2024 l'ancien indicateur FID, que tu trouveras encore dans d'anciens cours.

Pour mesurer : Lighthouse (onglet des outils de développement de Chrome et Edge) donne une mesure en laboratoire, sur ta machine ; PageSpeed Insights (pagespeed.web.dev) ajoute les données réelles des visiteurs quand le site en a assez ; la Search Console les suit pour tout un site (chapitre 13).

Tester le responsive

  • Le mode responsive des outils de développement (Ctrl+Maj+M) simule des tailles d'écran et des densités ; il ne remplace pas un essai sur un vrai téléphone, pour le tactile et la lisibilité.
  • Rétrécis la fenêtre à 320 px de large : le contenu doit rester lisible sans défilement horizontal (critère WCAG 1.4.10, « redistribution »). Un défilement horizontal trahit presque toujours une largeur fixe, une image sans max-width, ou un long mot qui ne se coupe pas.
  • Zoome à 200 % (Ctrl et +) : tout doit rester lisible et utilisable, sans texte coupé ni chevauchement.
  • Vérifie les cibles tactiles : un lien ou un bouton trop petit, ou trop collé à son voisin, est difficile à toucher.

Exercices

Exercice 1 sur 4 Mobile first Réussi À revoir

Réécris ce CSS desktop first en mobile first, avec une syntaxe de comparaison.

css
.ateliers {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 1023px) {
  .ateliers { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 639px) {
  .ateliers { grid-template-columns: 1fr; }
}
Voir la réponseMasquer la réponse de l'exercice 1
css
.ateliers {
  display: grid;
  grid-template-columns: 1fr;
}

@media (width >= 40rem) {      /* 640 px */
  .ateliers { grid-template-columns: repeat(2, 1fr); }
}

@media (width >= 64rem) {      /* 1 024 px */
  .ateliers { grid-template-columns: repeat(3, 1fr); }
}

La base est la version téléphone ; chaque palier ajoute une colonne. Les seuils restent les mêmes : max-width: 639px s'applique jusqu'à 639 px, donc la version deux colonnes commence à 640 px, soit 40rem. Avec grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)) (chapitre 9), tu pourrais même te passer de media query.

Tu avais trouvé ?

Exercice 2 sur 4 Quel fichier ? Réussi À revoir

html
<img src="img/atelier-800.webp"
     srcset="img/atelier-480.webp 480w, img/atelier-800.webp 800w,
             img/atelier-1200.webp 1200w, img/atelier-1800.webp 1800w"
     sizes="(width >= 60rem) 33vw, 100vw" alt="…" width="1800" height="1200">

1rem vaut 16 px. Quelle largeur, en pixels CSS, l'image occupe-t-elle, et quel fichier le navigateur choisit-il en général :

  1. sur un téléphone de 360 px de large, densité 2 ?
  2. sur un ordinateur de 1 920 px de large, densité 1 ?
  3. sur un ordinateur portable de 1 280 px de large, densité 2 ?
Voir la réponseMasquer la réponse de l'exercice 2

60rem = 960 px.

  1. 360 < 960 : l'image occupe 100vw = 360 px, soit 720 pixels physiques : le plus petit fichier suffisant est atelier-800.webp.
  2. 1 920 ≥ 960 : 33vw = 0,33 × 1 920 = 633,6 px, soit 633,6 pixels physiques : atelier-800.webp.
  3. 33vw = 0,33 × 1 280 = 422,4 px, soit 844,8 pixels physiques : atelier-1200.webp, le 800 étant juste trop petit.

La spécification laisse au navigateur une marge de choix (il peut prendre un fichier plus petit sur une connexion lente, ou garder un fichier plus grand déjà en cache) : ces réponses sont le comportement habituel, pas une garantie.

Tu avais trouvé ?

Exercice 3 sur 4 Calculer un clamp() Réussi À revoir

font-size: clamp(1.5rem, 1rem + 2.5vw, 2.5rem); avec 1rem = 16 px. Quelle est la taille du texte, en pixels :

  1. sur un écran de 320 px de large ?
  2. sur un écran de 800 px de large ?
  3. sur un écran de 1 600 px de large ?
  4. À partir de quelle largeur d'écran la taille maximale est-elle atteinte ?
Voir la réponseMasquer la réponse de l'exercice 3

Les bornes valent 24 px et 40 px ; la valeur idéale vaut 16 + 0,025 × largeur.

  1. 16 + 8 = 24 px : c'est exactement le minimum, 24 px.
  2. 16 + 20 = 36 px, entre les bornes.
  3. 16 + 40 = 56 px, au-dessus du maximum : 40 px.
  4. 16 + 0,025 × L = 40, donc L = 24 / 0,025 = 960 px.

Tu avais trouvé ?

Exercice 4 sur 4 Diagnostiquer les Core Web Vitals Réussi À revoir

PageSpeed Insights donne ces mesures pour la page d'accueil de la médiathèque : LCP 4,8 s (l'élément LCP est la photo de la façade), CLS 0,31, INP 120 ms. Le HTML contient <img src="images/facade.png" alt="…" loading="lazy">, et la photo PNG pèse 3,2 Mo pour 4 000 pixels de large.

  1. Classe chaque indicateur : bon, à améliorer ou mauvais.
  2. Propose quatre corrections dans le HTML et le traitement de l'image.
Voir la réponseMasquer la réponse de l'exercice 4
  1. LCP 4,8 s : mauvais (au-delà de 4 s). CLS 0,31 : mauvais (au-delà de 0,25). INP 120 ms : bon (sous 200 ms).
  2. Corrections :
    • retirer loading="lazy" de cette image, visible dès l'arrivée, et ajouter fetchpriority="high" ;
    • convertir la photo en WebP ou AVIF : une photo n'a rien à faire en PNG ;
    • la redimensionner et proposer plusieurs tailles avec srcset et sizes (par exemple 800, 1 200 et 1 600 px de large) ;
    • ajouter width et height (dans les proportions de l'image), avec height: auto en CSS : le navigateur réserve la place, ce qui corrige le décalage de mise en page (CLS).

Tu avais trouvé ?

Chapitre terminé !

Fais les 4 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : CSS moderne : variables, nesting, :has() et animationsRevoir le sommaire du cours