Performance et webfonts d'icônes

Introduction

Les webfonts permettent entre-autres d’inclure des icônes. De nombreuses librairies existent : par exemple GLYPHICONS (en partie fournie avec bootstrap) ou Font Awesome.

Pour fonctionner sous l’ensemble des navigateurs il est commun d’inclure 4 formats : OpenType, WOFF, TrueType et SVG. C’est ce dernier qui nous servira par la suite.

Passer par une webfont pour gérer les icônes a l’avantage de pouvoir modifier leur rendu en CSS (il est par exemple très facile de modifier leur couleur ou leur taille). Celle-ci étant vectorielle, l’icône pourra donc avoir n’importe quelle taille déterminée en CSS. De plus, tout comme cela pourrait être le cas pour des icônes intégrées via une sprite, un seul hit HTTP est utilisé pour charger l’ensemble des icônes.

En plus d’avoir des librairies d’icônes, il est possible d’intégrer ces propres icônes en webfont, à partir de ses propres fichiers SVG (ou des icônes en SVG d’une librairie, comme celle que vient de récemment open-sourcer google : material-design-icons). Le plugin grunt-webfont s’aquitte très bien de cette tâche.

Voici un exemple d’inclusion de webfont :

@font-face {
	font-family:"icons";
	src:url("icons-69b954a18a01fc7d8b579a40dacdca36.eot");
	src:url("icons-69b954a18a01fc7d8b579a40dacdca36.eot?#iefix") format("embedded-opentype"),
		url("icons-69b954a18a01fc7d8b579a40dacdca36.woff") format("woff"),
		url("icons-69b954a18a01fc7d8b579a40dacdca36.ttf") format("truetype"),
		url("icons-69b954a18a01fc7d8b579a40dacdca36.svg?#icons") format("svg");
	font-weight:normal;
	font-style:normal;
}

Une classe va indiquer d’utiliser la font définie plus haut.

.icon {
	font-family:"icons";
	display:inline-block;
	vertical-align:middle;
	line-height:1;
	font-weight:normal;
	font-style:normal;
	speak:none;
	text-decoration:inherit;
	text-transform:none;
	text-rendering:optimizeLegibility;
	-webkit-font-smoothing:antialiased;
	-moz-osx-font-smoothing:grayscale;
}

Il est ensuite possible d’accéder aux icônes via leur caractère. Autant de sélecteurs que d’icônes seront créés :

.icon-adjust:before {
	content:"\f101";
}

Notre icône pourra alors être insérée comme ceci :

<i class="icon icon-adjust"></i>

Problématiques

Nombre de fichiers inclus

Lorsque vous voulez inclure des icônes provenant de plusieurs librairies (GLYPHICONS, Font Awesome…), il faut inclure plusieurs fichiers (un fichier par librairie).

La même problématique se pose lorsque vous utilisez vos propres icônes, il faudra inclure le fichier généré par webfont, ainsi que la librairie d’icônes.

Nous aurons donc un hit HTTP par source d’icônes (si GLYPHICONS et Font Awesome sont tous deux intégrés, il y aura deux hits).

Poids des librairies

Si seulement quelques icônes vous intéressent dans la librairie, il est dommage de l’inclure totalement.

Voici les tailles des fichiers inclus par Font Awesome :

fontawesome-webfont.eot : 55K
fontawesome-webfont.svg : 281K
fontawesome-webfont.ttf : 110K
fontawesome-webfont.woff : 64K

Selon le navigateur utilisé entre 55Kb et 281Kb seront utilisés, cela pour rendre accessible 479 icônes. Dommage si c’est pour seulement en utiliser une poignée.

Il existe actuellement 1 446 demandes de nouvelles icônes pour Font Awesome. Le problème ne risque pas de s’améliorer avec le temps.

Nombre de sélecteurs CSS générés

Comme vu plus haut, nous allons avoir un sélecteur CSS pour utiliser la font. Rien de gênant dans celui-ci.

Par contre, ensuite nous allons avoir un sélecteur CSS par icône. Si l’on reprend le cas de Font Awesome, cela fait 479 sélecteurs. Si vous devez supporter IE, cela peut être très gênant, celui-ci ayant une limitation de 4095 sélecteurs par feuille de style. Dans les autres cas, ce sera de nombreux sélecteurs parsés inutilement par le navigateur.

Fichiers SVG originaux non disponibles

Les fichiers originaux ayant servis à la création de la font (et notamment des fichiers SVG) ne sont parfois pas disponibles, seulement la webfont. Continuons avec l’exemple de Font Awesome, ceux-ci ne sont pas présent dans le dépot git.

Solution

La réponse : le plugin grunt grunt-webfont-svg-extractor.

Ce plugin va vous permettre d’extraire un fichier SVG par icône se trouvant dans le fichier SVG Webfont. Nous allons ensuite pouvoir utiliser ces SVG (éventuellement tirés de différentes sources) pour en créer une webfont avec le plugin grunt-webfont cité précédement.

Principe

Le plugin va partir d’un fichier CSS et un fichier SVG.

Par exemple ces fichiers pour glyphicon/bootstrap :

Un fichier CSS pour des webfont contient des définitions d’icônes comme celles-ci :

.glyphicon-asterisk:before {
  content: "\2a";
}

Et l’icône est définie comme ceci dans le fichier SVG :

<glyph unicode="&#x2601;" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />

Nous allons donc devoir faire la correspondance entre le caractère unicode du fichier SVG et l’attribut content défini dans le fichier CSS pour récupérer les noms des SVG exportés.

De plus, le chemin SVG défini dans l’attribut d n’est pas utilisable directement dans un fichier SVG, quelques transformations sont nécessaires avant d’écrire le fichier.

C’est donc ces deux opérations que va effectuer le plugin grunt-webfont-svg-extractor.

Tutorial

Les instructions qui vont suivre nécessitent d’avoir quelques notions sur l’utilisation de Grunt.

Il faudra tout d’abord installer le plugin grunt-webfont-svg-extractor.

npm install grunt-webfont-svg-extractor --save-dev

Puis charger celui-ci dans le fichier Gruntfile.js.

grunt.loadNpmTasks('grunt-webfont-svg-extractor');

Puis ensuite configurer la tâche grunt :

  webfont_svg_extractor: {
    options: {
      fontPath: "test/fixtures/glyphicons-halflings-regular.svg",
      cssPath: "test/fixtures/bootstrap.css",
      outputDir: "tmp/",
      preset: "glyphicon",
      icons: [
        "chevron-up",
        "chevron-down"
      ]
    }
  },
})

A propos de ces options :

Une autre option est disponible :

Nous allons ensuite créer un fichier webfont. Pour cela nous passerons par grunt-webfont, que nous allons commencer par installer :

npm install grunt-webfont --save-dev

Puis charger celui-ci dans le fichier Gruntfile.js.

grunt.loadNpmTasks('grunt-webfont');

En configurant le plugin de cette façon, l’ensemble des fichiers SVG du dossier tmp seront utilisés pour créer une webfont dans le dossier output. La classe à utiliser pour indiquer d’utiliser cette font sera icon et toutes les classes définissant le caractère à utiliser seront préfixées par icon- (viendra ensuite le nom du fichier SVG sans son extension).

webfont: {
  icons: {
    src: 'tmp/*.svg',
    dest: 'output/',
    destCss: 'output/',
    options: {
      templateOptions: {
        baseClass: 'icon',
        classPrefix: 'icon-'
      },
      relativeFontPath: '',
      htmlDemo: true,
      engine: 'node'
    }
  }
}, 

Petit bonus, le fichier exporté par webfont sera nommé en fonction de son contenu. Il sera donc possible de configurer votre serveur web pour mettre en cache ces fichiers pour une très longue durée.

icons-69b954a18a01fc7d8b579a40dacdca36.eot
icons-69b954a18a01fc7d8b579a40dacdca36.svg
icons-69b954a18a01fc7d8b579a40dacdca36.ttf
icons-69b954a18a01fc7d8b579a40dacdca36.woff

Un exemple complet est disponible dans le dossier example du dépôt de grunt-webfont-svg-extractor.

Etude d’un cas

Sur le baromètre AFUP nous avions besoin de 3 icônes de Bootstrap et 2 icônes de Font Awesome.

Nous aurions donc dû faire 2 appels HTTP sur des fichiers qui pèseraient en tout entre 78Kb et 344Kb (selon le type de font utilisé par le navigateur). Entre 23Kb et 63Kb pour les GLYPHICONS de Bootstrap et entre 55Kb et 281Kb pour Font Awesome.

De plus 682 sélecteurs se trouveraient dans la CSS (202 pour les GLYPHICONS de Bootstrap et 480 pour Font Awesome).

En utilisant ce plugin nous ne chargeons qu’un seul fichier pesant entre 1,2Kb et 6Kb et avons seulement 6 sélecteurs CSS.

Le gain a donc été d’un hit HTTP, et entre 76.8Kb et 338Kb ainsi que 676 selecteurs CSS.

Licences

En utilisant ce plugin, attention à bien respecter les licences des différentes librairies.