Pourquoi vous ne devriez jamais utiliser px pour définir la taille de police dans CSS
Comment utiliser les unités px, rem et em en connaissance de cause, et prendre en compte les configurations de l'utilisateur.
Pardonnez le titre quelque peu putaclic... "jamais" est un mot fort, mais il s'agit d'un sujet important et mal compris.
Il circule un certain nombre d'idées fausses dans la sphère du développement Web, qui persistent alors même qu'elles sont régulièrement démenties. "Les liens externes doivent toujours s'ouvrir dans de nouveaux onglets" en est un bon exemple. CSS Tricks en a parlé en détail il y a près de dix ans (tl;dr : la plupart du temps faux), mais cette idée semble encore persister dans certains coins.
Exemple concret : l'idée qu'il n'y a pas de différence fonctionnelle entre les unités px
, et em
ou rem
en CSS. C'est une idée fausse que j'entends encore et encore, alors je me suis dit que j'allais l'aborder ici, dans un billet.
Soyons clairs : l'unité que vous utilisez dans votre CSS est absolument importante. Et vous devez éviter les px
lorsque vous définissez la taille de la police, dans la mesure du possible.
Encore une fois : "toujours" est un mot fort, et un peu dogmatique. Mais la vérité est la suivante : si vous choisissez la mauvaise unité, vous risquez d'écraser les préférences de vos utilisateurs, de leur rendre l'utilisation de votre site Web plus difficile (voire impossible) et de nuire au design par des effets secondaires visuels involontaires.
De quelles unités parlons-nous et que font-elles?
Avant d'aborder les raisons pour lesquelles nous devrions éviter d'utiliser px
pour la taille de la police, assurons-nous que nous avons bien compris de quelles unités il s'agit et comment elles se comportent en général.
px
px
est l'abréviation de pixel... bien qu'il ne s'agisse plus littéralement d'un pixel la plupart du temps. Il l'était autrefois, à l'époque où les écrans avaient tendance à avoir un ratio de pixels assez prévisible et de faible résolution, comme 1024×768. À cette époque, 1px
était généralement égal à un pixel réel sur l'écran physique.
Les écrans affichent les images à l'aide d'une grille de lumières colorées appelées pixels. Un pixel est une lumière colorée sur l'écran ; le plus petit "point" possible que le matériel est capable de restituer. C'est ce que j'entends par pixel "littéral", "réel" ou "périphérique" dans cette section ; un pixel dans le monde physique.
Cependant, lorsque les écrans haute résolution (parfois appelés "rétina") sont apparus et que les appareils ont commencé à contenir plus de pixels dans moins d'espace, ces pixels physiques sont devenus super petits. En naviguant sur le Web sur un écran haute résolution, il aurait été extrêmement difficile de lire quoi que ce soit si 1px en CSS correspondait encore à un pixel de périphérique littéral, car les pixels eux-mêmes se sont rapidement réduits. Les smartphones modernes ont des résolutions encore plus élevées que les téléviseurs HD, après tout.
Au lieu de cela, les navigateurs sur les écrans haute résolution mettent à l'échelle ce qu'ils affichent (zoom avant, plus ou moins), afin que les pages Web ne soient pas illisibles. Les images et autres éléments peuvent toujours être affichés dans toute leur gloire haute résolution, mais le texte et les autres éléments sont mis à l'échelle pour maintenir la lisibilité.
Ainsi, de nos jours, 1px
correspond généralement à la taille d'un pixel agrandi, "zoomé", plutôt qu'à un pixel littéral sur le matériel réel. Un élément de 1px
dans notre CSS occupera probablement plusieurs pixels physiques du matériel, et nous n'avons pas vraiment de moyen dans le CSS pur de spécifier un pixel littéral du périphérique. Mais ce n'est pas grave, car ils sont généralement trop petits pour que nous ayons envie de nous en occuper.
Un exemple : les pixels de l'iPhone 14 Pro sont si microscopiques que 16px, en pixels de périphérique littéral, correspondraient à la taille d'une police d'écriture imprimée de `2pt`. Heureusement que les navigateurs les mettent à l'échelle pour nous !
La plupart de ces éléments ne sont pas vraiment importants dans le contexte de cette discussion, mais je pense qu'il est bon de les connaître quand même. La partie importante est la suivante : 1px
est égal à ce que le navigateur traite comme un pixel unique (même si ce n'est pas littéralement un pixel sur l'écran matériel).
em et rem
Cela nous amène à em
et rem
, qui sont similaires l'un à l'autre. Pour continuer avec les anecdotes non strictement pertinentes mais néanmoins intéressantes : "em" est un terme typographique qui précède les ordinateurs de plusieurs décennies. Typographiquement, un em
est égal à la taille courante de la police (NdT : c'est à dire relatif à la taille de police de son élément parent).
Si la taille de votre police est réglée sur 32 pt
("pt" signifie point, un autre terme typographique ancien encore parfois utilisé), alors 1em correspond à 32 pt
. Si la taille actuelle de la police est de 20px
, alors 1em
= 20px
(mais encore une fois : vous ne devriez pas définir les tailles de police en pixels — ceci est juste pour l'exemple).
Sur le Web, la taille de police par défaut est de 16px
. Certains utilisateurs ne changent jamais cette valeur par défaut, mais beaucoup le font. Mais par défaut, au moins, 1em
et 1rem
seront tous deux égaux à 16px
.
À l'origine, Em faisait référence à la largeur d'un caractère "M", d'où son nom. Mais il fait désormais référence à la taille actuelle de la police, plutôt qu'aux dimensions d'un glyphe spécifique.
La différence entre em et rem
Pour différencier les deux : 1rem
est toujours égal à la taille de la police du navigateur — ou, plus précisément, à la taille de la police de l'élément html
. rem
signifie "root em", et la racine d'une page Web est la balise <html>
. Donc, 1rem
= la taille de la police du document, quelle qu'elle soit. (Qui, encore une fois, par défaut, est de 16px
, mais peut être modifiée par l'utilisateur).
em
, on l'a vu, est la taille de la police de l'élément courant. Prenez le CSS suivant :
.container {
font-size: 200%;
}
p {
font-size: 1em;
}
Avec le CSS ci-dessus, les paragraphes à l'intérieur de l'élément .container
seraient deux fois plus grands. En effet, 1em
signifie "la taille actuelle de la police", et à l'intérieur de l'élément .container
, cette taille est de 200 %. 1em
× 200% = 2em
(32px
par défaut).
Toutefois, les paragraphes situés à l'extérieur de l'élément .container
conservent la taille de police normale de 1em
(16px
par défaut).
Si nous remplaçons em
par rem
dans le CSS ci-dessus, toutes les balises de paragraphe auront toujours la taille de police par défaut du navigateur, où qu'elles se trouvent.
`font-size : 1em` est équivalent à `font-size : 100%`.
Les unités `em` et `%` ne sont pas toujours équivalentes dans d'autres contextes. Par exemple, `width : 1em` et `width : 100%` seraient très probablement bien différents, puisque dans ce cas, le pourcentage est basé sur la largeur du conteneur parent, et non sur sa taille de police. Mais `%` et `em` sont identiques en ce qui concerne la propriété font-size.
Donc, pour résumer :
1em
est la taille de la police de l'élément actuel1rem
(root em) est la taille de la police du document (c'est-à-dire celle du navigateur)
Très bien ; voilà ce que signifient les unités et d'où elles viennent. Répondons maintenant à la question de savoir pourquoi il est important de savoir laquelle nous utilisons.
Pourquoi tout cela est important
Une fois encore, l'idée fausse la plus répandue est la suivante : puisque 1em
est de toute façon égal à 16px
, l'unité choisie n'a pas d'importance. Et cela semble logique ; si 16px
= 1rem
, alors pourquoi la façon dont vous choisissez de le taper aurait-elle de l'importance ?
Rappelez-vous, em
et rem
sont relatifs ; par défaut, ils sont tous deux (en fin de compte) basés sur la taille de la police du navigateur.
En revanche, px
ne l'est pas ; il s'agit d'une valeur statique qui n'est pas basée sur ou affectée par quoi que ce soit d'autre.
2rem
correspond au double de la taille de la police du navigateur ; 0.5rem
correspond à la moitié de celle-ci, et ainsi de suite. Ainsi, si l'utilisateur modifie sa taille de police préférée (NdT : p.ex. en modifiant les réglages de son navigateur), si vous avez utilisé em
et rem
, tout le texte de votre site Web sera modifié en conséquence, comme il se doit. 2rem
est toujours le double de cette taille de police ; 0.5rem
est toujours la moitié de celle-ci.
En revanche, les valeurs px
sont statiques. 20px
reste 20px
, quelle que soit la taille de la police du conteneur, du navigateur ou de l'utilisateur. Quelle que soit la taille de la police préférée de l'utilisateur, lorsque vous définissez une valeur en pixels statiques, elle écrase ce choix et le remplace par la valeur exacte que vous avez spécifiée.
Cela signifie que si votre feuille de style utilise px
pour définir la taille de la police, il sera impossible pour l'utilisateur de modifier tout texte basé sur cette valeur.
em
et rem
fonctionnent avec la taille de police de l'utilisateur ; px
la remplace complètement.
C'est une très mauvaise chose. C'est inaccessible, et cela peut même empêcher quelqu'un d'utiliser le site tout court.
Ainsi, bien que ce comportement puisse être utilisé dans certains cas valables, ce n'est certainement pas ce que vous voulez comme valeur par défaut.
C'est également une très bonne raison d'éviter les unités de visualisation, comme `vw` ou `vh`, lors de la définition de la taille de la police. Ces unités sont également statiques et impossibles à modifier par l'utilisateur.
Tout au plus, une valeur comme `calc(1rem + 1vw)` pourrait être acceptable, car elle contient toujours `rem` comme base. Mais même dans ce cas, je vous recommande d'utiliser `clamp()` ou `media queries` pour définir les valeurs minimales et maximales, car les tailles d'écran vont souvent bien au-delà de ce que nous pouvons attendre ou tester.
Différences au-delà de la taille de la police
Ok, parlons maintenant de la façon dont px
et em
/rem
varient même lorsque nous ne traitons pas spécifiquement de la propriété font-size
.
Les développeurs testent généralement en effectuant un zoom avant ou arrière sur la page, et je pense que c'est de là que vient l'idée fausse au cœur de ce billet. Lorsque vous effectuez un zoom, tout est redimensionné (ou réduit) et, dans ce scénario, le choix de px
ou de em
/rem
comme unité CSS n'a généralement pas d'importance. Les deux se comportent de la même manière, en ce qui concerne le zoom. Et la plupart des développeurs privilégiés ayant une bonne vue ne se rendront probablement pas compte qu'il y a plus que cela. Cependant, la chose délicate est la suivante :
Même au-delà de la taille de la police, px ne se comporte pas de la même manière que em
et rem
.
Les unités px
sont toujours liées à la valeur mise à l'échelle des pixels sur l'écran. em
et rem
, en revanche, sont liés à la taille de la police du document, et non au zoom ou à l'échelle de la page.
Pour le démontrer, jetez un coup d'œil à ce CodePen :
Nous avons quelques paragraphes, chacun avec une bordure de 2px
en bas, et une marge de 20px
entre eux. Remarquez que nous utilisons des unités px
pour les deux.
Si vous effectuez un zoom avant ou arrière, la taille et la distance des éléments restent relatives. En d'autres termes, plus vous effectuez un zoom avant, plus la ligne devient épaisse et plus l'espace entre les paragraphes est grand.
Pour vous épargner la peine, voici une capture d'écran, montrant ce même codepen avec un zoom de 400%
. Le texte, la ligne et l'espacement sont tous 4 fois plus grands ; ils restent de la même taille les uns par rapport aux autres :
Lorsqu'il s'agit de faire un zoom avant ou arrière, il n'y a pas de réelle différence entre px
et em
ou rem
. Mais le zoom n'est pas le seul moyen pour les utilisateurs de rendre les sites Web plus utilisables.
Comme mentionné précédemment, les utilisateurs peuvent également spécifier une taille de police par défaut et/ou minimale. Et lorsqu'ils le font, c'est là que la fonctionnalité commence à diverger.
Dans la capture d'écran ci-dessous, j'ai défini la taille de police par défaut de Firefox à 64px
. Voilà ce que ça donne :
Comparez le texte de cette capture d'écran avec le texte de celle qui la précède. Remarquez comment, cette fois, les lignes ne sont pas plus épaisses, et les marges entre les paragraphes n'ont pas augmenté proportionnellement. Seul le texte lui-même est plus grand. Comme la largeur de la bordure et la marge ont toutes deux été définies en px
, elles restent telles quelles et ne sont pas mises à l'échelle.
Vous remarquerez toutefois que si vous remplacez les px
dans le CSS par les valeurs rem
équivalentes, les lignes et les marges s'agrandissent !
Tout cela signifie que le choix de px
plutôt que em
et rem
, ou l'inverse, répond à des motivations de design.
Donc, pour résumer,
- les valeurs
px
n'augmentent ni ne diminuent pas lorsque l'utilisateur modifie la taille de sa police de caractères, - Les valeurs
em
etrem
s'ajustent proportionnellement à la taille de la police.
Si vous souhaitez une démonstration interactive qui relie tout cela, consultez ce CodePen final ; ajustez le curseur en haut pour voir l'effet de la modification de la taille de la police du document sur divers éléments, en fonction de l'unité CSS qu'ils utilisent.
Lequel choisir ?
Donc, sachant que les valeurs em
et rem
s'échelonnent en fonction de la taille de la police, mais pas les valeurs px
, où cela nous mène-t-il ? Devrions-nous simplement ne jamais utiliser les px
pour quoi que ce soit ?
Même si je pense que vous pouvez suivre cette voie si vous le souhaitez, je pense qu'il y a une place pour les valeurs px
.
Puisque nous savons que les valeurs px
ne changeront pas lorsque et si l'utilisateur ajuste sa taille de police, cela signifie que les unités de pixels sont en fait un excellent choix pour certains éléments esthétiques. Peut-être avons-nous un certain espacement que nous ne voudrions pas voir s'agrandir lorsque la taille de la police est plus grande. (S'il s'agit d'un grand bloc d'espace négatif par défaut, il ne serait peut-être pas judicieux de lui permettre de passer à une taille encore plus massive).
Peut-être y a-t-il des tailles de bordure que nous ne voudrions pas modifier, ou peut-être y a-t-il des éléments décoratifs sur la page, créés avec CSS, qui ne sembleraient pas bien fonctionner à une taille plus grande, juste parce que la police est plus grande. Peut-être ne voulons-nous pas que le remplissage augmente avec la taille de la police. px
reste un bon choix dans tous ces cas.
Personnellement, je recommande de définir toutes les tailles en utilisant rem
. Je n'utilise em
que lorsque je veux que quelque chose soit proportionnel à la taille courante de la police (par exemple, une icône à côté d'un texte qui doit avoir exactement la même hauteur que les caractères, et un demi-caractère sur le côté). Je n'utiliserais pas px
n'importe où, sauf pour les éléments de design que je ne voulais pas explicitement mettre à l'échelle avec la taille de la police.
Mais une fois de plus, si vous retenez quelque chose de ce billet :
Ne définissez jamais la taille de la police en unités px
— du moins, pas à moins d'être absolument sûr de ce que vous faites, de son comportement et de son accessibilité.
Une note importante sur les media queries
Il s'agit d'un addendum tardif à cet article, mais il est important d'éviter les px
dans les requêtes media (media queries) pour les mêmes raisons que celles évoquées ci-dessus ; cela fonctionnera bien lorsque l'utilisateur zoome, mais une requête media qui utilise les px
échouera lorsque les utilisateurs définiront eux-mêmes une taille de police plus grande.
@media (min-width: 800px) {
/* Modifier la taille de police n'affectera pas ce breakpoint */
}
@media (min-width: 50rem) {
/* Modifier la taille de police affectera ce breakpoint */
}
En effet, lorsque la taille de la police augmente, 50rem
devient une valeur différente en fonction de cette préférence, alors que 800px
ne le devient pas.
La plupart du temps, lorsque nous écrivons du CSS pour des points de rupture (breakpoints) plus grands, nous partons du principe qu'il y a suffisamment de surface d'écran pour que les éléments puissent s'étendre. Cela peut ne pas être le cas si l'utilisateur a défini une très grande taille de police, et le fait de définir nos requêtes média en rem
plutôt qu'en px
nous permet d'éviter cette hypothèse et de répondre aux préférences de l'utilisateur.
J'ai rencontré ce problème sur mon site ; je définissais tous mes points de rupture en px
. Cependant, lorsque j'ai augmenté la taille de la police par défaut, mes requêtes média n'ont pas répondu, car elles ne tenaient compte que de la largeur en pixels de l'écran. J'avais donc toujours une minuscule barre latérale, avec un texte énorme écrasé de façon illisible à l'intérieur, puisque je n'avais pas tenu compte des préférences des utilisateurs. J'ai changé pour rem
immédiatement après, et cela a résolu le problème.
En résumé, évitez d'utiliser px
dans les requêtes média, sauf si vous êtes sûr de savoir ce que vous faites et quel effet cela aura sur les utilisateurs qui définissent leur propre taille de police dans le navigateur.
Un dernier mot sur l'accessibilité
Les directives d'accessibilité stipulent généralement que les applications Web doivent être utilisables jusqu'à 200 % de zoom et/ou de taille de police. Vous remarquerez que les démos ci-dessus vont bien au-delà.
C'est en partie pour le contraste, mais il est important de noter que de nombreux utilisateurs iront bien au-delà également, en fonction de leurs besoins et de leurs préférences. Une personne ayant une mauvaise vue ne s'arrêtera pas simplement à 200 % si cela ne lui convient pas. Nous devrions faire de notre mieux pour les inclure, en construisant nos sites et nos applications pour qu'ils soient lisibles et utilisables quoi qu'il arrive.
J'ai entendu certains spécialistes de la technologie affirmer que leurs utilisateurs sont jeunes et/ou férus de technologie, et que les personnes qui ont besoin d'une grande taille de police ne font pas partie de leur public et ne doivent donc pas être prises en compte. Mais j'aimerais insister sur le fait que c'est faux, présomptueux, et franchement, handicapant.
16px
est juste une valeur par défaut. Ce n'est pas une norme, ce n'est pas une cible, et ça ne devrait pas être une supposition.
Même en mettant de côté l'âgisme inhérent à cet argument, les besoins varient même chez les jeunes. L'invalidité n'a pas de seuil d'âge. Je connais personnellement une personne d'une trentaine d'années, employée comme développeur à plein temps (c'est-à-dire très jeune et très douée pour la technologie), qui doit définir une taille de police minimale nettement supérieure à la valeur par défaut à cause de sa vision. Les sites Web et les applications sont régulièrement cassés pour eux, parfois au point d'être inutilisables, parce que les développeurs ne tiennent pas compte de leurs besoins.
Je rappelle tout cela pour souligner l'importance de tester les UI comme le feraient nos utilisateurs. Ci-dessus, j'ai utilisé 64px
, et il est vrai que cela semble être une sélection assez extrême, étant quatre fois plus grande que la valeur par défaut... mais encore une fois, et si ce n'était pas le cas ? Et si un utilisateur en avait vraiment besoin ? Dire, de fait, à un utilisateur "tu as besoin de trop" n'est pas quelque chose que nous souhaitons faire.
Il y aura toujours des limites pratiques à ce que nous pouvons accommoder, bien sûr. Mais quoi qu'il en soit, nous devrions au moins aller aussi loin que possible. Même si le taux de 4 fois n'est pas toujours réalisable, il existe de nombreuses options possibles entre 100 et 400 % que les utilisateurs pourraient choisir de manière réaliste et sur lesquelles ils pourraient compter, et nous devrions faire de notre mieux pour ne pas les gêner. À défaut, ce que nous construisons sur le Web devrait au moins être suffisamment flexible pour aller bien au-delà des valeurs par défaut.
16px
n'est qu'un défaut. Ce n'est pas une norme, ce n'est pas un objectif, et cela ne devrait pas être une hypothèse. Nous pouvons, et devons, faire mieux.
NdT : voir également l'excellent article de Josh Comeau (en anglais) sur le même sujet.