Mis à jour 10 juillet 2026

États survol et pressé : les dériver de l’échelle

La couleur de survol d’un bouton ne devrait pas être calculée à partir de sa couleur au repos — elle devrait être le palier voisin sur la même échelle de couleur. En thème clair, le survol est un palier plus sombre que le repos, et l’état pressé un palier plus sombre encore : un bouton au repos sur brand-600 survole en brand-700 et se presse en brand-800. Trois vraies couleurs issues de la rampe, pas deux variantes filtrées d’une seule.

La règle semble presque trop menue pour mériter un article, et pourtant son inverse — des états de survol produits par l’opacité, darken() ou un filtre de luminosité — figure parmi les défauts les plus courants des interfaces livrées. Pourquoi les paliers voisins l’emportent, à quelle distance ils doivent se situer, et pourquoi toute la règle inverse son sens en mode sombre : tout découle de la façon dont les échelles de couleur sont construites, le terrain couvert par notre guide des échelles de couleur.

Pourquoi pas l’opacité ou un filtre darken() ?

Parce que les filtres quittent la rampe. Le survol de brand-600 devient une couleur qui n’existe nulle part ailleurs dans le système — sans nom, non testée, invisible pour le pipeline de tokens — et elle se comporte mal de trois manières précises.

L’opacité se mélange avec ce qui se trouve derrière. Un bouton à 85 % d’opacité rend une couleur sur une page blanche et une autre sur une carte teintée, si bien que le « même » état de survol varie selon les endroits du produit. Pire, le mélange peut entrer en collision avec l’arrière-plan : un bleu translucide sur une surface chaude en absorbe la chaleur, et personne n’a jamais choisi ce résultat.

L’assombrissement par canal dérive. darken() et ses semblables réduisent les valeurs RGB, et en sRGB cette opération fait plus qu’assombrir : elle désature généralement, et elle peut décaler la teinte à mesure que les canaux saturent à des rythmes différents. Le survol ressort plus gris et à la teinte légèrement faussée — perçu comme sale plutôt que comme plus profond.

La couleur filtrée n’a jamais été vérifiée en contraste. La couleur au repos a franchi ses seuils ; la sortie du filtre n’a rien franchi. Un texte qui atteignait 4,5:1 au repos peut échouer discrètement au survol.

La mécanique CSS est la partie triviale — :hover échange une propriété personnalisée ou une classe. La seule vraie question a toujours été quelle couleur l’échange fait entrer, et l’échelle contient déjà la bonne réponse : le palier d’à côté, généré par les mêmes règles, sur la même teinte, déjà vérifié.

Quelle doit être l’ampleur du saut entre états ?

Un palier est généralement le bon choix, et les limites de part et d’autre expliquent pourquoi.

Le plancher est perceptuel : un changement inférieur à la différence tout juste perceptible se lit comme aucun changement. Scale Composer avertit lorsque des paliers adjacents se situent à moins de 0,02 L l’un de l’autre, car un saut de survol plus petit que cela est un survol que les utilisateurs ne peuvent pas voir — le bouton paraît mort alors même que la feuille de style jure qu’il réagit.

Le plafond est l’identité : un saut de plusieurs paliers se lit comme une couleur différente plutôt qu’un état différent. Le bouton semble changer d’avis sur ce qu’il est, et l’œil interprète un remplacement, pas une pression. Entre ces limites, un palier bien construit s’installe confortablement : nettement visible, tout en restant indéniablement la même couleur. L’état pressé franchit alors le palier au-delà du survol — l’interaction s’approfondissant dans la direction où elle a commencé.

À quoi ressemble ce parcours en chiffres ?

À partir de la couleur de départ bleue #2563eboklch(0.546 0.215 262.9) — le voisinage 600/700/800 de la rampe donne ceci :

ÉtatPalierOKLCHHex
repos600oklch(0.52 0.134 262.9)#3E65B5
survol700oklch(0.43 0.073 262.9)#3A4F78
pressé800oklch(0.34 0.031 262.9)#2F3848

Chaque mouvement fait baisser la luminosité d’environ 0,09 — à peu près quatre fois le seuil JND de 0,02, donc indéniable, tandis que la teinte se maintient à 262,9° à travers les trois paliers. Cette colonne de teinte est l’argument discret contre les filtres : un darken() en sRGB ne l’aurait pas conservée. Le chroma qui s’atténue vers l’extrémité sombre, c’est le propre plan de saturation de la rampe à l’œuvre, et c’est pourquoi l’état pressé se lit comme le même bleu pressé plus profondément plutôt que comme une couleur nouvelle et plus grise. Scale Composer dérive stateNormal, stateHover et statePressed parmi ses rôles sémantiques exactement de cette façon — des paliers voisins sur la rampe, attribués face aux seuils de contraste de la surface.

Ouvrez ce parcours en trois paliers dans Scale Composer — repos, survol et pressé mis en évidence comme voisins sur la rampe bleue, avec les écarts de luminosité entre eux mesurés.

Les états repos, survol et pressé d'un bouton présentés comme trois paliers voisins sur une rampe bleue dans Scale Composer, chacun un palier plus sombre que le précédent

Et pour le focus et l’état désactivé ?

Deux états échappent brièvement à la règle du parcours.

Le focus n’est pas un changement de remplissage. C’est un rôle d’anneau distinct — un contour qui doit rester visible à la fois sur le bouton et sur la page derrière lui. Le dériver comme « un palier de plus » l’enfouit ; les utilisateurs au clavier ont besoin que l’anneau s’annonce, pas qu’il se fonde dans la séquence de pression.

L’état désactivé sort entièrement de la logique d’interaction. C’est la paire à faible chroma et faible contraste de l’ensemble des rôles, et c’est délibéré : un contrôle désactivé communique en s’effaçant. Les seuils de contraste ne s’y appliquent pas — les recommandations d’accessibilité dispensent les contrôles inactifs — et c’est pourquoi c’est le seul état qui est censé échouer aux vérifications que les autres franchissent.

Pourquoi le mode sombre inverse-t-il la direction ?

Sur les surfaces sombres, le survol s’éclaircit, il ne s’assombrit pas. La convention vient de la logique d’élévation : en thèmes sombres, les surfaces plus proches du spectateur s’affichent plus claires, et un contrôle survolé est momentanément « surélevé ». Il y a aussi une raison purement pratique — à partir d’un palier sombre au repos, s’assombrir tend vers le noir, et le changement d’état se noie.

Une palette sombre dérivée obtient cette inversion par construction plutôt que par exception. Scale Composer dérive les palettes sombres à partir de la même couleur de départ, avec leur propre courbe de luminosité et une augmentation de chroma d’environ 20 % (un environnement sombre atténue la coloration perçue), si bien que les paliers de la rampe sombre sont de vraies couleurs ordonnées à part entière — et « avancer d’un palier dans la direction interactive » pointe simplement dans l’autre sens le long d’eux. Aucun bricolage de survol propre au thème ; la règle survit, la direction s’inverse.

Basculez le même bouton sur la palette sombre dérivée — les rôles repos, survol et pressé re-dérivés sur la rampe sombre, avançant plus clair là où le thème clair avançait plus sombre.

Continuer la lecture