Skip to Content

Chapitre 8.2 — SVG & icônes

⏱️ TL;DR — Un <svg> n’est pas une <img> : il n’a pas d’attribut alt. Pour le rendre accessible, on distingue deux cas. Informatif (il porte du sens) → role="img" + un nom via <title> ou aria-label. Décoratif (pur ornement) → aria-hidden="true" et focusable="false" pour le retirer de l’arbre d’accessibilité. Cas le plus fréquent et le plus raté : une icône seule dans un bouton (icon-only). Sans texte visible, ce bouton doit recevoir un nom accessible, sinon le lecteur d’écran annonce « bouton » tout court — inutilisable.

🎯 Objectifs

  • Rendre un SVG informatif accessible avec role="img" + <title> ou aria-label.
  • Neutraliser un SVG décoratif avec aria-hidden="true" et focusable="false".
  • Donner un nom accessible à une icône seule dans un bouton ou un lien.
  • Éviter les pièges des icon fonts (icônes en police de caractères).

Le SVG n’a pas de alt

L’attribut alt n’existe que sur <img>. Un SVG inline (<svg>…</svg> directement dans le HTML) est un arbre d’éléments graphiques : par défaut, son accessibilité est incohérente d’un lecteur d’écran à l’autre. On la rend explicite. Deux questions, comme pour les images : ce SVG porte-t-il une information, ou est-il décoratif ?

SVG informatif

Il transmet du sens (un pictogramme qui remplace du texte, un logo, un schéma simple). On lui donne :

  • role="img" : on annonce à l’arbre d’accessibilité « ceci est une image » (et on masque sa structure interne).
  • un nom accessible : soit un <title> en premier enfant du SVG, soit aria-label sur le <svg>.
<!-- ✅ SVG informatif avec <title> --> <svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <title>Cours terminé</title> <path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" /> </svg> <!-- ✅ Équivalent avec aria-label --> <svg role="img" aria-label="Cours terminé" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> <path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" /> </svg>

En JSX/React, <title> fonctionne pareil ; pense juste à lier un id si tu utilises aria-labelledby.

// ✅ SVG informatif en React function IconValide() { return ( <svg role="img" aria-labelledby="t-valide" viewBox="0 0 24 24"> <title id="t-valide">Cours terminé</title> <path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z" /> </svg> ); }

SVG décoratif

Il n’ajoute rien à ce qui est déjà écrit (une icône posée à côté d’un mot qui dit déjà la même chose, un ornement). On le sort de l’arbre d’accessibilité :

  • aria-hidden="true" : le lecteur d’écran l’ignore totalement.
  • focusable="false" : évite qu’en Internet Explorer / vieux navigateurs le SVG capte le focus au clavier (piège historique).
<!-- ✅ Icône décorative à côté d'un texte qui dit déjà tout --> <a href="/telecharger"> <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24"><path d="M12 3v12m0 0-4-4m4 4 4-4M5 21h14" /></svg> Télécharger le support </a>

Ici le lien dit déjà « Télécharger le support » : l’icône flèche est un doublon visuel, on la masque. Le lecteur d’écran annonce « Télécharger le support, lien » — parfait.

Le cas critique : l’icône seule (icon-only)

Un bouton ou un lien qui ne contient qu’une icône, sans aucun texte visible, est le piège n°1 des interfaces modernes. Sans nom accessible, le lecteur d’écran n’a rien à annoncer : « bouton ». L’utilisateur ne sait pas ce qu’il fait.

La règle est absolue : une icône seule dans un contrôle DOIT avoir un nom accessible. Deux techniques.

<!-- ❌ Rien à annoncer : « bouton », sans plus --> <button> <svg viewBox="0 0 24 24"><path d="M15.5 14h-.8l-.3-.3a6.5 6.5 0 1 0-.7.7l.3.3v.8l5 5 1.5-1.5-5-5zM10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8z" /></svg> </button> <!-- ✅ Nom sur le bouton via aria-label + icône masquée --> <button aria-label="Rechercher"> <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24"><path d="M15.5 14h-.8l-.3-.3a6.5 6.5 0 1 0-.7.7l.3.3v.8l5 5 1.5-1.5-5-5zM10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8z" /></svg> </button> <!-- ✅ Variante : texte masqué visuellement (visually-hidden), lu par le lecteur d'écran --> <button> <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24"><path d="M15.5 14h-.8l-.3-.3a6.5 6.5 0 1 0-.7.7l.3.3v.8l5 5 1.5-1.5-5-5zM10 14a4 4 0 1 1 0-8 4 4 0 0 1 0 8z" /></svg> <span class="visually-hidden">Rechercher</span> </button>

Deux approches équivalentes : aria-label sur le contrôle (avec l’icône en aria-hidden), ou un <span class="visually-hidden"> avec un vrai texte. La seconde a un avantage : le texte survit si le CSS ou la traduction automatique casse aria-label. Dans les deux cas, l’icône est masquée pour ne pas parasiter le nom.

⚠️ Piège — Le bouton icône muet est partout : menu burger, fermeture de modale (la croix ×), like, partage, corbeille. Chacun sans nom accessible devient un « bouton » anonyme pour le lecteur d’écran. Passe ton interface en revue : tout contrôle qui n’a pas de texte visible doit avoir un aria-label ou un texte masqué. C’est le défaut le plus fréquent des audits de composants.

Les icon fonts, un cas piégeux

Certaines libs affichent les icônes via une police de caractères (<i class="fa fa-trash"></i>) : le glyphe est en réalité un caractère. Problèmes : selon le lecteur d’écran, ce caractère peut être lu à voix haute (un symbole aléatoire), ou ignoré. Toujours :

  • masquer le glyphe : aria-hidden="true" sur l’élément <i> porteur ;
  • donner le nom sur le contrôle (aria-label) ou via un texte masqué.
<!-- ✅ Icon font neutralisée + nom accessible --> <button aria-label="Supprimer"> <i class="fa fa-trash" aria-hidden="true"></i> </button>

Quand tu peux choisir, préfère des SVG inline aux icon fonts : plus nets, plus contrôlables, et l’accessibilité y est explicite.

Côté utilisateur — Sur une barre d’outils de six boutons icônes sans nom, un utilisateur de lecteur d’écran entend « bouton, bouton, bouton, bouton, bouton, bouton ». Il ne peut ni les distinguer ni deviner lequel supprime, lequel partage. Avec des noms : « Modifier, bouton », « Partager, bouton », « Supprimer, bouton »… La barre redevient utilisable. Une icône, c’est du sens visuel ; le nom accessible en est la traduction.

🧭 Sur A11yLearn — La barre d’actions d’une leçon (marquer comme lue, mettre en favori, partager, signaler) n’affiche que des icônes SVG pour rester compacte. On a systématisé : chaque <button> porte un aria-label explicite (« Marquer comme lue », « Ajouter aux favoris »…) et chaque <svg> interne est en aria-hidden="true" focusable="false". Un lint maison refuse en CI tout <button> sans texte ni aria-label : impossible d’introduire un bouton icône muet.

✏️ Exercices

Exercice 1 — Informatif ou décoratif ? Pour chaque SVG, dis comment le rendre accessible : (a) une icône « cadenas » seule dans un bouton « verrouiller le cours », (b) une petite étoile posée juste avant le mot visible « Favori », (c) un pictogramme d’avertissement qui remplace le mot « Attention » (aucun texte à côté).

✅ Solution

(a) Fonctionnelle/informative, icône seule → nom sur le bouton : aria-label="Verrouiller le cours", SVG en aria-hidden="true" focusable="false". (b) Décorative (le mot « Favori » est déjà là) → aria-hidden="true" focusable="false". (c) Informative (elle remplace un mot) → role="img" + <title>Attention</title> ou aria-label="Attention".

Exercice 2 — Corrige le bouton de fermeture. Ce bouton ferme une modale. Rends-le accessible.

<button onclick="closeModal()"> <svg viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18" /></svg> </button>

✅ Solution

L’icône croix est seule : le bouton n’a aucun nom. On ajoute un nom accessible et on masque l’icône.

<button onclick="closeModal()" aria-label="Fermer"> <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18" /></svg> </button>

Le lecteur d’écran annonce alors « Fermer, bouton ».

🧠 Quiz de révision

1. Pourquoi un <svg> ne se traite-t-il pas comme une <img> ?

Parce qu’un SVG inline n’a pas d’attribut alt et que, par défaut, son accessibilité varie d’un lecteur d’écran à l’autre. On l’explicite : role="img" + <title>/aria-label s’il est informatif, ou aria-hidden="true" + focusable="false" s’il est décoratif.

2. Comment nommer un SVG informatif ?

Avec role="img" sur le <svg>, puis un nom accessible : soit un <title> en premier enfant (éventuellement lié par aria-labelledby), soit aria-label directement sur le <svg>.

3. Que mettre sur un SVG purement décoratif ?

aria-hidden="true" pour le retirer de l’arbre d’accessibilité, et focusable="false" pour éviter qu’il capte le focus au clavier dans certains navigateurs. Le lecteur d’écran l’ignore alors totalement.

4. Un bouton ne contient qu’une icône. Quelle est la règle ?

Il doit avoir un nom accessible, sinon le lecteur d’écran annonce « bouton » sans plus. On met un aria-label sur le bouton (avec l’icône en aria-hidden), ou un texte masqué (visually-hidden) à l’intérieur.

5. Quel est le risque avec une icon font ?

Le glyphe est un caractère : selon le lecteur d’écran, il peut être lu à voix haute (symbole incompréhensible) ou ignoré. On masque toujours le porteur (aria-hidden="true") et on donne le nom sur le contrôle. Les SVG inline sont préférables quand on a le choix.


Chapitre suivant : Images complexes — décrire graphiques, schémas et cartes par une description longue ou un tableau.

Last updated on