Comme nous l’évoquions déjà dans « Introduction à la performance web » et dans « Les performances web, pour aller plus loin », le travail d’optimisation des performances d’un site passe grandement par l’optimisation de l’affichage côté client.
Quelques grands axes permettent de contribuer fortement à cet effort de performance côté CSS.
Réduire le poids des feuilles de style
Le premier axe d’optimisation est de réduire tout d’abord le poids du fichier CSS. Les conseils que nous avions vus dans « Les principes de base de CSS » et dans « Introduction à la performance web » restent valables :
- toujours aller du plus général au plus spécifique ;
- éviter d’utiliser des styles inutiles (par exemple, mettre un
display: block
ne sert à rien sur un élément flottant) ; - utiliser correctement l’héritage ;
- utiliser les notations compactes ;
- factoriser autant que possible les propriétés communes à plusieurs éléments ;
- évitez les unités inutiles, par exemple
border: 0
n’a pas besoin d’unité.
En ressort final, quand votre CSS est suffisamment travaillée, pensez encore à la minifier, cela permet d’en réduire drastiquement le poids, car bien entendu, vous avez particulièrement bien commenté votre feuille de styles ? :)
N’oubliez jamais de bien activer la compression GZIP ou DEFLATE pour réduire encore le poids de vos feuilles de styles, même si c’est en théorie plus du ressort du serveur que de l’intégration proprement dite.
Autre point important : arrangez-vous pour que votre CSS soit envoyée et reçue le plus tôt possible. Il est capital de comprendre que votre chemin critique de rendu de page passe très souvent par CSS, parfois c’est même CSS qui est le chemin critique.
Cela revient à combiner autant que possible vos fichiers CSS en un unique fichier, ainsi vous économiserez facilement des requêtes. Idéalement servez-les depuis le même domaine que celui de la page, cela évitera une coûteuse requête DNS. Dans certains cas extrêmes de recherche de performance, il est même conseillé de mettre le CSS directement en embarqué dans la page (ou du moins la partie critique pour accélérer la sensation de vitesse d’affichage de la page), c’est dire à quel point cette partie est critique.
Réduire les requêtes
L’intégration CSS peut également permettre d’économiser des requêtes HTTP, soit en réduisant leur nombre, soit en les supprimant.
Les sprites CSS
Les sprites CSS permettent de regrouper plusieurs images en une seule. Nous avions ici même publié un article expliquant l’idée de la technique : Les sprites CSS.
En général, cette technique est à réserver aux images de même nature et/ou de même fonction (pour d’évidentes raisons de maintenabilité).
Divers outils facilitent leur création et leur maintenance, typiquement les pré-processeurs comme Sass ont des fonctions qui permettent de les générer automatiquement. Cela peut être très pratique si le site doit utiliser de très nombreux sprites.
Encore une fois, une factorisation efficace peut permettre de grandement réduire le poids de la CSS. Par exemple, imaginons que vous ayez 64 images de 16 par 16 pixels qui sont combinées en un sprite CSS, au lieu d’avoir 64 fois ce genre de code :
.icon-16-pdf,
.icon-16-doc,
.icon-16-wav,
/* etc. */
.icon-16-zip {
display: inline-block;
width: 16px;
height: 16px;
// etc.
}
Il est possible d’effectuer une factorisation bien plus efficace et légère grâce à certains sélecteurs, par exemple :
[class*=icon-16] {
display: inline-block;
width: 16px;
height: 16px;
// etc.
}
vous permettra d’économiser un grand nombre de déclarations dispensables, ce sélecteur ciblera automatiquement toutes les classes dont l’attribut class
contient icon-16
.
Au repeat view
Bien entendu, une mise en cache appropriée des éléments statiques (CSS, images, JavaScript étant les principaux) permet d’optimiser les performances d’une intégration lors d’une seconde consultation. Toutefois, ces bonnes pratiques sont plus du ressort de la gestion du cache, et donc une problématique de serveur.
Nous avions écrit sur le sujet lors de précédents articles, donc nous ne reviendrons pas dessus, vous pouvez relire « Les performances web, pour aller plus loin ».
Embarquer du contenu dans la CSS
L’autre solution pour améliorer les performances est de limiter les requêtes HTTP. En effet, une requête HTTP peut avoir un coût bien supérieur en performances que du contenu directement embarqué dans une feuille de style, surtout en situation de mobilité où la latence ou tout simplement une mauvaise connectivité peuvent fortement ralentir le téléchargement d’une ressource.
Nous avions déjà évoqué la possibilité des Data-URI dans « Les performances web, pour aller plus loin ». C’est particulièrement bien adapté pour les images de petite taille. En général, le code produit est un petit peu plus lourd que l’image originale sous forme de fichier, mais l’économie d’une requête HTTP reste préférable en général.
Il est également possible d’embarquer d’autres types de contenus directement dans les feuilles de styles, des web fonts ou du SVG par exemple. Là c’est vraiment selon le cas d’utilisation que la décision se prendra. Par exemple, une web font embarquée dans la CSS va alourdir de manière conséquente cette dernière. Une question à sous-peser en somme ! :)
Autres astuces
Accélération matérielle
L’affichage et le calcul de propriétés CSS est en général très peu consommateur de ressources côté processeur, toutefois, le calcul d’animations ou de transitions CSS peut être plus compliqué pour certains périphériques peu puissants, et des saccades et autres ralentissements peuvent survenir. Toutefois, il est possible de déléguer certains de ces calculs à des processeurs graphiques qui eux sont spécialisés dans ce genre de calculs. On appelle cela « l’accélération matérielle », et cela peut se déclencher en utilisant certaines propriétés comme :
transform: translateZ(0);
/* avec tous les préfixes nécessaires ! */
Le gain en fluidité sera parfois visible immédiatement. D’autres avantages sont également possibles, comme le lissage des polices, l’accélération des filtres CSS, etc.
Pour en savoir plus sur ce sujet, vous pouvez lire l’article suivant : L’accélération matérielle au service de vos animations CSS.
Pur CSS ?
Parfois, les solutions les plus simples sont les meilleures : si le design souhaité peut être créé intégralement avec CSS (ou quasi-majoritairement), n’hésitez pas une seconde et franchissez le pas ! Non seulement se passer d’images vous fera gagner de précieuses requêtes, mais en plus les propriétés CSS ne « pèsent pas lourd » : quelques lignes de propriétés CSS seront toujours plus rapides et plus souples à l’utilisation que des images, aussi bien conçues soient-elles.
De là à dire que c’est une raison de plus de faire intervenir le développeur front-end très tôt dans les processus de création des sites, il n’y a qu’un pas… que je franchis allègrement ! C’est une économie substantielle en temps d’export des images pour le graphiste et une immense souplesse offerte au développeur front-end (particulièrement sur les sites en responsive), dont il serait bien dommage de se priver.
Autre point : l’approche d’intégration choisie par le développeur front-end peut également influer sur les performances : typiquement, si ce dernier choisit une intégration mobile-first, le rendu sera sûrement plus rapide sur les petits écrans, et le surplus de styles pour les écrans plus larges sera absorbé par les navigateurs plus puissants des ordinateurs dits « classiques ».
Quid des sélecteurs efficaces ?
Nous le mentionnions ici nous aussi dans « Introduction à la performance web » : avoir des sélecteurs courts et qualifiants est également une source d’optimisation. De nombreux articles comme « Writing efficient selectors » nous indiquaient de faire très attention à nos sélecteurs.
Premier rappel : un sélecteur efficace peut se définir comme un sélecteur évitant au moteur du navigateur trop d’analyse pour appliquer les styles.
Exemple : #header ul li a.header-link
est un sélecteur trop long : cela ne sert à rien de sur-spécifier les styles là où .header-link
suffirait à cibler les liens en question. Ajoutons à cela que la surcharge dans une media-query sera d’autant plus courte selon le sélecteur à surcharger.
Second rappel : un sélecteur qualifiant est quand à lui un sélecteur qui permet au navigateur de cibler très efficacement les éléments auxquels s’appliquent les styles. Cette « erreur » est souvent commise par simple ignorance de la manière dont les navigateurs évaluent les styles.
Par exemple : .header a
semble être un bon sélecteur : court et précis. Le développeur va se dire en l’écrivant : le navigateur va aller prendre tous les a
dans .header
. Or, cela ne se passe pas du tout comme cela pour le navigateur !
Ce dernier va évaluer cette règle CSS de droite à gauche, autrement dit, il va cibler tous les liens a
de la page, et regarder ensuite ceux qui se situent dans .header
. Si l’on imagine qu’il y ait beaucoup de liens dans la page, ce sélecteur devient donc peu qualifiant : le navigateur teste beaucoup d’éléments pour peu d’élus. Un ciblage direct par .header-link
aurait évité des efforts au navigateur.
Pour prendre une analogie, c’est un peu comme faire un SELECT *
dans une requête SQL : c’est à éviter, autant ne sélectionner que les champs dont on a besoin. Si vous n’êtes pas familier du back-end, voici une analogie humoristique : cela revient à prendre tous les produits d’un supermarché dans son caddie et à ne mettre sur le tapis de la caisse que ceux que vous voulez acheter. Pas très efficace ! :)
Toutefois, ces articles ont été écrits il y a plusieurs années, et ces propos sont à modérer : sur des navigateurs modernes et sur des machines décentes, l’efficience des sélecteurs serait à relativiser, comme l’indiquent plusieurs articles [1]. En quelque sorte, nous serions de l’ordre de la micro-optimisation. D’autres articles indiquent de quand même faire attention à certains sélecteurs trop peu efficaces et nous conseillent de les simplifier [2], comme le sélecteur universel. Bref, la question n’est pas totalement tranchée.
Qu’à cela ne tienne, je prends le parti d’affirmer qu’avoir des sélecteurs efficaces et qualifiants fait également partie du travail qualitatif qui doit être fait sur CSS, et ce pour plusieurs raisons :
- ce travail est induit par d’autres facteurs comme le fait que la CSS ne doit pas trop être liée à la structure HTML, que nous avons abordé dans l’article « Les principes de base en CSS » ;
- les sélecteurs efficaces sont plus facilement surchargeables, les CSS de sites en responsive n’en seront que plus légères ;
- des sélecteurs précis auront moins de risques d’être trop larges ou trop précis : les effets de bord sur des CSS très complexes seront limités ;
- même si c’est de l’ordre de la micro-optimisation pour les navigateurs modernes, pourquoi s’en priver ? Déjà les navigateurs plus anciens ou sur des périphériques plus modestes apprécieront, et cela ne coûte rien de plus de le faire.
Conclusion
Assurément, cette question des performances n’est pas triviale. Comme le précisaient déjà les articles d’OpenWeb sur la performance Web, ce sont surtout de bonnes pratiques et de bonnes habitudes à prendre très tôt et tout au long de la conception des sites Web.
Le développeur front-end est particulièrement sollicité sur cette problématique, et même si l’optimisation de ce dernier déborde souvent de son propre cadre en sollicitant le back-end, il a assurément une place de choix dans le travail sur les performances Web quand il produit des CSS.
Références, compléments
- Introduction à la performance web
- Les performances web, pour aller plus loin
- Les sprites CSS
- L’accélération matérielle au service de vos animations CSS
- Checklist Opquast performances Web : 41 bonnes pratiques Webperf
- Page Speed best practices: Optimize browser rendering
- Efficiently rendering CSS
- CSS and the critical path
- Writing efficient CSS
- Performance impact of CSS Selectors
- Simplifying CSS selectors
- Front-end performance for web designers and front-end developers
Note
Cet article fait partie de la série « Grands principes de construction moderne de CSS ».
Vos commentaires
# MoOx Le 15 mai 2014 à 11:29
Pour de l’optimisation sur la syntaxe (ce qui est abordé au début), un outil comme CSSO (http://bem.info/tools/optimizers/csso/) fait très bien ce taf.
Pour les sélecteurs, utiliser BEM est une bonne idée :
– https://github.com/suitcss/suit/tree/master/doc
– http://putaindecode.fr/posts/css/petite-definition-bem/
# Gaël Poupard Le 15 mai 2014 à 12:24
Ta dernière partie sur les sélecteurs peut également être justifiée par le fait que plus le sélecteur est efficient, plus il est simple à lire et comprendre - et pas seulement pour le navigateur mais pour le développeur suivant également.
# op Le 15 mai 2014 à 20:02
merci pour l’article ;)
# Ch. Onze Le 16 mai 2014 à 09:40
Comme d’habitude, article très intéressant. Merci !
# flexbox Le 28 mai 2014 à 10:52
Haha excellent le coup du caddie dans le supermarché !
Pour ceux qui souhaitenent faire un audit de leur CSS pour vérifier sa santée j’ai écrit un article qui reprends certains concepts : http://davidl.fr/blog/css-evolutif.html
# Samuel Martin Le 13 août 2014 à 00:36
"Dans certains cas extrêmes de recherche de performance, il est même conseillé de mettre le CSS directement en embarqué dans la page".
Cette phrase mériterait d’être détaillée. En utilisant du CSS inline
1. On perds l’intérêt de la mise en cache
2. On surchage chaque page HTML
3. Plus de parallélisation possible.
L’intérêt d’une telle pratique ce limite au site web composé d’un nombre très réduit de page HTML et encore. Je dirai même qu’a partir de deux pages HTML cette technique devient une mauvaise pratique. Votre avis ?
# Samuel Martin Le 13 août 2014 à 00:44
Correction je ne voulais pas dire "inline" mais "Embedded"
# Nicolas Hoffmann Le 13 août 2014 à 09:55
Effectivement, cela a des inconvénients ou du moins des contreparties.
Je la vois utile pour certains cas :
– effectivement, le nombre de pages très réduit
– donner une « sensation » de vitesse en mettant le CSS critique en inline (j’ai reformulé la phrase qui était en effet un peu légère)
À mon avis personnel, c’est vraiment à utiliser dans des cas bien particuliers de recherche de performance, ou de sensation de performance (pour que le navigateur commence déjà à afficher la page pendant que les assets continuent à se charger). Cela implique de mettre en place tout un système un poil compliqué (et qui doit être automatisé). En gros :
– au first load, il faut mettre le CSS critique en inline pour accélérer le rendu
– idéalement, il faudrait pouvoir quand même mettre en cache ce CSS, donc le lazy-loader + mise en cache
– et à un second load ou sur une autre page, ne plus avoir ce CSS en inline vu qu’il a été mis en cache au coup précédent (ou le ré-avoir systématiquement si le lazy-load était impossible au coup précédent, etc.).
Bien sûr, si les gabarits changent, ça complexifie encore plus le tout.
Je ne vais pas mentir : même si j’ai fait qq essais en mode curieux, je n’ai eu à mettre un tel système en place sur mes réalisations. Dans les cas où je suis, j’ai franchement plus à gagner à tout servir de manière très classique en concentrant mes optimisations sur la CSS en elle-même et en limitant le nombre de requêtes. Cela me permet de descendre régulièrement en-dessous de 3s de temps d’affichage sur un navigateur moderne (avec serveur en Europe et testé depuis les USA), je n’ai clairement pas le temps ni le budget pour aller gratter encore des ms supplémentaires. :)
Il faudrait demander à quelqu’un qui utilise ça, je vais essayer de pinger Kaelig, je crois qu’il a déjà fait ça.
# Sylvain Zyssman Le 13 août 2014 à 10:14
L’intérêt de mettre une partie minime (mais critique) du CSS est en effet la sensation de vitesse apportée à l’utilisateur, en chargeant en priorité le contenu "above the fold".
Je vous renvoie vers les slides de Patrick Hamann [https://speakerdeck.com/patrickhamann/css-and-the-critical-path] qui prend l’exemple du jounal The Guardian.
En mettant le CSS critique dans les 1ers 14Kb (http://blog.jingleweb.fr/2014/07/performance-web-limpact-tcp/) on arrive à afficher du contenu très rapidement à l’écran.
Quant à l’alourdissement des pages HTML, il est évident. Je pense qu’il est indispensable de combiner cette optimisation avec d’autres techniques, telles que le GZIP des pages par exemple.
# Éric Le 13 août 2014 à 10:14
Ca fait partie des équilibres à étudier au cas par cas.
Je pense qu’on peut facilement conseiller d’avoir une CSS unique en ressource externe, mise en cache pour le cas générique. L’idée c’est que l’utilisateur a des chances de revenir plusieurs fois, profitant de la mise en cache et donc de l’économie d’un second transfert. C’est d’autant plus vrai si la CSS est la même pour toutes les pages du site.
Certains ont tenté, avec des résultats intéressants, de mettre la CSS de certaines pages en inline. Généralement c’est pour des landing page : page d’accueil, adresse pour un concours, pour une page avec conversion immédiate, etc. L’idée c’est de faire en sorte que la toute première impression soit exceptionnelle, quitte à rajouter le poids d’une CSS téléchargée une seconde fois (cette fois ci en fichier lié et mis en cache) sur les pages suivantes.
Le cas parfait pour moi c’est sur une page autonome où on attend un fort taux de conversion (genre inscription de l’utilisateur) et où l’utilisateur arrive directement. Le temps d’attente peut faire gagner quelques dixièmes de points de conversion.
Ca peut aussi s’envisager sur une page d’accueil qui aurait peut de fichiers annexes et qui de toutes façons ne partagerait pas exactement la même CSS que le reste du site (ce qui arrive finalement fréquemment). La page du moteur Google est un très bon exemple mais ça n’a pas besoin d’être aussi spécifique que ça.
Par contre ça veut dire ne pas gâcher la même page avec plein de ressources externes ou un rendu long, sinon le gain sera nul au premier passage, et négatif sur tous les suivants.
# Michael S Le 13 août 2014 à 16:49
On peut utiliser un cookie pour détecter le premier chargement de la page et afficher le CSS critique seulement dans ce cas, tout en ayant un fichier externe avec le CSS qui sera en cache, mais je suis pas sur de l’impact sur les performances du Javascript qui va tester l’existance du cookie.
Après les cookies/Javascript peuvent être désactivés toussaaa donc bon.. perso, je pense qu’on est dans la trache des 20% (règle des 80-20) non ? :)
# pascal Le 13 août 2014 à 22:37
En ce qui concerne les css dans la page HTML, c’est intéressant pour les mobiles lorsque la quantité de css est importante. La mise en oeuvre est relativement complexe : il faut prévoir une mise en cache (lorsque la page est rendue) & utiliser les cookies (en js) pour ne pas le faire sur les pages / accès suivants.
Le dernier article de Filament Group décrit bien le processus, les outils, le pourquoi (désolé pas de lien je suis en vacances avec un réseau chaotique...)
Parmi les outils uncss (grunt par Adi Osmani ) et loadCSS (Filament Group) permettent d’identifier les styles nécessaires à une page (à mettre dans
style
) puis de charger la feuille complète (pour le cache).Évidemment, ce type d’amélioration de la perf n’a d’impact que si tous les autres voyants sont au vert (images, scripts & styles concaténés et minifiés etc.)
L’article de Filament Group contient les stats avant/après et l’impact semble important surtout sur mobile.
# Nicolas Hoffmann Le 14 août 2014 à 09:29
Michael : en effet, ça implique de penser via noscript aussi pour que ça ne casse pas.
Pascal : effectivement, très bonne ressource que tu mentionnes, en voici le lien : http://filamentgroup.com/lab/performance-rwd.html
# Olivier C Le 23 juillet 2015 à 16:11
Je suis en phase avec tous les points abordés dans cet article, sauf avec celui des sélecteurs universels. En effet, outre le fait qu’ils permettent une grande polyvalence j’attends toujours une preuve de leur contre-productivité, chiffre à l’appui.
# Nicolas Hoffmann Le 24 juillet 2015 à 10:19
Olivier C. : le problème de ces sélecteurs vient du fait de l’analyse des sélecteurs, de droite à gauche (le navigateur va tout prendre, et regarder lesquels sont dans le sélecteur suivant).
Après, outre ces considérations, je trouve que c’est un peu comme !important : c’est un bazooka, et un bazooka mal manié fait beaucoup de dégâts inutiles.
# Matthieu Le 19 février 2016 à 15:18
Bonjour,
Je sais que cet article n’est pas tout récent, mais je me posais une question par rapport à l’efficacité des sélecteurs.
J’ai l’habitude d’écrire :
et de sélectionner mes items de la façon suivante en CSS :
.mon-menu li { ... }
D’après vous, je devrez plutôt écrire :
et sélectionner mes items de la façon suivante en CSS :
.item-menu { ... }
C’est bien ça ?
Ça devient lourd je trouve, non ?
# Nicolas Hoffmann Le 20 février 2016 à 09:20
@Matthieu => Qu’on s’entende bien : si l’on prend l’axe strict des performances, c’est de la micro-micro-optimisation. Et ce n’est franchement pas grave.
Après il y a d’autres considérations : supposons que ce menu doive être dupliqué et adapté, puis adapté en responsive, puis re-dupliqué, etc. là on va apprécier de cibler directement chaque élément et d’éviter des sélecteurs à rallonge très pénibles (et un joyeux bordel).
Dans l’absolu, si c’est dans une situation avec très peu d’évolution, non, ce n’est pas grave.
Sur la question de la lourdeur, ce n’est qu’un point de vue : perso je me suis habitué à faire ainsi, et j’y trouve plus agréable. ;)
Vos commentaires
Suivre les commentaires : |