CSS Units : Pourquoi remplacer px par ch améliore la lisibilité de vos sites

Le pixel est un mensonge
Les pixels n'existent pas. Pas vraiment, du moins pas comme on les imagine.
Même quand vous croyez dessiner avec une "précision pixel-perfect", votre écran vous ment. Il n'y a pas de grille parfaite de carrés platoniques sur un écran — les pixels CSS ne correspondent pas aux pixels physiques du matériel.
Comme le rappelle la documentation MDN sur les unités CSS : 1px ne correspond pas nécessairement à un pixel physique de l'écran. Sur un écran HD (Retina, 4K), un pixel CSS peut couvrir plusieurs pixels physiques. Et 1cm en CSS ne correspond pas à un centimètre SI non plus.
"Les longueurs sont perceptuelles : 16px a la même taille apparente sur un téléphone, un laptop ou une TV à distance de visionnage typique." — MDN
C'est là que l'unité ch entre en jeu.
L'unité ch : la largeur du "0"
L'unité ch (character width) est définie comme la largeur du glyphe "0" (zéro) dans la police et la taille de fonte courantes.
/* La largeur du conteneur principal est de 75 caractères "0" */
--width-content: min(75ch, 100%);
main {
max-width: var(--width-content);
}
Dans une police monospace (Consolas, Monaco, Fira Code), tous les caractères ont la même largeur — 1ch = 1 caractère. Dans une police proportionnelle (Inter, Roboto, Georgia), la largeur varie, mais ch reste une référence stable, liée à la police réelle utilisée.
Eric Meyer l'expliquait dès 2018 : contrairement à une idée reçue, ch n'est pas une unité "character" générique — elle ne mesure que le "0". Mais c'est amplement suffisant pour dimensionner des conteneurs de texte.
Pourquoi c'est un game-changer pour la lisibilité
La règle des 60-75 caractères
Les études en lisibilité montrent que l'œil humain lit confortablement des lignes de 60 à 75 caractères. Au-delà, le retour à la ligne fatigue ; en deçà, la lecture devient saccadée.
Avec px, vous fixez une largeur arbitraire qui ne tient pas compte de la police :
/* ❌ Approche pixel : la largeur est figée */
main {
max-width: 720px;
}
Avec ch, la largeur s'adapte à votre typographie :
/* ✅ Approche ch : la largeur suit la police */
main {
max-width: 75ch;
}
Changez de police, augmentez la taille de la fonte, passez en italique — le conteneur s'ajuste automatiquement pour maintenir le confort de lecture.
Des espacements proportionnels
Le même principe s'applique aux marges et paddings autour du texte :
/* ❌ Padding fixe */
.card {
padding: 24px 32px;
}
/* ✅ Padding proportionnel à la typographie */
.card {
padding: 2ex 2ch;
}
C'est ce que fait Terence Eden sur son blog (shkspr.mobi), qui a popularisé l'approche. Son constat : "Je veux que l'espace autour d'un élément soit proportionnel au texte qu'il contient."
Au-delà de ch : les autres unités typographiques CSS
ex — la hauteur des bas-de-casse
L'unité ex correspond à la hauteur du caractère "x" (minuscule) dans la police courante. Elle est utile pour les dimensions verticales liées au texte :
blockquote {
padding: 1ex 2ch;
/* Hauteur proportionnelle aux bas-de-casse */
}
lh — la hauteur de ligne
L'unité lh (line height) est peut-être encore plus pertinente pour les espacements verticaux, comme le souligne Adam dans les commentaires de l'article original :
.card {
padding: 1lh 2ch;
/* Espacement vertical = une ligne de texte */
}
Tableau comparatif des unités CSS
| Unité | Référence | Usage recommandé | Exemple |
|---|---|---|---|
px | Pixel CSS (perceptuel) | Bordures, ombres, dégradés | border: 1px solid |
ch | Largeur du "0" | Largeurs de conteneurs, padding horizontal | max-width: 75ch |
ex | Hauteur du "x" | Hauteurs, espacements verticaux | padding: 1ex 0 |
lh | Hauteur de ligne | Espacements verticaux, grilles | margin-top: 1lh |
rem | Taille de la racine (html) | Tailles de police globales | font-size: 1.25rem |
em | Taille du parent | Tailles relatives au contexte | padding: 1em |
Les pièges à connaître
1. Layout shifts avec les web fonts
Tyler Sticka (Cloud Four) a documenté un problème avec les ch units et les web fonts. Si la police de caractères n'est pas chargée au moment du rendu, le navigateur utilise une police de secours — dont le "0" a une largeur différente.
Quand la police se charge (FOIT — Flash of Invisible Text, ou FOUT — Flash of Unstyled Text), la largeur du conteneur change, causant un Cumulative Layout Shift (CLS). Solution :
/* ✅ Pré-charger la police et utiliser font-display: swap */
@font-face {
font-family: 'Inter';
font-display: swap; /* Affiche la police de secours, puis swap */
}
Ou utiliser size-adjust dans @font-face pour harmoniser les métriques.
2. ch n'est pas une unité "character"
Eric Meyer le rappelle : ch mesure le "0", pas la largeur moyenne des caractères. Dans une police comme "Inter" (proportionnelle), 20ch peut contenir 20 à 35 caractères selon les lettres utilisées. Pour un comptage exact, il faut utiliser une police monospace.
3. Pas adapté aux hauteurs
Utiliser ch pour les hauteurs n'a pas de sens — c'est une unité horizontale. Pour la verticale, préférez ex, lh, ou em.
Mise en pratique : un template typographique complet
:root {
/* Base typographique */
--font-body: 'Inter', system-ui, sans-serif;
--font-size: 1rem;
--line-height: 1.6;
/* Conteneur principal */
--content-width: min(75ch, 100% - 4ch);
--content-padding: 2ch;
}
body {
font-family: var(--font-body);
font-size: var(--font-size);
line-height: var(--line-height);
}
main {
max-width: var(--content-width);
margin-inline: auto;
padding: var(--content-padding);
}
/* Cartes et sections */
.card {
padding: 1.5lh 2ch;
margin-block: 1lh;
}
/* Citations */
blockquote {
padding-inline: 2ch;
margin-inline: 0;
border-inline-start: 0.25ch solid currentColor;
}
/* Code */
pre {
padding: 1.5lh 2ch;
font-size: 0.9em; /* Réduit légèrement pour le code */
}
La philosophie : déléguer au navigateur
Le passage de px à ch s'inscrit dans une tendance plus large du CSS moderne : déléguer les calculs au navigateur.
min(),max(),clamp()remplacent les media querieschetlhremplacent les largeurs et espacements en durcontainer queriesremplacent les breakpoints globauxaspect-ratioremplace les calculs de ratio
Moins de code, plus de robustesse, une meilleure accessibilité.
Conclusion
L'unité ch n'est pas une solution miracle — elle ne remplacera pas tous vos px. Mais pour tout ce qui touche à la largeur du texte (conteneurs, padding horizontal, tailles de colonnes, espaces entre les éléments textuels), c'est l'outil le plus pertinent que vous puissiez utiliser.
Combineée avec ex ou lh pour la verticale, et clamp() pour les tailles de police fluides, vous obtenez une typographie qui s'adapte à :
- La police utilisée (changez-la, le layout suit)
- La taille de police de l'utilisateur (zoom, préférences d'accessibilité)
- La largeur de l'écran (sans une seule media query)
Checklist pour migrer vos projets
- Remplacer
max-width: 720pxparmax-width: min(75ch, 100%) - Utiliser
chpour les paddings horizontaux des conteneurs de texte - Utiliser
lhouexpour les espacements verticaux - Pré-charger les web fonts avec
font-display: swap - Tester le CLS avec Lighthouse après migration
- Vérifier le rendu avec zoom à 200% (accessibilité)
Le pixel était une commodité pour les écrans fixes. L'ère du responsive, des web fonts et de l'accessibilité exige des outils plus intelligents. ch en fait partie.


