Comment travailler avec des icônes en SVG

Un bel article de notre frenchie Florens Verschelde qui a fait le buzz et a été repris par l'excellent CSS-Tricks. Explications claires du process et réflexions pour aller plus loin.

Par

Il existe bien des façons d’utiliser les icônes SVG en HTML et en CSS, et je ne les ai pas toutes essayées. Ce qui suit correspond à la façon de travailler de la petite équipe front-end de Kaliop. Elle répond à nos besoins, qui comprennent :

Des sites web de communication et de contenu, souvent basés sur de gros CMS, plutôt que des applications full-JavaScript.
Des icônes qui sont souvent de simples icônes monochromes — chacune pouvant être utilisée en différentes couleurs, selon le contexte et les interactions utilisateur — mais possibilité d’avoir des icônes en bichromie.
Compatibilité navigateurs IE9+.

Table des matières

Préparer vos icônes

Lorsque vous recevez une icône SVG, conçue par un designer ou livrée avec un outil graphique (Illustrator, Adobe Assets, Sketch, Inkscape, etc.), il peut être tentant de l’inclure directement dans votre projet. Cependant, une petite révision dans votre outil favori peut vous épargner quelques maux de tête et améliorer significativement le résultat.

Une icône simple sur un plan de travail Illustrator (à gauche) et sur un artboard Sketch (à droite)

Travailler avec un nouveau document ou artboard

Créez un nouveau document ou un nouvel artboard (plan de travail) dans votre outil favori et copiez-collez votre icône au centre. C’est une excellente façon de vous assurer que votre icône soit propre et ne contienne pas une tonne de chemins cachés.

La coupe au carré c’est plus facile

Votre icône ne doit pas nécessairement être un carré, mais les icônes carrées sont plus faciles à traiter (sauf si votre graphique est vraiment très haut ou très large).

Les dimensions exactes importent seulement si vous voulez gérer au pixel près pour obtenir les résultats les plus nets possible sur les écrans à faible densité. Par exemple, si toutes vos icônes tiennent sur une grille de 15px × 15px, et si elles sont utilisées le plus souvent avec ces dimensions exactes, vous pouvez y aller et continuer à travailler avec des artboards ou des documents de 15 × 15. Quand je ne suis pas sûr, je préfère utiliser un artboard de 20 × 20.

Bien dégagé autour des oreilles

Laissez un peu de marge autour de la forme, surtout si elle est arrondie. Les navigateurs utilisent l’anticrénelage (anti-aliasing) pour rendre les formes SVG mais parfois les pixels supplémentaires de l’anticrénelage sont rendus en dehors de la viewBox et sont coupés.

Nous n’avons pas laissé d’espace autour de l’icône, donc il y a des chances qu’elle soit rendue avec des côtés légèrement carrés. Et si le navigateur ne rend pas le SVG à la perfection, ça peut être encore pire.

Une bonne règle générale, pour une icône de 16 ou 20px, serait de laisser un espace de 0.5 ou 1px de chaque côté. De plus, n’oubliez pas d’exporter tout l’artboard, pas seulement les chemins sélectionnés, sinon vous perdriez l’espace autour de la forme.

Exporter en SVG

  • dans Illustrator, j’utilise simplement “Save as” et je choisis le format “SVG”.
  • dans Inkscape, vous pouvez faire “Save as” et choisir “Optimized SVG”.
  • dans Sketch, vous pouvez sélectionner un artboard, cliquer sur “Make exportable” en bas à droite et choisir le format “SVG”.

Apprendre un peu de SVG

Il est vraiment utile de connaître les bases de SVG pour pouvoir lire et comprendre la structure de fichiers SVG simples. Au minimum, vous devriez connaître :

  • Elements: <svg>, <symbol>, <g>, <path>.
  • Attributes: d, fill, stroke, stroke-width.

Remarque : quand vous exportez un SVG depuis un outil de design, il comprendra souvent du markup et des métadonnées inutiles, ainsi que des données excessivement précises pour le chemin (dans l’attribut d). Essayez l’outil SVGOMG and comparez le code avant et après pour voir ce qui est supprimé ou simplifié.

Supprimer les données de couleurs

Pour les icônes monochromes, assurez-vous que :

  1. dans votre fichier source, les chemins sont noirs (#000000)
  2. dans le code exporté, il n’y a pas d’attribut fill.

Si nous avons des fill dans le code SVG, nous ne pourrons pas remplacer ces couleurs à partir de notre code CSS. Il est donc préférable de les supprimer, du moins pour les icônes monochromes.

Illustrator ne produit pas d’attributs fill pour les chemins qui sont entièrement noirs (#000000). Par contre, Sketch crée ces attributs, donc il vous faudra sans doute supprimer manuellement les attributs fill="#000000".

Créer un sprite SVG

Cette partie comprend pas mal de code mais il n’est pas compliqué du tout. Nous voulons créer un document SVG contenant des éléments <symbol>. Chaque <symbol> doit avoir un attribut id et un attribut viewBox, et contiendra les éléments <path/> de l’icône (ou d’autres éléments graphiques).

J’appelle sprite ce document SVG en référence aux sprites dans les jeux vidéos et CSS, mais on pourrait aussi bien l’appeler sprite sheet (feuille de sprites) ou symbol store (entrepôt de symboles).

Voici un sprite contenant une seule icône :

<svg xmlns="http://www.w3.org/2000/svg">  
  <symbol id="cross" viewBox="0 0 20 20">
    <path d="M17.1 5.2l-2.6-2.6-4.6 4.7-4.7-4.7-2.5 2.6 4.7 4.7-4.7 4.7 2.5 2.5 4.7-4.7 4.6 4.7 2.6-2.5-4.7-4.7"/>
  </symbol>
</svg>  

Ajouter une icône à notre sprite

Imaginons qu’Illustrator nous ait donné ce code pour une nouvelle icône :

<?xml version="1.0" encoding="utf-8"?>  
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->  
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"  
  viewBox="0 0 15 15" style="enable-background:new 0 0 15 15;" xml:space="preserve">
  <path id="ARROW" d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
</svg>  

Nous pouvons la simplifier (manuellement ou via SVGOMG), en ne conservant que l’attribut viewBox et les données essentielles :

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15">  
  <path d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
</svg>  

Nous pouvons maintenant la copier/coller dans notre sprite. Nous devons transformer l’élément <svg viewBox="…"> en élément <symbol id="…" viewBox="…"> et l’insérer manuellement dans notre sprite :

<svg xmlns="http://www.w3.org/2000/svg">  
  <symbol id="cross" viewBox="0 0 20 20">
    <path d="M17.1 5.2l-2.6-2.6-4.6 4.7-4.7-4.7-2.5 2.6 4.7 4.7-4.7 4.7 2.5 2.5 4.7-4.7 4.6 4.7 2.6-2.5-4.7-4.7"/>
  </symbol>
  <symbol id="play" viewBox="0 0 15 15">
    <path d="M7.5,0.5c3.9,0,7,3.1,7,7c0,3.9-3.1,7-7,7c-3.9,0-7-3.1-7-7l0,0C0.5,3.6,3.6,0.5,7.5,0.5 C7.5,0.5,7.5,0.5,7.5,0.5L7.5,0.5L7.5,0.5z M6.1,4.7v5.6l4.2-2.8L6.1,4.7z"/>
  </symbol>
</svg>  

On peut le faire à la main pour chaque icône, mais il existe des outils permettant d’automatiser le processus. Nous utilisons gulg-svg-sprite (voici un exemple réel de fichier gulpfile.js si vous êtes curieux), mais il y a d’autres outils graphiques ou en ligne qui exportent les sprites de symboles SVG, dont Icomoon.

Astuce : Créez un dossier pour vos icônes sources

Si vous réalisez vos sprites manuellement, je vous recommande de créer un dossier contenant chaque icône :

assets/  
    icons/
        cross.svg
        play.svg
        search.svg
        ...
public/  
    sprite/
        icons.svg

Par la suite, si vous devez reconstruire le icons.svg ou modifier une icône individuelle, vous avez les sources pour travailler. Veillez à ne pas laisser votre sprite et votre dossier source désynchronisés.

Évidemment, si vous utilisez un build process (avec Grunt ou Gulp ou autre), vous pouvez lui donner votre dossier source et le laisser créer le sprite directement.

Ajouter les icônes à votre page

La mauvaise nouvelle est que si nous voulons utiliser nos icônes SVG, nous devons les intégrer au HTML. Pas de backgrounds CSS, pas de pseudo-éléments ::before. La bonne nouvelle, c’est que ce n’est pas du tout verbeux :

<svg><use xlink:href="/path/to/icons.svg#play"></use></svg>  
une icone play simple, triangle blanc sur disque noir

Fournir un texte alternatif

Il existe plusieurs façons d’ajouter un texte alternatif à une icône. Sur la base de nos test de lecteurs d’écran, voici celle que nous utilisons pour une meilleure accessibilité.

Tout d’abord, si nous ne voulons pas ajouter de texte alternatif (en général parce qu’il y a déjà un texte suffisant dans le contexte), nous utilisons aria-hidden="true" pour garantir que les lecteurs d’écran ne liront pas l’icône à haute voix :

<a href="/news/">  
  <svg aria-hidden="true">
    <use xlink:href="/path/to/icons.svg#newspaper"></use>
  </svg>
  Latest News
</a>  

Le second cas d’usage ressemble à ceci : nous avons un lien ou un bouton dont le seul contenu est une icône. Dans ce cas, nous utilisons aria-label, de préférence sur l’élément <a> ou <button> :

<a href="/news/" aria-label="Latest News">  
  <svg aria-hidden="true">
    <use xlink:href="/path/to/icons.svg#newspaper"></use>
  </svg>
</a>  

Une autre option consiste à utiliser l’élément <title>. Il est particulièrement utile en dehors des éléments interactifs (où aria-label pourrait ne pas être lu par les lecteurs d’écran). Par exemple, si vous montrez des marqueurs oui/non (type “hirondelle” ou croix) dans une colonne de tableau, vous pourriez avoir :

<td>  
  <svg>
    <title>Yes</title>
    <use xlink:href="/path/to/icons.svg#tick"></use>
  </svg>
</td>  

Enfin, n’oubliez pas que :

  • votre texte accessible peut changer selon le contexte (une icône “loupe” peut signifier “montrer le formulaire de recherche” dans un contexte, et “envoyer ma requête” dans un autre).
  • votre texte accessible doit changer lorsque la langue de votre page est modifiée.

C’est pourquoi votre texte alternatif doit être situé dans le HTML où est inséré votre icône. Certains articles recommandent de mettre les éléments <title> dans votre sprite, mais en pratique ça ne fonctionne pas — et de plus la plupart des lecteurs d’écran les ignoreront.

Sprites externes et en ligne

Jusqu’à présent nous avons montré des exemples de sprite externe. Mais certains navigateurs anciens — en particulier les WebKits anciens et toutes les versions d’Internet Explorer (avant Edge 13) — ne supportent les références en ligne que pour <use xlink:href="#some-id"/>.

On peut utiliser un polyfill pour cela (svg4everybody, svgxuse). Ou bien, vous pouvez choisir d’inclure votre sprite dans le code HTML de chaque page.

<body>  
  <!-- Données cachées -->
  <svg aria-hidden="true" style="display:none">
    <symbol id="icon-play">…</symbol>
    <symbol id="icon-cross">…</symbol>
    <symbol id="icon-search">…</symbol>
  </svg>

  <!-- Une icône visible : -->
  <button aria-label="Start playback">
    <svg aria-hidden="true"><use xlink:href="#icon-play"/></svg>
  </button>
</body>  

Chaque méthode a ses pour et ses contre :

Sprite en ligne Sprite externe
Support navigateur

Support natif in IE9+.

Safari/WebKit ancien mettre le sprite au début de la page (avant toute référence).

Support natif dans Edge 13+, Safari 9+.

IE Safari/WebKit anciens, utiliser un polyfill comme svg4everybody or svgxuse.

Chargement d’un sprite externe depuis un domaine différent impossible (même avec CORS, voir Chromium bug p.ex.). Polyfill possible avec svgxuse.

Cache

Pas de cache.

Le poids de votre sprite (5KB, 15KB, 50KB…) est ajouté à chaque page.

Le sprite est un fichier séparé et peut être mise en cache par le navigateur.

De plus il ne gonfle pas votre cache HTTP côté serveur.

Vitesse de rendu

Rendu instantané des icônes.

Sur les réseaux lents, le rendu de la structure et du contenu de la page peut être retardé si vous avez un sprite SVG lourd inséré avant votre contenu.

Attention au FONSI! (Flash of No SVG Icons, mais bon, cet acronyme est une blague, à ne pas réutiliser ok ?)

Les icônes peuvent apparaître avec un petit retard parce que (a) elles requièrent une requête HTTP séparée et (b) le navigateur n’a pas considéré le chargement de ce fichier comme une priorité (via son look-ahead pre-parser).

Mises à jour

Quand vous mettez à jour votre sprite, vous devez vous assurer qu’il est ajouté à chaque page. Soit en régénérant toutes les pages HTML si vous utilisez un générateur statique, soit en invalidant tous les caches.

Seul un fichier public est modifié, donc la gestion des mises à jour et du cache côté-client est plus facile.

J’aime bien combiner les deux méthodes, en créant deux sprites :

  1. un petit, contenant les icônes essentielles (p.ex. les icônes principales utilisées dans le header), à insérer en ligne dans chaque page. Taille cible : 5KB ou moins.
  2. un plus grand, contenant toutes les icônes du projet, et conservé comme un fichier externe. Taille cible : 50KB ou moins.

Sur de plus gros projets, on pourrait ajouter d’autres sprites externes lorsque certaines icônes peuvent être regroupées et ne sont utilisées que dans une partie du site pour des fonctionnalités spécifiques.

Appliquer des styles CSS aux icônes

OK, nous avons nos icônes et nos sprites SVG, nous savons comment ajouter des icônes à notre HTML et ça nous a pris un bon bout de temps, pouvons-nous enfin passer au style ?

Bien sûr, je vous mets juste une ou deux classes d’abord

Vous pourriez sélectionner tous les éléments <svg> dans CSS mais ce n’est pas idéal si vous utilisez SVG pour plusieurs icônes. De plus, il y a un bug connu de Firefox qui pourrait vous jouer des tours si faisions comme cela (voir plus bas), donc évitons.

À la place, je recommande d’ajouter deux classes pour chaque icône : une générique et une portant le nom de l’icône.

<svg class="Icon Icon--arrow" aria-hidden="true">  
  <use xlink:href="/path/to/icons.svg#arrow"></use>
</svg>  

Nous utilisons les conventions de nommage SUIT CSS, mais vous pouvez utiliser un style différent (par exemple class="icon-arrow" est plus court et il peut être ciblé sur toutes les icônes via un sélecteur comme svg[class*="icon-"]).  NdT : pour plus d’infos sur le ciblage via les sélecteurs ou les attributs, voir l’article Attribut du Dico CSS.

Style d’icône par défaut

Je recommande ce style par défaut pour vos icônes :

.Icon {
  /* Permet de redimensionner en changeant la font-size de l’icône */
  width: 1em; height: 1em;
  /* Bel alignement visuel des icônes avec le texte */
  vertical-align: -0.15em;
  /* fill par défaut = valeur de la propriété couleur de son élément parent */
  fill: currentColor;
  /* Les paths et les strokes qui dépassent de la viewBox peuvent apparaître dans IE.
     Si vous utilisez normalize.css, inutile de l’ajouter. */
  overflow: hidden;
}
Icônes avec notre style par défaut. la seule différence entre la rangée du haut et celle du bas est la font-size et la couleur du container.

Ensuite, si vous voulez personnaliser une icône dans un contexte spécifique, vous pouvez ajouter des styles comme ceux-ci :

.MyComponent-button .Icon {
  /* Modifier la largeur et la hauteur */
  font-size: 40px;
  /* Changer la couleur de remplissage (fill) */
  color: purple;
  /* Changer l’alignement vertical si vous avez besoin de plus de précision
     (parfois nécessaire pour un rendu parfait au pixel près) */
  vertical-align: top;
}

Avec ce code, vos icônes SVG devraient être petites par défaut et utiliser la couleur du texte de l’élément parent.

Si les formes de l’icône n’héritent pas de la couleur du texte de l’élément parent (currentColor), vérifiez que vous n’avez pas des attributs fill dans le code de votre icône.

Styles SVG hérités

De nombreuses propriétés de style SVG sont héritées. Par exemple lorsque nous fixons la propriété CSS fill sur notre élément <svg> conteneur, elle se propage à notre <path>, <circle> et autres éléments graphiques.

Nous pouvons utiliser cette technique pour d’autres propriétés CSS SVG, par exemple les propriétés stroke :

.Icon--goldstar {
  fill: gold;
  stroke: coral;
  stroke-width: 5%;
  stroke-linejoin: round;
}
Icône d’étoile, avec les styles par défaut et personnalisé.

La plupart du temps nous ne changerons pas grand chose : uniquement la propriété fill pour la couleur principale et parfois nous ajouterons ou modifierons une stroke (un peu comme une bordure).

Deux couleurs de remplissage par icône

Voici une technique assez simple pour permettre à une icône d’avoir deux chemins avec deux valeurs de fill (autrement dit deux couleurs).

<symbol id="check" viewBox="0 0 20 20">  
  <!-- héritera de la valeur de la propriété CSS fill -->
  <path d="…" />
  <!-- héritera de la valeur de la propriété CSS color -->
  <path fill="currentColor" d="…" />
</symbol>  

...et le CSS :

.Icon--twoColors {
  fill: rebeccapurple;
  color: mediumturquoise;
}
Des icônes en deux couleurs.

Laisser un peu d’espace pour les traits

Vous vous rappelez ce que nous disions à propos de l’espace à laisser autour de nos formes ? C’est d’autant plus important si vous utilisez les strokes (les traits, dont on peut régler l’épaisseur).

.Icon--strokespace {
  fill: none;
  stroke: currentColor;
  stroke-width: 5%;
}

Dans SVG, les strokes sont peints des deux côtés du chemin. Si votre chemin touche les limites du viewport, la moitié du chemin sera coupée, faute d’avoir laissé un espace autour de la forme.

Dans cet exemple, la première icône n’a pas d’espace réservé sur les côtés, la seconde en a un peu (0.5px pour un viewport de 15px).

Utiliser des pourcentages pour stroke-width

Donner l’épaisseur correcte à un trait peut relever du défi. Regardez ces deux exemples dans lesquels nous utilisons stroke-width:1px sur l’élément <svg> :

Deux icônes en forme de coeur avec deux traits d’épaisseur différente

Que se passe-t-il ? La propriété stroke-width accepte une valeur de "longueur" mais cette valeur est relative aux coordonnées locales de votre icône. Dans les exemples ci-dessus :

  1. la première icône a une viewBox de 20px × 20px. Donc un trait de 1px a une épaisseur de 1/20e de la taille de l’icône, c’est important mais pas trop grand.
  2. la deuxième icône a une viewBox de 500px × 500px, et donc un stroke de 1px a une épaisseur de 1/500e de la taille de l’icône, et c’est vraiment très petit.

Si toutes nos icônes utilisent une viewbox identique, ce n’est pas un problème. Par contre, si elles diffèrent grandement, qu’elles utilisent des pixels ou des valeurs sans unité (stroke-width:1), on est mal. Que faire ?

Les pourcentages peuvent nous aider. Le même exemple avec stroke-width:5%.

Deux icônes en forme de coeur avec des traits de même épaisseur

Voilà qui est mieux. Pour les icônes carrées, stroke-width:N% marchera parfaitement, mais remarquez qu’il peut se comporter différemment pour les éléments SVG larges ou hauts, voyez la partie avancée ci-dessous.

Tout n’est pas icône

Ce n’est pas parce que vous avez un SVG qu’il doit obligatoirement se retrouver dans un sprite. Par exemple :

  • les illustrations que vous n’avez pas besoin de styler : utilisez plutôt l’élément <img>
  • les illustrations que vous voulez animer : insérez plutôt le <svg> en ligne dans votre page, de façon à pouvoir sélectionner et styler des groupes spécifiques ou des chemins ou des formes, animer certaines parties, etc.

Une bonne règle générale pourrait être que si c’est une grande illustration que vous devez afficher à 100px × 100px ou plus, ou si elle contient des dizaines d’éléments, ce n’est pas une “icône”.

Sujets avancés et astuces

Nous avons vu dans la section précédente tout ce dont vous avez besoin pour utiliser les icônes SVG. Cette section avancée ajoute des informations plus complexes.

Éviter les grandes icônes non stylées

Que se passe-t-il si votre feuille de style principale ne se charge pas parce que l’utilisateur a une connexion peu fiable, par exemple dans un train (ça m’arrive tout le temps) ? La page peut toujours s’afficher sans styles. Si vous avez une structure HTML correcte, la page sera lisible mais vos icônes apparaîtront vraiment trop grandes.

Les navigateurs récents dimensionnent l’élément svg à 300px par 150px par défaut. D’autres navigateurs peuvent leur donner une largeur géante de 100% !

Je recommande d’insérer ceci dans la partie <head> de vos pages :

<style>.Icon{width:1em;height:1em}</style>  

Bref & concis.

Précharger les sprites externes

Dans la section Ajouter des icônes à vos pages, nous avons dit que les icônes chargées depuis un sprite externe peuvent apparaître avec un temps de retard, entre autres parce que le scan de préchargement du navigateur (ou look-ahead ou pre-parser) ne comprend pas que <use xlink:href="/path/to/icons.svg#something"></use> signifie que nous avons un fichier important à charger le plus tôt possible.

Que faire pour y remédier ?

  • solution standard (et future) : ajoutez <link rel="preload" href="/path/to/icons.svg" as="image"> dans la partie <head> de la page (détails sur preload, compatible bientôt dans tous les bons Chromes proches de chez vous et espérons-le dans les autres navigateurs par la suite).
  • solution vieille école : ajoutez <img style="display:none" alt="" src="/path/to/icons.svg"> au début de votre <body>.

Je n’ai pas testé ces solutions, en général le compromis “en ligne + externe” donne des performances suffisamment bonnes pour qu’on n’ait pas à recourir au préchargement. Mais ça vaudrait de le coup d’y regarder de plus près.

Sélectionner les formes et chemins individuels

Nous avons vu comment personnaliser les fills et les strokes pour tous les chemins d’un <symbol> et deux ou plus de couleurs de différents chemins. Mais il serait intéressant de pouvoir sélectionner des chemins spécifiques (en utilisant des classes, peut-être ?) directement dans l’instance du <symbol>. Est-ce possible ?

Là, maintenant, la réponse est : oui et non.

  1. Si vous utilisez des sprites externes, vous ne pouvez pas sélectionner de chemins individuels (ou d’autres éléments) à l’intérieur d’un <symbol> utilisé.
  2. Si votre sprite est en ligne, vous pouvez sélectionner et styler des éléments à l’intérieur du sprite, mais ces styles s’appliqueront à toutes les instances des symboles.

Donc, même avec un sprite en ligne, vous pourriez faire ceci :

#my-symbol .style1 {
  /* Styles pour un groupe de chemins */
}
#my-symbol .style2 {
  /* Styles pour un autre groupe */
}

Mais pas cela :

.MyComponent-button .Icon .style1 {
  /* Pour 1 groupe de chemins pour cette icône dans ce contexte */
}
.MyComponent-button .Icon .style2 {
  /* Styles pour un autre groupe */
}

Sauf dans Firefox ! Il se trouve que dans Firefox la sélection à l’intérieur d’une instance du symbole fonctionne parfaitement. Le seul problème est que c’est un comportement non standard, donc il n’y a aucune chance que les autres navigateurs s’alignent. En fait, il serait même souhaitable que FF corrige ce bug.

À l’avenir, il y aura peut-être une façon standard de sélectionner via le shadow DOM, mais ce n’est pas sûr du tout (il existait un combinateur /deep/ mais il a été supprimé).

Plus de deux couleurs avec des propriétés CSS personnalisées

Comme nous l’avons vu, nous pouvons facilement changer les couleurs de nos icônes SVG à partir de CSS pour des icônes monochromes (c’est facile) et en bichromie (ça demande une petite préparation). Existe-t-il une façon de réaliser des icônes multicolores avec plus de deux couleurs personnalisables ?

Nous pourrions y parvenir avec les propriétés CSS personnalisées (également connues comme Variables CSS). Cela demande une petite préparation du côté de notre SVG :

<symbol id="iconic-aperture" viewBox="0 0 128 128">  
  <path fill="var(--icon-color1)" d="…" />
  <path fill="var(--icon-color2)" d="…" />
  <path fill="var(--icon-color3)" d="…" />
  <path fill="var(--icon-color4)" d="…" />
  <path fill="var(--icon-color5)" d="…" />
  <path fill="var(--icon-color6)" d="…" />
</symbol>  

Pour cette démo, j’ai emprunté une icône de l’excellent Iconic, qui offre des icônes SVG responsives et multicolores (alimentées par CSS et du JavaScript, si je comprends bien). J’ai essayé d’imiter leur exemple multicolore pour cette icône, j’espère qu’ils ne m’en voudront pas.

Un symbole utilisant six propriétés CSS personnalisées. Fonctionne avec Firefox, Chrome ou Safari 9.1+

Cela fonctionne assez bien avec les navigateurs compatibles. Il n’y a qu’une seule icône : la première instance ne déclare pas les variables attendues, donc elle se résoud en currentColor. Les deux instances suivantes déclarent chacune un ensemble de valeurs déclarées comme variables.

Comment sont calculés les pourcentages de stroke-with?

À quoi correspond le pourcentage dans stroke-width:N% ? Est-ce la largeur, la hauteur de l’icône ? En fait, c’est la diagonale, mais avec une formule funky (spec) : la diagonale est divisée par la racine carrée de 2, à peu près 1,4.

Qu’est-ce que cela signifie ? Eh bien pour les icônes carrées, le résultat de cette formule est le côté du carré. Donc 1% signifie “un pourcent de la largeur ou un pourcent de la hauteur”. Simple et clair.

Pour les icônes plus larges ou plus hautes cependant le résultat peut être variable :

Dans la seconde icône (ratio d’aspect de 2:1, montré avec la même hauteur et une largeur double), le stroke-width:5% nous donne un stroke de 7,91% de la hauteur et 3,95% de la largeur.

Toutes choses bien considérées, je recommande quand même d’utiliser les valeurs de pourcentage pour stroke-width. Si vous vous en tenez à des icônes carrées, vous pouvez utiliser les valeurs en pourcentage en sachant que grosso modo elles signifient “pourcentage de la largeur de l’icône”.

Désolé, pas de remplissage dégradé

Avec toutes ces possibilités de couleurs de remplissage, nous pouvons certainement réaliser des choses aussi simples qu’utiliser un dégradé comme fill ?

Malheureusement non. La propriété fill n’accepte pas les valeurs d’image, et la fonction CSS linear-gradient() génère une valeur d’image.

SVG a sa propre syntaxe pour coder et utiliser les dégradés. Mais son utilisation nous entraînerait assez loin des icônes SVG simples, donc je dirais juste que cela pourrait se faire, mais ça demanderait du travail et vous auriez à coder au moins quelques paramètres. Essayez si vous voulez :)

Conventions inspirées par les bugs des navigateurs

Safari : évitez les attributs de largeur et hauteur

Pour éviter les icônes géantes dans les pages non stylées, nous nous sommes d’abord appuyés sur les attributs width et height de l’élément <svg> :

<svg width="20" height="20">  
  <use xlink:href="…"></use>
</svg>  

Puis nous avons testé nos premiers sites web “basés sur les icônes SVG” dans Safari et iOS, et la moitié des icônes étaient corrompues ! Pourquoi ?!??

Il se trouve que Safari/WebKit n’apprécie pas d’avoir des attributs width et height avec une seule dimension, et le code CSS qui essaie de modifier cette dimension par la suite. En particulier lorsqu’on diminue la taille de l’icône, celle-ci rapetisse mais pas le contenu !

Notre solution a été de laisser tomber ces attributs et de nous appuyer sur le seul CSS pour dimensionner les icônes.

Remarquez que ce bug a peut-être été corrigé dans les dernières livraisons de Safari (9.1 pour le desktop, iOS 9.3).

Safari : évitez le padding sur le conteneur svg

Si vous voulez une couleur de background, des bordures, du padding, etc., vous devriez essayer de styler l’élément contenant l’icône et non l’élément <svg> lui-même. Bien que cela semble fonctionner dans les derniers navigateurs, il existe des problèmes connus de rendu dans les versions anciennes de navigateurs WebKit, donc je recommande de styler un élément enveloppant (wrapper, comme <span>, <button>, <a>, etc.).

Styles de boîtes appliqués sur l’élément svg directement ou sur un élément wrapper. La plupart des navigateurs devraient rendre l’élément de la même façon, mais les versions anciennes de WebKit risquent de se fâcher.

 NdT : pour inspecter le code, vous pouvez vous reporter à l’article original, que vous pouvez également consulter en version txt.

Firefox : évitez de cibler l’élément svg

Cela créera des problèmes dans Firefox. Pourquoi ? Lorsque nous utilisons l’élément <use>, les navigateurs créent un shadow DOM (un DOM fantôme) où ils dupliquent les contenus des <symbol> que nous utilisons. Schématiquement, ça ressemble à ceci :

<svg class="Icon Icon--something" aria-hidden="true">  
  <use xlink:href="#something">
    <svg viewBox="0 0 20 20">
      <path d="…" />
    </svg>
  </use>
</svg>  

Comme expliqué dans la section précédente, Firefox permet actuellement la sélection dans le shadow DOM créé par l’élément <use>. Donc si vous avez ce CSS ...

svg {  
  fill: red;
}
.Icon--something {
  fill: green;
}

...dans Firefox les styles seront :

<svg class="Icon Icon--something" aria-hidden="true" fill="green;">  
  <use xlink:href="#something">
    <svg viewBox="0 0 20 20" fill="red;">
      <path d="…" />
    </svg>
  </use>
</svg>  

Dans d’autres navigateurs, l’icône sera verte (comme attendu), mais dans Firefox elle sera rouge car l’élément <svg> intérieur prendra les styles du premier ensemble de règles (fill: red).

Une autre façon d’éviter ce bug est d’utiliser ce sélecteur :

:not(use) > svg { … }

Intéressé par SVG ? Retrouvez une liste des meilleurs articles et ressources du web.

Tous les articles sur SVG parus dans la Cascade.

Tous les articles sur les bases SVG parus dans la Cascade.

Tous les articles sur Sketch3 parus dans la Cascade.

Articles sur les mêmes thèmes dans la Cascade :

Utiliser SVG, par Chris Coyier
SVG, style et animation, par Sara Soueidan
Grunt pour ceux qui pensent que Grunt est compliqué, par Chris Coyier
Gulp pour les débutants, par Zell Liew


Article original paru le 26 mars 2016 dans le blog de Florens Verschelde. Vous pouvez consulter les sources du texte ici.

Sur l’auteur : est un professionnel du web, auteur et photographe. On peut le suivre sur twitter et sur son blog.

Traduit avec l’aimable autorisation de l’auteur.
Copyright Florens Verschelde © 2016.