De 66 % à 185 px : comment les navigateurs résolvent les valeurs CSS avant le rendu
Suivez une valeur CSS à travers les étapes déclarée, en cascade, spécifiée, calculée, utilisée et réelle, et comprenez pourquoi les unités relatives ainsi que la technique de conversion rem se comportent de cette manière.
Gagner la cascade n’est pas la fin du parcours d’une déclaration CSS. Même si width: 50% l’emporte sur toutes les règles concurrentes, le navigateur ne peut pas encore transmettre 50% à son moteur de mise en page ; il doit déterminer ce que représente cinquante pour cent dans le contexte actuel et le transformer en une taille concrète. Ce guide suit une seule valeur à travers chaque étape de traitement définie par la spécification, montre où les unités relatives telles que rem et les pourcentages sont résolues, et explique le truc populaire de conversion px en rem ainsi que ses limites en termes d’accessibilité.
La valeur que vous écrivez n’est pas celle qui est utilisée
Beaucoup de ce que vous saisissez dans un fichier de style est une relation plutôt qu’une mesure. Une largeur peut représenter une part de la largeur du parent :
width: 50%;
ou, dans une autre règle, une part différente :
width: 66%;
La taille de police peut être relative à la taille de police de l’élément racine :
font-size: 2rem;
Et une hauteur peut représenter une fraction de la zone d’affichage :
height: 50vh;
Aucune de ces valeurs n’est exprimée en pixels, pourtant le mise en page a besoin de dimensions réelles afin de déterminer l’emplacement de chaque élément. Pour combler cette lacune, chaque valeur passe par une série fixe d’étapes :
Declared Value
↓
Cascaded Value
↓
Specified Value
↓
Computed Value
↓
Used Value
↓
Actual Value
Ces noms semblent être du jargon au premier abord, mais chaque étape répond à une question spécifique. Les sections suivantes les expliquent dans l’ordre en utilisant un seul exemple continu.
Valeur déclarée : ce que vous avez demandé
La valeur déclarée est simplement celle qui apparaît dans la déclaration. Étant donné :
width: 66%;
La valeur déclarée pour width est 66%. Il s’agit d’une demande, rien de plus. Un élément peut facilement avoir plusieurs valeurs déclarées pour la même propriété provenant de règles différentes, donc la première étape consiste à en choisir une.
Valeur en cascade : la déclaration qui survit
Imaginons deux règles qui définissent toutes deux la largeur d’un paragraphe. L’une utilise un sélecteur de type :
p {
width: 100px;
}
et l’autre utilise une classe :
.amazing {
width: 66%;
}
Si un paragraphe possède la classe amazing, les deux règles s’y appliquent. Le sélecteur de classe est plus spécifique que le sélecteur d’élément pur, donc la cascade choisit :
width: 66%;
Cette valeur 66% qui survit est la valeur en cascade. Cette étape est importante car chaque étape suivante ne prend en compte que le gagnant ; les déclarations perdantes sont ignorées plutôt que traitées.
Valeur spécifiée : s’assurer que chaque propriété en a une
La valeur spécifiée est le résultat de la garantie que chaque propriété sur chaque élément possède une valeur. La règle est simple :
- si une valeur en cascade existe, elle devient la valeur spécifiée ;
Dans notre exemple, il s’agit d’une valeur en cascade, donc la valeur spécifiée reste 66% et cette étape ne semble rien faire. Elle devient importante pour les propriétés pour lesquelles aucune règle n’est mentionnée, c’est précisément là que l’héritage entre en jeu. Ce mécanisme est abordé dans la dernière section.
Valeur calculée : résolution de ce qui peut l’être sans mise en page
La valeur calculée correspond à ce que le navigateur peut déterminer sans avoir à mettre en page la page réellement. Les longueurs relatives basées sur les tailles de police deviennent des longueurs absolues, et les mots-clés sont transformés en valeurs concrètes. Prenons l’exemple suivant :
font-size: 2rem;
La unité ici :
2rem
Ce terme décrit une relation, à savoir « le double de la taille de police de l’élément racine », plutôt qu’une mesure fixe. Lors du calcul, le navigateur consulte la taille de police de l’élément racine et la convertit en pixels. Si la taille de police de l’élément racine est de 16 px, la taille de police calculée sera de 32 px.
L’une des raisons pour lesquelles cette conversion a lieu à ce stade est l’héritage : c’est la valeur calculée que les éléments enfants héritent, ce qui leur permet d’obtenir une longueur concrète au lieu d’une relation qu’ils devraient interpréter dans leur propre contexte. C’est pourquoi les tailles de police em imbriquées s’additionnent de cette manière, et pourquoi les hautesurs de ligne en pourcentage peuvent parfois provoquer un affichage serré du texte dans les éléments enfants ayant une police plus grande.
Certaines valeurs ne peuvent pas encore être déterminées. Une largeur en pourcentage dépend de la taille du bloc contenant, qui n’est connue qu’après le rendu, ce qui fait que 66% reste généralement un pourcentage dans la valeur calculée et n’est fixé qu’à l’étape suivante.
Valeur utilisée : intégration des informations de rendu
La valeur utilisée est le résultat obtenu une fois que les informations de rendu sont disponibles. Retour à :
width: 66%;
Une proportion en pourcentage n’est pas complète tant qu’on ne sait pas de quoi elle représente une part. Pour width, cette référence correspond à la largeur du bloc contenant. Supposons que la section parente soit :
280px
large. Le navigateur peut alors calculer :
66% of 280px
ce qui donne :
184.8px
Ce chiffre représente la valeur réellement utilisée. C’est le point de bascule dans le processus : le navigateur ne conserve plus le pourcentage que vous avez spécifié, mais une dimension réelle dérivée de la géométrie effective de la page. Il en va de même pour des valeurs comme les largeurs auto, qui n’ont de sens que lorsque le layout environnant est connu. Si vous interrogez un élément en JavaScript à l’aide de getComputedStyle, de nombreuses propriétés dépendantes du layout, telles que width, renvoient cette valeur en pixels déterminée, plutôt que le pourcentage, ce qui constitue un moyen pratique d’observer cette étape en pratique.
Valeur réelle : adaptation aux contraintes du dispositif
La dernière étape tient compte des limites de l’environnement de rendu. Un dispositif ne peut pas nécessairement afficher une valeur avec une précision arbitraire, donc une valeur utilisée comme :
184.8px
peut finir par être affichée de manière approximative comme :
185px
Ce chiffre ajusté représente la valeur réelle. En pratique, les navigateurs modernes disposent les éléments avec une précision sub-picométrique et appliquent un arrondi ou un ajustement à différents moments en fonction de la propriété et du moteur, il ne faut donc pas compter sur une règle d’arrondi spécifique. L’idée essentielle est simplement qu’il peut y avoir un ajustement final entre la valeur calculée et celle qui apparaît à l’écran.
En résumé, toute cette séquence se résume en une phrase : ce que vous écrivez dans un feuille de style n’est pas nécessairement ce que le navigateur finit par utiliser, et il existe des étapes bien définies entre les deux.
Pourquoi les unités relatives deviennent moins mystérieuses
Ce modèle explique pourquoi les unités relatives sont si utiles et si souvent mal comprises. Des unités comme celles-ci :
%
rem
em
vh
vw
Cela vous permet de décrire les tailles en fonction d’autre chose (le conteneur parent, la taille de police par défaut, la taille de police propre à l’élément, la zone de visualisation) au lieu d’utiliser des pixels en code fixe. C’est ce qui permet aux maquettes de s’adapter à différents écrans et paramètres d’utilisateur.
Cependant, chacune d’elles doit être résolue par rapport à sa référence avant que le layout ne puisse l’utiliser, et elles sont résolues à des étapes différentes : les unités relatives aux polices au stade du calcul, et les pourcentages par rapport au bloc contenant au stade de l’utilisation. En tenant compte de ce processus, on peut remplacer l’idée vague selon laquelle « le navigateur s’en charge » par une question plus précise lors du débogage : de quoi dépend cette valeur, et quelle est sa référence en ce moment ? Lorsqu’un layout responsive ne fonctionne pas correctement, la cause est très souvent une référence différente de ce que l’on supposait, comme un pourcentage se référant à un bloc contenant inattendu, ou un em qui passe par plusieurs éléments imbriqués.
Conversion de px en rem avec une racine de 10px
Comprendre le traitement des valeurs permet également de clarifier un flux de travail courant de mise à l’échelle : convertir les valeurs en pixels d’un design en rem. Supposons qu’un design spécifie :
padding: 30px;
et que vous préfériez l’exprimer comme suit :
padding: 3rem;
Une approche populaire consiste à définir la taille de police racine sur un nombre entier :
html {
font-size: 10px;
}
Avec cela en place :
1rem = 10px
les conversions deviennent alors des calculs mentaux :
30px → 3rem
40px → 4rem
20px → 2rem
En général :
rem = pixels / 10
L’avantage ne réside pas seulement dans des nombres plus ordonnés. Comme chaque rem est calculé en fonction de la taille de police racine au moment du rendu, modifier cette valeur racine rééchelle en même temps toutes les mesures basées sur rem sur la page, ce qui vous offre un contrôle global unique sur le système de mise à l’échelle.
Il existe une mise en garde importante. Définir la taille de racine à 10px fixe remplace la taille de police par défaut que l’utilisateur a pu choisir dans les paramètres de son navigateur, ce qui contredit l’un des principaux avantages en termes d’accessibilité de l’utilisation de rem. Une alternative largement utilisée est html { font-size: 62.5%; } : avec la valeur par défaut de 16px du navigateur, cela donne toujours 10px, mais la taille s’adapte proportionnellement lorsque l’utilisateur augmente sa taille préférée. Si vous adoptez ce schéma, n’oubliez pas non plus de définir une taille de police lisible pour body (par exemple 1.6rem), car sinon le texte reprendrait par défaut la taille réduite de la racine.
Lorsque rien n’a été déclaré du tout
Une question reste sans réponse après les étapes précédentes. Prenons ce markup :
<div class="parent">
<p>Hello World</p>
</div>
et une règle sur le conteneur :
.parent {
color: red;
}
Il n’existe nulle part de règle du type :
p {
color: red;
}
Pourtant, le texte du paragraphe est en rouge. Le paragraphe ne possède aucune règle de cascade pour la propriété color, ce qui signifie que sa valeur spécifiée doit provenir d’ailleurs. Comme color est une propriété héritée, le navigateur utilise la couleur calculée du parent. Il s’agit donc d’héritage, qui s’intègre directement à l’étape des valeurs spécifiées décrite précédemment. Pour en savoir plus sur les propriétés qui sont héritées, pourquoi les enfants reçoivent des valeurs calculées plutôt que déclarées, et comment inherit et initial modifient les valeurs par défaut, l’héritage mérite une étude approfondie à part entière.
Points clés
- Une valeur CSS traverse six étapes : déclarée, en cascade, spécifiée, calculée, utilisée et réelle.
- La cascade réduit de nombreuses valeurs déclarées à une seule valeur en cascade ; seule cette dernière est ensuite traitée plus en détail.
rem et em, deviennent des longueurs absolues lors de l’étape de calcul ; les largeurs en pourcentage attendent le rendu du layout et sont déterminées à l’étape d’utilisation.62,5% respecte mieux les préférences de police de l’utilisateur qu’un 10px fixe.