Publié le 15 mai 2024

Styliser un lien n’est pas une simple tâche de décoration, mais un acte de narration interactive.

  • Chaque pseudo-classe (`:link`, `:visited`, `:focus`, `:hover`, `:active`) est un « mot » qui communique une information cruciale à l’utilisateur.
  • Ignorer des états comme `:focus` ou `:visited` revient à livrer une interface muette, frustrante et inaccessible.

Recommandation : Pensez chaque état non pas comme un style, mais comme une phrase dans votre dialogue avec l’utilisateur pour créer une expérience riche, intuitive et respectueuse.

Pour de nombreux développeurs, styliser un lien se résume à deux étapes : choisir une couleur et ajouter un effet au survol avec `:hover`. C’est une routine mécanique, une case à cocher sur la longue liste des tâches de l’intégration. On sait vaguement qu’il existe d’autres états, comme `:focus` pour l’accessibilité ou `:visited` pour les liens déjà consultés, mais ils sont souvent perçus comme des contraintes techniques, des détails superflus que l’on copie-colle sans conviction ou, pire, que l’on ignore superbement.

Cette approche, pourtant si répandue, est une erreur fondamentale. Elle considère les pseudo-classes comme de simples outils cosmétiques. Mais si la véritable clé n’était pas dans la décoration, mais dans la narration ? Si chaque état d’un lien était un mot dans un langage non-verbal destiné à l’utilisateur ? `:link` est une promesse de contenu. `:visited` est un souvenir. `:hover` est une conversation qui commence. `:focus` est un guide pour ceux qui ne voient pas avec les yeux. Et `:active` est la confirmation d’une décision. Ensemble, ils forment une grammaire visuelle qui raconte une histoire.

Cet article vous propose de changer radicalement de perspective. Nous n’allons pas simplement lister des propriétés CSS. Nous allons agir en sémiologues de l’interaction, en décodant le sens caché derrière chaque pseudo-classe. Vous apprendrez non seulement à styliser vos liens, mais surtout à les faire parler. Vous découvrirez comment un ordre de déclaration précis n’est pas une lubie de spécification mais une règle de syntaxe, comment l’accessibilité devient une évidence narrative et comment vos interactions peuvent enfin avoir du sens, sur ordinateur comme sur mobile.

Pour naviguer à travers cette exploration du langage interactif, voici le plan que nous allons suivre. Chaque section décortiquera un aspect de cette grammaire visuelle, vous donnant les clés pour construire des interfaces plus intelligentes et plus humaines.

L-V-F-H-A : la règle mnémonique pour ne plus jamais se tromper dans l’ordre de vos pseudo-classes

Toute langue possède sa grammaire, et le langage des états de lien en CSS ne fait pas exception. Sa règle la plus fondamentale, et pourtant la plus souvent transgressée, concerne l’ordre de déclaration des pseudo-classes. Un ordre incorrect ne génère pas d’erreur dans la console, mais il brise silencieusement la logique de l’interaction. Pourquoi ? La réponse tient en un mot : la spécificité. En CSS, lorsque deux sélecteurs ont la même spécificité, c’est le dernier déclaré qui l’emporte. Or, un lien peut avoir plusieurs états simultanément (par exemple, être à la fois `:visited` et `:hover`).

L’ordre correct pour gérer cette cascade est L-V-F-H-A : `:link`, `:visited`, `:focus`, `:hover`, `:active`. Pour s’en souvenir, les développeurs ont inventé des moyens mnémotechniques. L’un des plus connus est la phrase anglaise « LoVe, For HAte? ». Cet ordre garantit que les états les plus spécifiques et temporaires (comme `:hover` ou `:active`) ne sont pas écrasés par des états plus généraux (comme `:link` ou `:visited`).

Voici la décomposition de cette syntaxe interactive :

  • :link : L’état initial, la promesse. C’est le style par défaut pour un lien qui n’a jamais été visité.
  • :visited : La mémoire. Il indique à l’utilisateur qu’il a déjà exploré cette destination.
  • :focus : L’indicateur d’accessibilité. Il montre quel élément est actuellement sélectionné via la navigation au clavier. Il doit être déclaré après `:visited` pour être visible sur les liens déjà cliqués.
  • :hover : Le dialogue. Il signale l’interactivité potentielle lorsque l’utilisateur survole l’élément avec sa souris. Il doit venir après `:focus` pour que l’effet de survol fonctionne même si l’élément a déjà le focus.
  • :active : La confirmation. C’est l’état fugace qui apparaît au moment précis du clic, le feedback ultime avant l’action. Étant le plus spécifique, il doit être déclaré en dernier.

Respecter cet ordre n’est donc pas une simple convention, c’est s’assurer que chaque « mot » de votre langage visuel sera prononcé au bon moment, créant une expérience utilisateur cohérente et prévisible.

Ne supprimez jamais l’outline de `:focus` : un plaidoyer pour l’accessibilité

Dans le dialogue entre l’interface et l’utilisateur, l’état `:focus` est la voix qui guide ceux qui ne peuvent pas utiliser de souris. C’est le halo lumineux qui dit : « Vous êtes ici ». Pourtant, une pratique désastreuse persiste : `a:focus { outline: none; }`. Cette seule ligne de code, souvent ajoutée pour des raisons esthétiques, plonge dans le silence des millions d’utilisateurs qui dépendent de la navigation au clavier, qu’ils soient en situation de handicap moteur, malvoyants ou simplement des « power users ». En France, la situation est critique : une étude récente révèle que seulement 0,45% des sites publics et privés audités sont totalement conformes au RGAA, la visibilité du focus étant un critère majeur.

Le fameux « outline » bleu ou noir par défaut des navigateurs n’est peut-être pas du goût de tous les designers, mais il a le mérite d’être universellement reconnu et très contrasté. Le supprimer sans alternative est un non-sens en matière d’UX et une faute d’accessibilité. L’enjeu n’est pas de subir ce style, mais de se l’approprier pour l’intégrer harmonieusement à sa charte graphique.

Main appuyant sur la touche Tab d'un clavier avec des halos lumineux autour des touches

La solution n’est pas la suppression, mais la personnalisation. Vous pouvez et devez styliser l’état `:focus` pour qu’il soit à la fois esthétique et fonctionnel. Utilisez la propriété `outline` en jouant sur sa couleur, son épaisseur et son style, ou utilisez `outline-offset` pour le décoller légèrement de l’élément et améliorer sa lisibilité. Il est crucial d’éviter de remplacer `outline` par `box-shadow`, car cette dernière n’est souvent pas visible lorsque l’utilisateur active un mode de contraste élevé dans son système d’exploitation, rendant votre effort inutile.

Plan d’action : auditer la visibilité de votre focus

  1. Points de contact : Listez tous les éléments interactifs de votre site (liens, boutons, champs de formulaire, etc.).
  2. Collecte : Naviguez sur votre site uniquement avec la touche `Tab`. L’élément actif est-il TOUJOURS clairement identifiable visuellement ?
  3. Cohérence : Le style du `:focus` est-il cohérent avec celui du `:hover` ? La bonne pratique est de doubler systématiquement chaque règle `:hover` avec une règle `:focus`.
  4. Contraste et Visibilité : Testez votre page en activant le mode de contraste élevé (Windows) ou en inversant les couleurs (macOS). Le focus est-il toujours visible ? Si vous utilisez `box-shadow`, il disparaîtra probablement.
  5. Plan d’intégration : Remplacez tous les `outline: none` par un style personnalisé (ex: `outline: 2px solid var(–primary-color); outline-offset: 2px;`) et assurez sa visibilité en toutes circonstances.

La stylisation limitée de `:visited` : comment les navigateurs vous protègent d’un pistage insoupçonné

La pseudo-classe `:visited` est la mémoire de l’interface. Elle répond à une question simple de l’utilisateur : « Suis-je déjà allé ici ? ». Ce simple changement de couleur est un outil de navigation puissant, qui évite à l’utilisateur de cliquer deux fois sur le même lien ou de se perdre dans un parcours déjà effectué. C’est un mot discret mais essentiel dans notre dialogue. Cependant, vous avez peut-être remarqué qu’il est impossible de transformer radicalement un lien visité : impossible de changer sa taille, d’ajouter du `padding` ou de le faire danser. Cette limitation n’est pas un bug, mais une protection délibérée.

Jusqu’en 2010, il était possible d’appliquer n’importe quel style à un lien visité. Un site malveillant pouvait alors créer une liste invisible de milliers d’URL et, en utilisant JavaScript pour vérifier la couleur de chaque lien, déterminer précisément quels sites vous aviez visités. C’était une faille de confidentialité majeure. Pour y remédier, les navigateurs ont drastiquement limité les propriétés CSS applicables à `:visited`. Pour des raisons de sécurité évidentes, cette pseudo-classe est aujourd’hui très encadrée.

Les seules propriétés que vous pouvez modifier en toute sécurité sont celles qui touchent à la couleur, sans affecter la géométrie de l’élément :

  • `color`
  • `background-color`
  • `border-color`
  • `outline-color`
  • Les propriétés de couleur SVG comme `fill` et `stroke`

Toute autre tentative de modification (comme `font-size`, `display`, `margin`, etc.) sera simplement ignorée par le navigateur. Cette contrainte transforme le design de l’état `:visited`. Il ne s’agit plus d’être créatif, mais d’être subtil et efficace. L’objectif est de fournir un indice visuel clair mais discret, souvent une simple désaturation ou un changement de teinte de la couleur principale du lien, sans jamais compromettre la vie privée de l’utilisateur.

Effets de survol avancés : la puissance des pseudo-classes combinées aux pseudo-éléments

Si `:visited` est la mémoire et `:focus` le guide, `:hover` est le terrain de jeu de l’expression créative. C’est ici que le dialogue avec l’utilisateur devient le plus visible, le plus tangible. Mais un grand pouvoir implique de grandes responsabilités, notamment en matière de performance. Une animation de survol saccadée ou qui provoque un décalage de la mise en page (layout shift) est pire qu’une absence d’animation. Elle communique la lenteur et le manque de professionnalisme.

La clé d’un effet de survol réussi est de n’animer que des propriétés CSS peu coûteuses pour le moteur de rendu du navigateur. Les propriétés idéales sont `transform` et `opacity`, car elles ne déclenchent ni de « reflow » (recalcul de la mise en page) ni de « repaint » (redessinage des pixels), étant gérées directement par le GPU. Animer `width`, `height`, `margin` ou `padding` est à proscrire : cela force le navigateur à recalculer toute la géométrie de la page, ce qui peut être très coûteux.

Doigt approchant d'une surface interactive avec ondulations lumineuses colorées irradiant du point de contact

Pour aller plus loin que le simple changement de couleur, la combinaison de `:hover` avec les pseudo-éléments `::before` et `::after` ouvre des possibilités infinies. On peut créer des effets de soulignement qui apparaissent élégamment, des arrière-plans qui glissent, ou des icônes qui se révèlent. Par exemple, pour un soulignement qui s’anime depuis le centre, on peut positionner un `::after` sous le lien avec une largeur initiale de 0 (`transform: scaleX(0)`) et l’animer vers une largeur de 100% (`transform: scaleX(1)`) au survol. Cette technique est non seulement élégante, mais aussi très performante.

Le choix des propriétés à animer lors d’un survol a un impact direct sur la perception de la fluidité et de la qualité de votre interface. Ce tableau récapitule les bons et les mauvais élèves en matière de performance.

Performance des propriétés CSS pour les animations de survol
Propriété Performance Impact sur le layout Recommandé pour :hover
transform Excellente Aucun ✅ Oui
opacity Excellente Aucun ✅ Oui
color Bonne Aucun ✅ Oui
width/height Mauvaise Recalcul complet ❌ Non
margin/padding Mauvaise Recalcul complet ❌ Non

`:focus-visible` : la pseudo-classe qui réconcilie enfin les designers et l’accessibilité

Le conflit était ancien et tenace. D’un côté, les défenseurs de l’accessibilité martelaient : « Ne supprimez jamais l’outline de `:focus` ! ». De l’autre, les designers se plaignaient : « Mais cet anneau bleu apparaît au clic de la souris sur mes boutons, c’est disgracieux ! ». Les deux avaient raison. Afficher le focus au clic de souris est souvent redondant, mais le masquer complètement pénalise la navigation au clavier. Pendant des années, les développeurs ont dû bricoler avec du JavaScript pour tenter de résoudre ce dilemme. Cette ère est révolue grâce à `:focus-visible`.

Cette pseudo-classe est une véritable révolution diplomatique. Elle permet au navigateur de décider intelligemment quand afficher l’indicateur de focus. La règle est simple : si l’utilisateur interagit via le clavier (avec la touche `Tab`, par exemple), le style défini dans `:focus-visible` s’applique. Si l’utilisateur clique avec la souris sur un élément comme un bouton ou un lien, le navigateur considère que le focus est déjà évident et n’applique pas le style. C’est le meilleur des deux mondes : une interface propre pour les utilisateurs de souris et un guidage clair pour les utilisateurs de clavier.

Entièrement standardisée, la pseudo-classe :focus-visible bénéficie d’un support universel sur tous les navigateurs modernes depuis mars 2022. Son implémentation est d’une grande simplicité. Au lieu de styliser `:focus`, vous stylisez `:focus-visible`. Pour une compatibilité maximale avec les anciens navigateurs, on peut utiliser une requête `@supports` pour n’appliquer la logique que si le navigateur la comprend.

Attention cependant à un cas particulier : un clic sur un élément qui requiert une saisie clavier, comme un «  ou un `

`:hover` est mort (sur mobile) : comment repenser vos interactions pour le tactile

Le survol (`:hover`) est un pilier de l’interaction sur ordinateur. C’est une conversation subtile entre l’utilisateur et l’interface, rendue possible par la dissociation entre le pointeur et l’action de clic. Sur un écran tactile, cette conversation n’existe pas : le doigt est à la fois le pointeur et l’acteur du clic. Tenter de transposer la logique du `:hover` sur mobile est une erreur de traduction qui mène à des expériences utilisateur frustrantes, comme le fameux « sticky hover » où un menu reste ouvert après un premier tap, en masquant le contenu et en obligeant l’utilisateur à taper une seconde fois pour activer le lien.

Sur mobile, le mot `:hover` n’a pas de sens. Il faut apprendre un nouveau dialecte, celui du tactile. Le survol, qui servait à révéler une information ou des actions secondaires, doit être remplacé par des gestes intentionnels. Le dialogue ne se fait plus en amont, mais en réaction directe à une action de l’utilisateur. Les micro-interactions ne disparaissent pas, elles se déplacent : de `:hover`, elles migrent vers `:active`.

Plutôt que de chercher à imiter le survol, voici des alternatives natives du tactile qui créent une expérience bien plus fluide et intuitive :

  • Le « Toggle » au premier tap : Pour les éléments qui cachaient une information au survol, le premier tap révèle cette information et le second tap (sur l’élément ou ailleurs) la masque.
  • L’accordéon : Pour les listes d’éléments complexes, un tap sur un en-tête déploie le contenu associé, offrant un contrôle total à l’utilisateur.
  • Le « Long Press » (appui long) : Ce geste, souvent géré avec JavaScript, peut remplacer le survol pour révéler des actions contextuelles (ex: « partager », « supprimer »).
  • Le feedback sur `:active` : Le moment du contact du doigt sur l’écran est l’équivalent du clic. C’est à ce moment précis que le feedback visuel doit avoir lieu. Un léger changement de couleur de fond ou une petite animation de mise à l’échelle (`transform: scale(0.98)`) sur `:active` donne un retour instantané et satisfaisant.

Admettre que `:hover` est mort sur mobile n’est pas un aveu d’échec, mais une prise de conscience libératrice. Elle nous force à concevoir des interactions plus directes, plus honnêtes et finalement mieux adaptées au contexte d’usage, en traduisant l’intention plutôt qu’en copiant le style.

Caché, mais pas pour tout le monde : la bonne façon de masquer un élément en CSS

Dans notre langage interactif, savoir se taire est parfois aussi important que de parler. Masquer un élément en CSS semble trivial : `display: none;` et le tour est joué. Mais cette approche radicale revient à effacer complètement l’élément de l’existence. Il est non seulement invisible, mais aussi inaccessible aux lecteurs d’écran et ignoré par la mise en page. Parfois, c’est ce que l’on veut. Mais souvent, l’intention est plus nuancée : on veut cacher un élément visuellement, tout en le laissant « audible » pour les technologies d’assistance.

C’est le cas typique des liens d’évitement (« skip links »). Ces liens, souvent placés en tout début de page, sont invisibles pour les utilisateurs voyants mais sont le premier élément à recevoir le `:focus` lors de la navigation au clavier. Ils permettent aux utilisateurs de lecteurs d’écran de « sauter » directement au contenu principal sans avoir à écouter la lecture de tout le menu de navigation. Pour eux, ce lien n’est pas caché, il est essentiel. Pour réaliser cela, on ne peut pas utiliser `display: none;` ou `visibility: hidden;` qui masquent également l’élément aux lecteurs d’écran.

La technique la plus robuste consiste à utiliser une classe CSS dédiée (souvent nommée `.sr-only` pour « Screen Reader Only ») qui sort l’élément du flux visible de la page sans le supprimer du DOM accessible. Cette technique combine plusieurs propriétés pour rendre un élément de 1 pixel par 1 pixel, sans débordement visible, tout en restant parfaitement lisible par les technologies d’assistance. Voici un comparatif des différentes méthodes de masquage et de leur impact.

Ce tableau, inspiré par des analyses sur l’accessibilité et la visibilité, clarifie le rôle de chaque technique.

Techniques de masquage et leur impact sur l’accessibilité
Technique Visible Lecteur d’écran Espace occupé Cas d’usage
display: none Non Non Non Masquage total
visibility: hidden Non Non Oui Conserver l’espace
classe .sr-only Non Oui Non Texte pour lecteurs d’écran
opacity: 0 Non Oui Oui Animations
position + clip Non Oui Non Skip links

Choisir la bonne technique de masquage est un acte sémantique. C’est décider si un élément doit être véritablement silencieux ou s’il doit simplement murmurer à l’oreille de certains utilisateurs. C’est une distinction fondamentale pour construire une expérience réellement inclusive.

À retenir

  • L’ordre des pseudo-classes (L-V-F-H-A) n’est pas une convention mais une règle de grammaire imposée par la cascade CSS pour gérer la spécificité.
  • Supprimer l’outline de `:focus` sans alternative personnalisée est une faute d’accessibilité qui rend la navigation au clavier impossible.
  • Les interactions basées sur `:hover` ne se traduisent pas sur les écrans tactiles et doivent être remplacées par des patterns natifs comme le « tap » ou le « long press ».

Le survol n’est pas un gadget : comment créer des `:hover` qui ont du sens

Nous avons établi que le survol est un dialogue. Mais pour qu’un dialogue soit intéressant, il doit apporter de la valeur. Un effet `:hover` qui ne fait que changer une couleur sans raison ou qui déclenche une animation tape-à-l’œil juste pour le spectacle est un bruit inutile. Un bon effet de survol n’est pas un gadget ; il remplit une fonction précise. Pour créer des interactions qui ont du sens, chaque effet de survol devrait servir l’une de ces trois fonctions cardinales : confirmer, informer ou préfigurer.

Premièrement, confirmer l’interactivité. C’est la fonction la plus basique mais essentielle. Le changement visuel au passage de la souris rassure l’utilisateur : « Oui, cet élément est cliquable ». Cela répond directement à la Loi de Fitts, qui stipule que le temps nécessaire pour atteindre une cible est fonction de la distance et de la taille de la cible. Le feedback du `:hover` réduit l’incertitude et fluidifie l’action. Pour que ce feedback soit perçu comme instantané, les études en psychologie cognitive montrent qu’une transition de 150 à 250ms est optimale.

Deuxièmement, informer. Le survol peut être utilisé pour révéler une information contextuelle concise. Pensez à un lien dont le texte est tronqué et où le survol affiche le titre complet dans une infobulle, ou à une icône qui révèle son libellé au passage de la souris. L’information est disponible au besoin, sans encombrer l’interface en permanence. C’est un principe d’économie cognitive : on ne montre que ce qui est nécessaire, quand c’est nécessaire.

Troisièmement, préfigurer le résultat de l’action. Le survol peut offrir un aperçu de ce qui se passera après le clic. C’est le cas d’une liste de thèmes visuels où le survol d’un nom applique une preview de ce thème à un élément de la page. L’utilisateur peut ainsi prendre une décision plus éclairée avant de s’engager. C’est une forme de prototypage en temps réel, directement dans l’interface.

Pour que ces interactions soient réellement utiles, il est impératif d’appliquer le même style au `:focus` qu’au `:hover`, garantissant une expérience cohérente pour tous. Pour approfondir votre maîtrise, il est crucial de créer des :hover qui ont véritablement du sens.

En adoptant cette approche fonctionnelle, vous transformez vos effets de survol de simples décorations en outils puissants au service de l’utilisateur. L’étape suivante consiste à auditer vos propres projets à travers ce prisme sémantique et à vous demander : « Qu’est-ce que mes liens racontent vraiment ? ».

Questions fréquentes sur la stylisation des liens avec les pseudo-classes CSS

Pourquoi l’ordre des pseudo-classes comme L-V-H-A est-il si important en CSS ?

L’ordre L-V-H-A (Link, Visited, Hover, Active) ou L-V-F-H-A (avec Focus) est crucial à cause de la cascade et de la spécificité en CSS. Un lien peut avoir plusieurs états à la fois (par exemple, être à la fois `:visited` et `:hover`). Comme ces pseudo-classes ont la même spécificité, c’est la dernière règle déclarée dans votre feuille de style qui l’emporte. En plaçant `:hover` et `:active` à la fin, vous vous assurez que leurs styles, plus spécifiques et temporaires, écraseront les styles des états plus généraux comme `:link` et `:visited`, garantissant ainsi que l’interaction visuelle se produit comme prévu.

Quelle est la différence concrète entre :focus et :focus-visible ?

`:focus` s’active dès qu’un élément reçoit le focus, que ce soit via un clic de souris ou la navigation au clavier. `:focus-visible`, quant à lui, est plus intelligent : il s’active principalement lorsque l’utilisateur navigue avec le clavier (touche `Tab`). Le navigateur estime que si vous utilisez la souris, le focus est déjà évident, et n’affiche donc pas le style. Cela permet de satisfaire à la fois les designers (pas d’outline au clic) et l’accessibilité (outline visible pour la navigation clavier).

Peut-on utiliser :hover sur les appareils tactiles comme les mobiles ou les tablettes ?

Techniquement, oui, mais c’est une très mauvaise pratique. Sur un écran tactile, il n’y a pas de curseur pour « survoler » un élément. Le premier contact du doigt déclenche à la fois l’état `:hover` et l’état `:focus`, ce qui provoque souvent un effet « collant » (sticky hover) où l’élément reste dans son état de survol jusqu’à ce que l’on touche ailleurs. Il est recommandé de ne pas utiliser `:hover` pour des interactions critiques sur mobile et de privilégier des patterns tactiles comme le `tap` (géré avec `:active`) ou le `long press` (géré avec JavaScript).

Rédigé par Sofia Garnier, Sofia Garnier est une experte en accessibilité web (a11y) avec une décennie d'expérience, spécialisée dans l'audit et l'implémentation de front-ends inclusifs. Elle se concentre sur l'interaction entre le HTML sémantique et le CSS pour créer des expériences utilisables par tous.