Un site qui tient
sur tous les écrans
Le convertisseur ne cible aucun appareil nommé. Il décrit des classes d'écran — lues dans un fichier JSON, donc modifiables sans toucher au code — et refuse de produire une page qui déborde, se chevauche ou devient inatteignable au doigt.
On décrit une contrainte, pas un appareil
Un site ne peut pas être conçu pour des téléphones nommés : la liste change chaque année, et la queue de distribution est longue — aucun marché ne dépasse 37 % cumulés sur ses deux résolutions les plus fréquentes.
Le CMS décrit donc des contraintes — peu de largeur, peu de hauteur — et y répond. Un appareil sorti demain tombera dans une classe existante.
La recommandation est explicite, elle n'est pas une opinion. Google, sur web.dev :
« Don't define breakpoints based on device classes, or any product, brand name, or operating system. This makes your code difficult to maintain. »
Pourquoi un fichier JSON et pas des valeurs dans le CSS
Les ruptures ne sont pas écrites dans la feuille de style. Elle porte des jalons — {{RUPTURE:large}} — que le convertisseur substitue depuis noyau/responsive.json.
La raison est concrète : une valeur en dur dans le CSS et une valeur dans le JSON divergeraient au premier ajustement, et le JSON deviendrait une documentation qui ment. Ici, modifier le JSON modifie réellement la mise en page.
Un jalon inconnu arrête la compilation (RSP-008) : sans ce contrôle, @media (min-width: ) serait une requête invalide que le navigateur ignore sans rien dire, et toute la mise en page d'une classe d'écran disparaîtrait en silence.
4 classes de largeur, 1 de hauteur
Chaque classe porte ses résolutions observées et leur part de marché réelle. Ajouter une classe au JSON suffit : le convertisseur l'intègre sans modification de code.
Les valeurs sont en em et non en pixels — une rupture en pixels ne suivrait pas la taille de police choisie par le lecteur.
| Clé | Classe | À partir de | Résolutions observées | Part CH |
|---|---|---|---|---|
| compact | Téléphone | 0em 0px | 414x896 · 390x844 · 393x852 | 25,33 % |
| moyen | Tablette portrait | 48em 768px | 768x1024 · 820x1180 · 810x1080 | 10,32 % |
| large | Tablette paysage & petit ordinateur | 64em 1024px | 1280x800 · 800x1280 | 5,43 % |
| etendu | Ordinateur de bureau | 80em 1280px | 1920x1080 · 2560x1440 · 1536x864 | 22,07 % |
414 × 896 est la résolution la plus fréquente de Suisse — 25,33 % du trafic mobile. C'est précisément celle où un débordement de 17 px avait été signalé, et corrigé.
Source : StatCounter, juillet 2026
Le téléphone en paysage
C'est le cas que l'on oublie. Un téléphone tourné offre moins de 400 px de hauteur, et environ 330 px réellement visibles une fois la barre du navigateur déduite.
La hauteur y est la ressource rare, pas la largeur.
99,78 % des téléphones en paysage sont sous 480dp de haut.
Source : Google — Window size classes
Portrait 414 × 896 Paysage 896 × 414
− 72 px de menu fixe (17 %)
− ~64 px de barre du navigateur
═══════════
~278 px de contenu visible
Pourquoi le CMS interroge la HAUTEUR et jamais l'orientation
C'est le point le plus contre-intuitif de cette page.
orientation: landscape est un simple test largeur > hauteur. Il est donc vrai pour un téléphone à 852 × 393 comme pour un écran de bureau à 2560 × 1440 : une seule règle recouvrirait le cas le plus contraint et le plus confortable.
Pire, MDN documente un piège :
« Opening the soft keyboard on many devices in portrait orientation will cause the viewport to become wider than it is tall, thereby causing the browser to use landscape styles instead of portrait. »
Un formulaire qui se réorganiserait en orientation: landscape se réagencerait sous les doigts du visiteur pendant qu'il tape.
@media (max-height: 30em) exprime la contrainte réelle — le manque de hauteur — sans dépendre d'une inférence sur la position de l'appareil.
Les unités de hauteur : pourquoi svh et pas vh ni dvh
vh équivaut à lvh : il vaut la hauteur barre masquée, la plus grande possible. Au chargement, barre visible, un bloc en 100vh est structurellement plus haut que l'espace disponible. Ce n'est pas un défaut de navigateur, c'est le comportement spécifié.
dvh suit la barre en temps réel : le contenu se redimensionne pendant le défilement. MDN le décrit comme « degrading UX and causing performance hits » — et cela dégrade aussi le CLS.
svh est stable et garantit que le contenu tient. Support : 94,09 %.
min-height: 100vh; /* repli pour les moteurs anciens */
min-height: 100svh; /* le moteur qui connaît svh écrase */Ce que le moteur refuse de produire
Ces règles ne sont pas des conseils. Leur violation arrête la compilation — le site n'est pas publié.
Elles portent des codes de la famille RSP, documentés en détail au journal des erreurs.
RSP-001 — Aucun élément ne dépasse la largeur du viewport, à aucune classe d'écran.
RSP-002 — Tout enfant de grille ou de boîte flexible doit neutraliser « min-width:auto ».
Par défaut, un élément de grille ne peut pas devenir plus étroit que son contenu le plus long. Un mot long — fréquent en allemand — impose alors une piste plus large que le conteneur, et la page déborde. Le défaut est invisible en français et apparaît à la traduction.
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Grid_layout_and_overflow
RSP-003 — Toute cible tactile mesure au moins 24 x 24 px.
RSP-004 — Aucun bloc ne réclame 100 % de la hauteur quand celle-ci est sous 480 px.
Le défaut le plus instructif est RSP-002. Par défaut, un élément de grille ne peut pas devenir plus étroit que son mot le plus long. Ce mot impose alors une piste plus large que le conteneur, et la page déborde.
Mesuré avant correction sur ce site : 91 conteneurs sur 97 y étaient exposés. Le défaut est invisible en français et se déclare à la traduction — il suffit d'un composé allemand.
Ces défauts ne se voient pas dans le code
Trois familles de défauts échappent à toute relecture. Chacune exige une mesure différente, dans un vrai moteur de rendu.
C'est la raison d'être de la sonde tests/sonde_responsive.js.
| Défaut | Comment il se mesure | Trouvé ainsi |
|---|---|---|
| Débordement | rect.right > clientWidth | 17 px à 414×896, piste de grille |
| Chevauchement | boîtes comparées deux à deux | logo sur texte, 59×18 px en paysage |
| Défaut masqué | ouvrir tous les <details> | menu de langue, 41 px, invisible fermé |
Deux pièges de méthode, rencontrés pour de vrai
1. Redimensionner la fenêtre ne suffit pas. Demander 414 px de large donnait un viewport de 598 px : la fenêtre du navigateur absorbe le redimensionnement. Il faut émuler un appareil.
2. Un serveur local qui ne compresse pas fausse tout. Le même CSS pesait 47 Ko en clair contre 12,8 Ko en brotli, et le LCP affichait 1 498 ms au lieu de 717 ms. Sans cette correction, on optimiserait un problème inexistant.
Et il faut regarder l'écran, pas seulement les chiffres. Une règle corrigeant le débordement coupait l'allemand en plein mot — « SCHENK / EN SIE ». Aucune mesure ne le signalait ; une capture d'écran l'a montré immédiatement. Corrigé par hyphens: auto, qui coupe aux syllabes selon la langue déclarée : « SCHEN-KEN ».