Niveau 15

Les Liens

Maîtriser le stylisme des liens en CSS : états LVHFA, animations, décoration, accessibilité, et composants de navigation modernes.

1. Le concept fondamental

En une phrase : Les liens en CSS se contrôlent à travers des pseudo-classes d'état, des propriétés de décoration et des transitions pour créer des navigations intuitives et accessibles.

Les liens sont le fondement de la navigation web. En CSS, on ne se contente pas de les colorer : on gère leurs 5 états (LVHFA), on anime leur décoration, on crée des indicateurs visuels, et on conçoit des composants de navigation modernes. Ce chapitre couvre tout l'arsenal pour maîtriser les liens en CSS.

L'élément a et ses attributs

Attribut Description
href URL de destination du lien
target Où ouvrir le lien : _blank (nouvel onglet), _self (même onglet)
rel Relation : noopener, noreferrer, nofollow
title Info-bulle au survol (accessibilité)
download Téléchargement au clic

Les 5 états d'un lien (LVHFA)

Astuce : L'ordre LVHFA est crucial ! Si vous inverséz :hover et :visited, les styles ne s'appliqueront pas correctement à cause de la spécificité CSS.

Ce que le CSS peut faire avec les liens

Capacité Propriétés CSS Usage
États :link, :visited, :hover, :active, :focus Styles différents par état d'interaction
Décoration text-decoration, text-underline-offset Soulignement, barré, survolé,none
Couleur color par pseudo-classe Couleur différentes par état
Animations transition, ::after, transform Soulignement glissant, highlights
Focus :focus-visible, outline Accessibilité clavier
Formes display, padding, border-radius Boutons, pills, navigation

2. Syntaxe et propriétés

Les 5 états : LVHFA

/* L — Lien non visité (défaut) */
a:link {
  color: #2563eb;
}

/* V — Lien visité */
a:visited {
  color: #7c3aed;
}

/* H — Au survol */
a:hover {
  color: #1d4ed8;
}

/* A — En cours de clic */
a:active {
  color: #dc2626;
}

/* F — Focus au clavier */
a:focus {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* ✅ Ordre correct LVHFA */
a:link    { color: #2563eb; }
a:visited { color: #7c3aed; }
a:hover   { color: #1d4ed8; }
a:active  { color: #dc2626; }

:focus-visible vs :focus

/* :focus — s'applique à tout focus (clavier ET clic) */
a:focus {
  outline: 2px solid #2563eb;
}

/* :focus-visible — s'applique UNIQUEMENT au focus clavier */
/* ✅ Recommandé pour l'accessibilité */
a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* Masquer le focus pour les clics, garder pour le clavier */
a:focus:not(:focus-visible) {
  outline: none;
}

a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
  border-radius: 4px;
}
Important : Utilisez :focus-visible plutôt que :focus pour les indicateurs de focus. Cela évite l'outline inutile au clic tout en gardant l'accessibilité clavier.

text-decoration

/* Supprimer la décoration */
a {
  text-decoration: none;
}

/* Soulignement classique */
a {
  text-decoration: underline;
}

/* Autres valeurs */
a { text-decoration: overline; }      /* Ligne au-dessus */
a { text-decoration: line-through; }  /* Ligne barrée */
a { text-decoration: none; }          /* Pas de décoration */

/* Combinaison */
a {
  text-decoration: underline wavy #ef4444;
}
Propriété Valeurs Description
text-decoration-line none, underline, overline, line-through Type de ligne
text-decoration-style solid, dashed, dotted, double, wavy Style de la ligne
text-decoration-color Couleur CSS Couleur de la ligne
text-decoration-thickness auto, px, em Épaisseur de la ligne
text-underline-offset auto, px, em Distance entre le texte et le soulignement

Propriétés détaillées de la décoration

a {
  text-decoration-line: underline;
  text-decoration-style: wavy;
  text-decoration-color: #ef4444;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}

/* Version raccourcie */
a {
  text-decoration: underline wavy #ef4444 2px;
  text-underline-offset: 4px;
}

Transitions sur les liens

a {
  color: #2563eb;
  text-decoration: underline;
  text-decoration-color: transparent;
  text-underline-offset: 3px;
  transition: color 0.3s ease, text-decoration-color 0.3s ease;
}

a:hover {
  color: #1d4ed8;
  text-decoration-color: #1d4ed8;
}

a:active {
  color: #dc2626;
}

Animation de soulignement glissant

/* Soulignement qui glisse de gauche à droite */
.link-slide {
  position: relative;
  color: #2563eb;
  text-decoration: none;
  padding-bottom: 2px;
}

.link-slide::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: #2563eb;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}

.link-slide:hover::after {
  transform: scaleX(1);
}

/* Soulignement qui glisse du centre */
.link-center::after {
  transform-origin: center;
}

/* Soulignement qui glisse de droite à gauche */
.link-right::after {
  transform-origin: right;
}

Effet highlight de fond

/* Highlight qui apparaît au survol */
.link-highlight {
  position: relative;
  color: #1e293b;
  text-decoration: none;
  padding: 0.1em 0.3em;
  border-radius: 4px;
}

.link-highlight::before {
  content: "";
  position: absolute;
  inset: 0;
  background: #dbeafe;
  border-radius: 4px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
  z-index: -1;
}

.link-highlight:hover::before {
  transform: scaleX(1);
}

/* Highlight du centre */
.link-highlight-center::before {
  transform-origin: center;
}

Effets avec icônes et flèches

/* Flèche qui glisse au survol */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: #2563eb;
  text-decoration: none;
  font-weight: 600;
}

.link-arrow .arrow {
  transition: transform 0.3s ease;
}

.link-arrow:hover .arrow {
  transform: translateX(4px);
}

/* Flèche qui apparaît au survol */
.link-arrow-reveal {
  display: inline-flex;
  align-items: center;
  gap: 0;
  color: #2563eb;
  text-decoration: none;
  transition: gap 0.3s ease;
}

.link-arrow-reveal .arrow {
  opacity: 0;
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.link-arrow-reveal:hover {
  gap: 0.5rem;
}

.link-arrow-reveal:hover .arrow {
  opacity: 1;
  transform: translateX(2px);
}

Liens en forme de bouton

/* Bouton plein */
.link-btn {
  display: inline-block;
  padding: 0.6em 1.5em;
  background: #2563eb;
  color: white;
  text-decoration: none;
  border-radius: 8px;
  font-weight: 600;
  transition: background 0.3s ease, transform 0.2s ease;
}

.link-btn:hover {
  background: #1d4ed8;
  transform: translateY(-1px);
}

.link-btn:active {
  transform: translateY(0);
}

/* Bouton outline */
.link-btn-outline {
  display: inline-block;
  padding: 0.6em 1.5em;
  border: 2px solid #2563eb;
  color: #2563eb;
  background: transparent;
  text-decoration: none;
  border-radius: 8px;
  font-weight: 600;
  transition: background 0.3s ease, color 0.3s ease;
}

.link-btn-outline:hover {
  background: #2563eb;
  color: white;
}

Liens pill / badge

.link-pill {
  display: inline-block;
  padding: 0.3em 0.8em;
  background: #f1f5f9;
  color: #475569;
  text-decoration: none;
  border-radius: 9999px;
  font-size: 0.85rem;
  font-weight: 500;
  transition: background 0.2s ease, color 0.2s ease;
}

.link-pill:hover {
  background: #2563eb;
  color: white;
}

Lien de navigation avec indicateur actif

/* Barre de navigation */
.nav-links {
  display: flex;
  gap: 0;
  list-style: none;
  border-bottom: 2px solid #e2e8f0;
}

.nav-link {
  position: relative;
  padding: 0.75rem 1.25rem;
  color: #64748b;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.3s ease;
}

.nav-link::after {
  content: "";
  position: absolute;
  bottom: -2px;
  left: 0;
  right: 0;
  height: 2px;
  background: #2563eb;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.nav-link:hover {
  color: #1e293b;
}

.nav-link:hover::after,
.nav-link.active::after {
  transform: scaleX(1);
}

.nav-link.active {
  color: #2563eb;
}

Liens de fil d'Ariane (breadcrumb)

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}

.breadcrumb a {
  color: #2563eb;
  text-decoration: none;
  transition: color 0.2s ease;
}

.breadcrumb a:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

.breadcrumb .separator {
  color: #94a3b8;
  user-select: none;
}

.breadcrumb .current {
  color: #64748b;
  pointer-events: none;
}

Liens réseaux sociaux

/* Icônes sociales en cercle */
.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #f1f5f9;
  color: #64748b;
  text-decoration: none;
  font-size: 1.2rem;
  transition: background 0.3s ease, color 0.3s ease, transform 0.2s ease;
}

.social-link:hover {
  background: #2563eb;
  color: white;
  transform: translateY(-2px);
}

/* GitHub */
.social-link.github:hover { background: #24292e; }
/* Twitter */
.social-link.twitter:hover { background: #1da1f2; }
/* LinkedIn */
.social-link.linkedin:hover { background: #0077b5; }
/* YouTube */
.social-link.youtube:hover { background: #ff0000; }

Skip links (accessibilité)

/* Skip link — caché par défaut, visible au focus */
.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  background: #2563eb;
  color: white;
  padding: 0.75rem 1.5rem;
  text-decoration: none;
  font-weight: 600;
  z-index: 1000;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 0;
}

/* Utilisation */
<a href="#main-content" class="skip-link">
  Aller au contenu principal
</a>

Indicateurs de liens externes

/* Flèche pour liens externes */
a[target="_blank"]::after {
  content: " ↗";
  font-size: 0.8em;
  vertical-align: super;
  opacity: 0.6;
}

a[target="_blank"]:hover::after {
  opacity: 1;
}

/* Icône externes */
.link-external {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.link-external::after {
  content: "🔗";
  font-size: 0.75em;
}

Liens dans différents contextes

/* Lien dans du texte */
.article-content a {
  color: #2563eb;
  text-decoration: underline;
  text-underline-offset: 3px;
  transition: color 0.2s ease;
}

.article-content a:hover {
  color: #1d4ed8;
}

/* Lien dans une carte */
.card-link {
  display: block;
  color: inherit;
  text-decoration: none;
  padding: 1rem;
}

.card-link:hover {
  background: #f8fafc;
}

/* Lien dans la navigation */
.nav a {
  color: #475569;
  text-decoration: none;
  padding: 0.5rem 0.75rem;
  border-radius: 6px;
  transition: background 0.2s ease, color 0.2s ease;
}

.nav a:hover {
  background: #f1f5f9;
  color: #1e293b;
}

/* Lien dans le pied de page */
.footer a {
  color: #94a3b8;
  text-decoration: none;
  transition: color 0.2s ease;
}

.footer a:hover {
  color: white;
}

3. Visualisation

Les 5 états LVHFA

:link Non visité
:visited Visité
:hover Au survol
:active En cours de clic
:focus-visible Focus clavier

Les 5 états d'un lien : LVHFA (Link, Visited, Hover, Active, Focus)

text-decoration en images

underline
overline
line-through
dashed
dotted
double
wavy
offset: 6px

Différentes valeurs de text-decoration et text-underline-offset

Animations de soulignement

Formes de liens

Différentes formes visuelles pour les liens

4. Exemples progressifs

Exemple 1 — Lien stylé de base (LVHFA complet)

a:link    { color: #2563eb; text-decoration: none; }
a:visited { color: #7c3aed; }
a:hover   { color: #1d4ed8; text-decoration: underline; }
a:active  { color: #dc2626; }
a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
  border-radius: 4px;
}

Rendu :

Exemple 2 — Soulignement glissant

.link-slide {
  position: relative;
  color: #2563eb;
  text-decoration: none;
  padding-bottom: 2px;
}

.link-slide::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 2px;
  background: #2563eb;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}

.link-slide:hover::after {
  transform: scaleX(1);
}

Exemple 3 — Highlight de fond

.link-highlight {
  position: relative;
  color: #1e293b;
  text-decoration: none;
  padding: 0.1em 0.3em;
  border-radius: 4px;
}

.link-highlight::before {
  content: "";
  position: absolute;
  inset: 0;
  background: #dbeafe;
  border-radius: 4px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
  z-index: -1;
}

.link-highlight:hover::before {
  transform: scaleX(1);
}

Exemple 4 — Lien en forme de bouton

.link-btn {
  display: inline-block;
  padding: 0.6em 1.5em;
  background: #2563eb;
  color: white;
  text-decoration: none;
  border-radius: 8px;
  font-weight: 600;
  transition: background 0.3s ease, transform 0.2s ease;
}

.link-btn:hover {
  background: #1d4ed8;
  transform: translateY(-1px);
}

.link-btn:active {
  transform: translateY(0);
}

Exemple 5 — Navigation avec indicateur actif

.nav-links {
  display: flex;
  gap: 0;
  list-style: none;
  border-bottom: 2px solid #e2e8f0;
}

.nav-link {
  position: relative;
  padding: 0.75rem 1.25rem;
  color: #64748b;
  text-decoration: none;
  font-weight: 500;
  transition: color 0.3s ease;
}

.nav-link::after {
  content: "";
  position: absolute;
  bottom: -2px;
  left: 0;
  right: 0;
  height: 2px;
  background: #2563eb;
  transform: scaleX(0);
  transition: transform 0.3s ease;
}

.nav-link:hover,
.nav-link.active {
  color: #2563eb;
}

.nav-link:hover::after,
.nav-link.active::after {
  transform: scaleX(1);
}

Exemple 6 — Fil d'Ariane (breadcrumb)

.breadcrumb {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
}

.breadcrumb a {
  color: #2563eb;
  text-decoration: none;
  transition: color 0.2s ease;
}

.breadcrumb a:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

.breadcrumb .separator {
  color: #94a3b8;
}

.breadcrumb .current {
  color: #64748b;
  pointer-events: none;
}

Rendu :

Exemple 7 — Liens réseaux sociaux

.social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: #f1f5f9;
  color: #64748b;
  text-decoration: none;
  font-size: 1.2rem;
  transition: background 0.3s, color 0.3s, transform 0.2s;
}

.social-link:hover {
  background: #2563eb;
  color: white;
  transform: translateY(-2px);
}

.social-link.github:hover { background: #24292e; }
.social-link.twitter:hover { background: #1da1f2; }
.social-link.linkedin:hover { background: #0077b5; }

Rendu :

Exemple 8 — Skip link (accessibilité)

.skip-link {
  position: absolute;
  top: -100%;
  left: 0;
  background: #2563eb;
  color: white;
  padding: 0.75rem 1.5rem;
  text-decoration: none;
  font-weight: 600;
  z-index: 1000;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 0;
}

/* Utilisation : premier élément du body */
<a href="#main-content" class="skip-link">
  Aller au contenu principal
</a>

Rendu :

Navigation... (appuyez sur Tab pour voir le skip link)

Appuyez sur Tab pour révéler le lien de saut

Exemple 9 — Indicateur de lien externe

/* Flèche pour liens target="_blank" */
a[target="_blank"]::after {
  content: " ↗";
  font-size: 0.8em;
  vertical-align: super;
  opacity: 0.6;
  transition: opacity 0.2s ease;
}

a[target="_blank"]:hover::after {
  opacity: 1;
}

Exemple 10 — Liens dans différents contextes

Rendu :

5. Erreurs fréquentes

Erreur n°1 — Inverser l'ordre LVHFA

L'ordre correct est LVHFA. Si vous mettez :hover avant :visited, les styles de :visited peuvent écraser :hover à cause de la spécificité.

/* ❌ MAUVAIS — Ordre incorrect */
a:hover   { color: blue; }
a:visited { color: purple; }
a:link    { color: blue; }

/* ✅ BON — Ordre LVHFA respecté */
a:link    { color: #2563eb; }
a:visited { color: #7c3aed; }
a:hover   { color: #1d4ed8; }
a:active  { color: #dc2626; }
Erreur n°2 — Utiliser :focus au lieu de :focus-visible

:focus s'applique à tout focus, y compris au clic. :focus-visible ne s'applique qu'au focus clavier, ce qui est l'UX souhaitée.

/* ❌ MAUVAIS — Outline visible au clic aussi */
a:focus {
  outline: 2px solid blue;
}

/* ✅ BON — Outline uniquement au clavier */
a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

/* Masquer le focus non-visible */
a:focus:not(:focus-visible) {
  outline: none;
}
Erreur n°3 — Supprimer l'outline sans alternative

outline: none sans :focus-visible casse l'accessibilité clavier. Les utilisateurs clavier ne voient plus où ils se trouvent.

/* ❌ MAUVAIS — Pas d'accessibilité */
a:focus { outline: none; }

/* ✅ BON — Garder le focus clavier */
a:focus:not(:focus-visible) { outline: none; }
a:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }
Erreur n°4 — Ne pas avoir de contraste suffisant

Les couleurs de lien doivent respecter le ratio de contraste WCAG (4.5:1 minimum pour le texte normal). Un lien gris clair sur fond blanc est illisible.

Erreur n°5 — Oublier :active

Beaucoup de développeurs oublient :active. Sans ce feedback, l'utilisateur n'a aucune confirmation visuelle de son clic.

Erreur n°6 — Utiliser :hover pour les informations critiques

:hover n'est pas accessible sur mobile (pas de curseur). Les informations critiques ne doivent pas dépendre uniquement du survol.

6. Bonnes pratiques

Bonne pratique n°1 — Respecter l'ordre LVHFA

Placez toujours les états dans l'ordre : :link, :visited, :hover, :active, :focus.

Bonne pratique n°2 — Utiliser :focus-visible pour l'accessibilité

Remplacez :focus par :focus-visible pour ne montrer l'outline qu'aux utilisateurs clavier.

Bonne pratique n°3 — Toujours fournir un feedback :active

Un changement de couleur ou de style au clic confirme à l'utilisateur que l'action a été déclenchée.

Bonne pratique n°4 — Utiliser des transitions pour les changements d'état

Les transitions fluides entre les états améliorent l'expérience utilisateur. transition: color 0.3s ease suffit souvent.

Bonne pratique n°5 — Respecter les contrastes WCAG

Ratio minimum de 4.5:1 pour le texte normal, 3:1 pour le texte grand. Utilisez des outils comme WebAIM Contrast Checker.

Bonne pratique n°6 — Ajouter un indicateur pour les liens externes

Une flèche ou un icône indique que le lien ouvre un nouvel onglet.

Bonne pratique n°7 — Inclure un skip link pour l'accessibilité

Un lien de saut en début de page permet aux utilisateurs clavier d'aller directement au contenu principal.

Bonne pratique n°8 — Utiliser text-underline-offset pour un soulignement élégant

Un offset de 3-4px sépare le soulignement du texte pour un rendu plus propre.

/* Checklist d'un lien bien stylé */
a:link {
  color: #2563eb;
  text-decoration: none;
}

a:visited {
  color: #7c3aed;
}

a:hover {
  color: #1d4ed8;
}

a:active {
  color: #dc2626;
}

a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
  border-radius: 4px;
}

/* Transition fluide */
a {
  transition: color 0.3s ease;
}

7. Mini-défi

Défi n°1 — Menu de navigation complet

Objectif : Créer un menu de navigation horizontal avec :

  • 5 liens avec l'ordre LVHFA complet
  • Un indicateur actif avec soulignement glissant
  • Focus-visible pour l'accessibilité
  • Transitions fluides entre tous les états
  • Responsive (empilé sur mobile)

Défi n°2 — Collection de styles de liens

Objectif : Créer 5 styles de liens différents :

  • Soulignement glissant (scaleX)
  • Highlight de fond (scaleX)
  • Lien avec flèche animée
  • Bouton lien (plein + outline)
  • Lien pill/badge

Défi n°3 — Skip link + navigation complète

Objectif : Créer une page avec :

  • Un skip link accessible (visible au focus clavier)
  • Une barre de navigation avec liens stylés
  • Un breadcrumb fonctionnel
  • Des liens sociaux avec animations
  • Le tout accessible au clavier

8. Projet — Système de liens complet

Consigne :

Créez un système de liens complet qui combine les concepts vus dans ce chapitre :

  1. Un skip link fonctionnel avec animation
  2. Une barre de navigation avec indicateur actif glissant
  3. Un breadcrumb avec séparateurs
  4. Des liens de texte avec soulignement animé
  5. Des boutons-liens (plein, outline, ghost)
  6. Des liens sociaux avec animations hover
  7. Des indicateurs de liens externes
  8. Le tout doit être 100% accessible au clavier

9. Révision

Récapitulatif des concepts clés

Concept Propriétés Usage principal
États LVHFA :link, :visited, :hover, :active, :focus-visible Styles différents par état d'interaction
Décoration text-decoration, text-underline-offset Soulignement, barré, survolé, none
Focus :focus-visible, outline, outline-offset Accessibilité clavier
Transitions transition, transform Soulignement glissant, highlights
Formes display, padding, border-radius Boutons, pills, navigation
Pseudo-éléments ::after, ::before Soulignement custom, icônes, highlights
Navigation Combinaison de classes Nav bar, breadcrumb, tabs
Accessibilité :focus-visible, skip link, aria-label Navigation clavier, screen readers

Quiz rapide

  1. Quel est l'ordre correct des pseudo-classes pour les liens ?
  2. Quelle est la différence entre :focus et :focus-visible ?
  3. Comment créer un soulignement glissant de gauche à droite ?
  4. Pourquoi faut-il éviter outline: none sans alternative ?
  5. Comment indiquer qu'un lien ouvre un nouvel onglet ?
Réponses :
  1. LVHFA : :link, :visited, :hover, :active, :focus
  2. :focus s'applique à tout focus, :focus-visible uniquement au focus clavier
  3. Avec ::after, transform: scaleX(0→1), transform-origin: left, et transition
  4. Il casse l'accessibilité clavier — les utilisateurs clavier ne voient plus le focus
  5. Avec target="_blank" et un indicateur visuel (flèche )

Cheat sheet

/* Liens de base LVHFA */
a:link    { color: #2563eb; text-decoration: none; }
a:visited { color: #7c3aed; }
a:hover   { color: #1d4ed8; }
a:active  { color: #dc2626; }

/* Focus accessible */
a:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
  border-radius: 4px;
}

a:focus:not(:focus-visible) { outline: none; }

/* Soulignement glissant */
.link-slide::after {
  content: "";
  position: absolute;
  bottom: 0; left: 0;
  width: 100%; height: 2px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
}
.link-slide:hover::after { transform: scaleX(1); }

/* Highlight fond */
.link-hl::before {
  content: "";
  position: absolute; inset: 0;
  background: #dbeafe;
  border-radius: 4px;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease;
  z-index: -1;
}
.link-hl:hover::before { transform: scaleX(1); }

/* Bouton lien */
.link-btn {
  display: inline-block;
  padding: 0.6em 1.5em;
  background: #2563eb;
  color: white;
  text-decoration: none;
  border-radius: 8px;
  transition: background 0.3s;
}

/* Skip link */
.skip-link {
  position: absolute;
  top: -100%; left: 0;
  background: #2563eb; color: white;
  padding: 0.75rem 1.5rem;
  z-index: 1000;
  transition: top 0.2s;
}
.skip-link:focus { top: 0; }

/* External link indicator */
a[target="_blank"]::after {
  content: " ↗";
  font-size: 0.8em;
  vertical-align: super;
}