Sprites CSS : leur utilité actuelle et leur création

La plupart des tutoriels de sprites CSS commencent encore par la même phrase qu’en 2009 : les sprites réduisent les requêtes HTTP, donc accélèrent votre site. Cet argument reposait sur une contrainte HTTP/1.1 qui n’existe plus sur un serveur moderne. Voici la version honnête : ce qui justifie encore réellement un sprite, ce qui ne le justifie plus, et les calculs exacts background-position et les calculs Retina pour en créer un qui fonctionne.

Ce qu’est réellement un sprite CSS

Un sprite CSS est un fichier réunissant plusieurs images, avec du CSS n'en montrant qu'une. Aucun recadrage ni JavaScript : l'élément reçoit une valeur fixe de width et height, toute la planche est définie comme sa propriété background-image, et background-position déplace cette planche derrière l’élément pour faire apparaître l’icône voulue dans la zone visible. La zone est une fenêtre ; la planche de sprites est une grande feuille de papier déplacée derrière.

Le détail qui surprend au départ est que les décalages sont négatif. Vous ne déplacez pas la fenêtre vers l’icône, mais la planche pour amener l’icône devant la fenêtre. Pour afficher une icône dont le coin supérieur gauche se trouve à 96 pixels du bord gauche, déplacez la planche de 96 pixels vers la gauche, soit background-position: -96px 0.

Un exemple concret : empaquetez cinq icônes de 24×24 sur une ligne, avec 8 pixels d’espace entre elles, en commençant à 8 pixels du bord gauche et 8 du haut. La planche mesure 160×32. Le bord gauche de la troisième icône est à 8 + 24 + 8 + 24 + 8 = 72 pixels, et son bord supérieur à 8 pixels. La règle est :

DéclarationSon rôle
background-image: url("/sprite.png")Charge toute la planche derrière l'élément
background-repeat: no-repeatEmpêche la répétition de la planche et l’apparition d’autres icônes sur les bords
background-position: -72px -8pxDécale la planche de 72px à gauche et de 8px vers le haut pour placer la troisième icône à l’origine
width: 24px; height: 24pxLimite la fenêtre à une icône et masque le reste
display: inline-blockPermet à un span en ligne de respecter largeur/hauteur

Ne changer que les deux nombres dans background-position et vous obtenez une autre icône à partir du même fichier. C’est tout le mécanisme.

Planche 256 par 128 de huit icônes 64 par 64 en grille 4 par 2 ; troisième de la seconde rangée encadrée. Flèches à 128 pixels horizontalement et 64 verticalement ; élément 64 par 64 isolé avec background-position: -128px -64px
L’élément est une fenêtre de taille fixe. Des décalages négatifs déplacent la planche derrière elle jusqu’à ce que l’icône voulue soit visible.

En toute honnêteté : HTTP/2 a changé le calcul

Les sprites CSS ont été inventés pour contourner une limite précise. Avec HTTP/1.1, le navigateur ouvrait peu de connexions TCP par origine, généralement six, et chaque connexion ne pouvait transporter qu’une requête à la fois. Quarante icônes imposaient quarante requêtes en attente dans six canaux, chacune avec un coût d’établissement et de latence aller-retour. Les fusionner en un fichier transformait quarante requêtes successives en une seule. Sur une connexion à forte latence, ce n’était pas une micro-optimisation, mais souvent le gain le plus important possible sur la page.

HTTP/2 utilise le multiplexage. De nombreuses requêtes partagent simultanément une connexion, sans blocage en tête de file au niveau HTTP, et les en-têtes sont compressés entre les requêtes. Le problème précis pour lequel les sprites ont été inventés a largement disparu. Si un tutoriel affirme en 2026 que les sprites sont plus rapides parce qu’ils réduisent le nombre de requêtes, sans autre explication, il décrit un Web qui n’existe plus.

Vérifiez ce que vous servez réellement avant d'optimiser. Si votre site utilise un CDN moderne ou un hébergeur actuel, vous êtes presque certainement déjà en HTTP/2 ou HTTP/3. Ouvrez le panneau réseau, ajoutez la colonne « Protocol » et vérifiez. Optimiser une contrainte inexistante est du travail inutile.

Pourquoi cet article existe-t-il alors ? Parce que « la raison d’origine a disparu » ne signifie pas « il n’y a aucune raison ». Plusieurs vrais avantages subsistent malgré le multiplexage, et c’est sur eux qu’il faut décider :

  • Le coût supplémentaire par requête ne devient pas nul. Les requêtes multiplexées coûtent peu, mais ne sont pas gratuites. Chacune implique des en-têtes, une recherche dans le cache et une place dans l’ordonnanceur de ressources du navigateur. Pour cinq icônes, c’est négligeable. Pour deux cents petites icônes, c’est mesurable, et le sprite réduit l’ensemble à une entrée.
  • La gestion du cache devient plus simple. Un fichier, une entrée de cache, un Cache-Control politique, un seul nom de fichier avec empreinte à invalider. Deux cents fichiers d’icônes versionnés séparément constituent une surface de compilation et de déploiement qu’il faut gérer et qui ne l’est généralement pas.
  • Gestion atomique des versions. Les icônes arrivent ensemble, sans mélange entre trois anciennes en cache et d'autres nouvelles. Pour déployer un design system, c'est une garantie de cohérence, pas seulement de rangement.
  • Pas de clignotement d'icône manquante. Une fois la planche chargée, toutes ses icônes sont immédiatement disponibles. Des icônes chargées individuellement apparaissent l’une après l’autre à la résolution des requêtes, surtout là où cela gêne le plus : une barre d’outils ou une grille d’icônes visible sans défilement.
  • Changements d’état sans latence. Un état de survol ou actif placé ailleurs sur la même planche est déjà téléchargé. Avec des fichiers séparés, l’image de survol n’est souvent demandée qu’au premier survol, provoquant un bref vide lorsque l’utilisateur touche le bouton pour la première fois. Cela a toujours été l’un des meilleurs arguments en faveur des sprites, et HTTP/2 ne l’a pas affaibli.

Quand éviter un sprite CSS

Une recommandation crédible doit aussi dire non ; les sprites CSS ont plusieurs cas d'exclusion.

  • Les icônes monochromes évolutives relèvent de SVG. Un élément en ligne <svg> ou un sprite SVG construit à partir de <symbol> et <use> s’adapte à toute taille sans calculs Retina et hérite de la couleur via currentColor. Pour des icônes d'interface classiques, c'est simplement meilleur ; les planches ont ainsi perdu discrètement l'essentiel de ce marché.
  • Une seule icône ne justifie jamais un sprite. Pour une image unique, utilisez-la : le sprite ajoute calculs et compilation sans bénéfice.
  • Icônes changeant indépendamment. Le versionnement atomique a un coût : changer une icône invalide et fait retélécharger toute la feuille. Une collection souvent modifiée convient mal.
  • Tout contenu nécessitant recoloration ou thème. Un sprite matriciel fixe les couleurs. Mode sombre, thème et états exigent seconde planche ou filtres fragiles. C'est l'avantage structurel le plus clair du SVG.
  • Icônes liées à la taille de police. Les sprites sont liés à des dimensions en pixels. Si vos icônes doivent s’adapter à em à côté du texte, une approche vectorielle le gère facilement, contrairement à un sprite.

Sprite, sprite SVG, SVG intégré ou police d’icônes

ApprocheRedimensionnement netRecolorer en CSSRequêtesMise en cacheAccessibilité
Sprite CSS (matriciel)Non : pixels fixes, feuille 2x nécessaireNon, seulement des astuces de filtresUn pour toutes les icônesExcellent, un fichier durableImage de fond invisible aux aides techniques : nécessite du texte
Sprite SVG (symbol + use)Oui, toute tailleOui, via currentColorUn pour toutes les icônesExcellent, un fichierDans le DOM, accepte title et ARIA
SVG intégré par icôneOui, toute tailleOui, contrôle CSS completZéro, intégré au HTMLAucun : renvoyé avec chaque pageMeilleur, entièrement dans le DOM
Police d'icônesOui, s’adapte à font-sizeOui, via colorUn fichier de policeBonMédiocre — les glyphes sont lus à voix haute ou remplacés par des polices de secours

Considérez ce tableau comme une aide à la décision, pas comme un classement. Les images matricielles multicolores — drapeaux, logos, badges illustrés, captures d’icônes d’applications — ne peuvent pas raisonnablement devenir des sprites SVG. C’est dans ce domaine que les sprites CSS restent la bonne réponse, et non une solution héritée.

Créer un

La méthode comporte quatre étapes ; seule la troisième est délicate à la main.

  1. Rassemblez les icônes. Exportez à la taille d’affichage finale, ou au double : consultez la section Retina. Gardez des tailles uniformes lorsque c’est possible : une grille régulière simplifie les coordonnées et le CSS.
  2. Regroupez-les dans une seule planche. Une simple rangée ou une grille fixe est plus facile à comprendre ; un outil d’empaquetage utilise mieux l’espace lorsque les tailles varient. Laissez un espace entre les icônes.
  3. Relevez les coordonnées. Chaque icône doit avoir ses coordonnées x, y, sa largeur et sa hauteur en pixels de planche. Les mesurer à la main dans un éditeur est une source d’erreur : une lecture décalée d’un pixel fait apparaître un morceau de l’icône voisine, sans indication de la cause.
  4. Écrivez le CSS. Une règle commune et une petite par icône.

La règle de base commune compte plus qu’il n’y paraît. Chaque icône a besoin du même background-image, background-repeat: no-repeat, et display: inline-block. Répéter la background-image Une URL dans cinquante règles ne pose pas un problème de performance, le navigateur ne la charge qu’une fois, mais de maintenance : renommer la planche pour invalider le cache oblige à modifier cinquante endroits au lieu d’un. Utilisez une classe de base pour le commun et des classes par icône ne contenant que position et taille :

RègleSommaire
.spritebackground-image, background-repeat: no-repeat, display: inline-block
.sprite-searchbackground-position: -8px -8px; width: 24px; height: 24px
.sprite-settingsbackground-position: -40px -8px; width: 24px; height: 24px

Dans le balisage, c'est <span class="sprite sprite-search"></span>. Dans Sass, cette idée s'exprime généralement par un espace réservé, %sprite-base, intégré à chaque règle d'icône avec @extend — produit un sélecteur groupé dans le résultat compilé plutôt que de répéter les déclarations, et limite le balisage à une seule classe par icône.

Une image de fond est invisible aux lecteurs d'écran. Une icône sprite ne dit rien aux aides techniques. Si elle porte seule un sens, par exemple un bouton sans texte, ajoutez une vraie étiquette : texte visuellement masqué dans l'élément ou un aria-label sur le bouton. Les icônes décoratives à côté d’un texte existant ne nécessitent rien.

Retina et HiDPI : l’astuce background-size

C’est la partie omise ou mal comprise par la plupart des guides. Un sprite matriciel 1x paraît doux sur un écran 2x. La solution n’est pas de modifier les décalages, mais de compacter à double résolution puis d’indiquer au CSS une taille de planche moitié moindre.

Reprenez l’exemple précédent et doublez tout à l’export : les icônes font 48×48 pixels réels, l’espace est de 16 et la planche mesure 320×64 pixels réels. Réglez maintenant background-size: 160px 32px — exactement la moitié des dimensions réelles de la planche. Le navigateur réduit toute la planche à un espace de coordonnées de 160×32 pixels CSS et reporte les détails supplémentaires sur les pixels physiques d’un écran HiDPI.

L’avantage est que tous les autres nombres CSS restent dans le système de coordonnées 1x d’origine. La troisième icône reste background-position: -72px -8px avec width: 24px; height: 24px, inchangé, même si ses pixels réels sont à 144, 16 dans le fichier. Calculez une fois les décalages en pixels CSS ; l'unique background-size la déclaration sur la classe de base gère la correspondance de densité pour toutes les icônes à la fois.

Deux conséquences méritent d’être dites explicitement. D’abord, background-size appartient à la règle de base partagée et ne doit pas être répété par icône. Ensuite, ne servez pas deux planches derrière une requête média sans bonne raison : la planche 2x à demi-taille s’affiche correctement aussi sur les écrans 1x, le navigateur la réduisant simplement. Une planche et une règle couvrent donc les deux cas, au prix d’un fichier plus grand. Une planche d’icônes bien compressée étant généralement petite, ce compromis vaut normalement la peine et évite le cas délicat des ratios de pixels fractionnaires comme 1.5 et 2.5, où une requête média doit choisir un côté et où l’un des deux sera incorrect.

Survol et variantes d'état

La disposition classique des sprites interactifs est une grille : chaque colonne est une icône et chaque ligne un état, normal sur la première, survol sur la deuxième, actif ou désactivé sur la troisième. Comme les colonnes ne bougent pas, changer d’état ne modifie que Y et garde le décalage X identique.

Avec des icônes de 24px et un espacement de 8px, la première ligne se trouve à y = 8 et la deuxième à y = 40. La règle par défaut de la troisième icône est background-position: -72px -8px et sa règle de survol est background-position: -72px -40px. Si vous définissez le pas vertical comme variable, par exemple --sprite-row: 32px — vous pouvez exprimer l’état de survol une seule fois sur la classe de base avec calc() avec une variable X propre à chaque icône, plutôt que d’écrire une deuxième règle pour chacune.

C’est là que le sprite dépasse réellement les fichiers séparés : les pixels de survol arrivent avec ceux de l’état normal, donc le premier survol est instantané. Gardez aussi les états sur la même planche : une planche de survol séparée réintroduit exactement le clignotement de première requête que cette disposition évite.

L’espacement, et pourquoi les débordements sont moins prononcés ici que dans les jeux

Placer les images côte à côte peut amener un échantillonneur à dépasser les limites d’une icône et à récupérer la couleur de sa voisine. Dans les moteurs de jeu, c’est un risque constant et bien connu, car les textures sont réduites, mipmappées, filtrées et dessinées avec des transformations sous-pixel arbitraires.

Les sprites CSS évoluent dans un environnement moins contraignant. Pas de mipmaps, des arrière-plans généralement composés à des positions entières et, à exactement 1:1 sans transformation, aucun échantillonnage ne franchit les limites. Mais « moins fréquent » ne signifie pas « jamais » : les problèmes apparaissent dans des situations prévisibles, comme les ratios de pixels de l’appareil fractionnaires (1.5, 2.25, 3), le zoom de page à des pourcentages inhabituels, ou toute transform: scale() sur un ancêtre, et le background-size la réduction décrite dans la section Retina : tous placent les contours des icônes sur des limites non entières de pixels physiques, où le compositeur doit interpoler.

La solution est la même et coûte peu : laissez un petit espace transparent de 2 à 4 pixels à 1x, donc 4 à 8 sur une planche 2x, entre les icônes et autour de la planche. Cela augmente très peu le fichier et élimine toute une catégorie de signalements comme « une ligne pâle apparaît à gauche de cette icône, seulement sur mon portable ». Pour approfondir l’extrusion, les débordements de mipmaps et les puissances de deux, l’équivalent dans les moteurs de jeu est traité dans Compactage d’atlas de sprites : espacement, puissances de deux et débordements.

Échecs courants

  • Icônes voisines visibles sur les bords. La boîte de l’élément est plus grande que l’icône et révèle le contenu de la planche au-delà. La largeur/hauteur est incorrecte ou les marges internes agrandissent la boîte ; vérifiez si box-sizing: border-box entre en jeu, car cela change ce que comprend votre largeur déclarée.
  • Oublier background-repeat: no-repeat. Le réglage par défaut est repeat, la planche se répète donc et des fragments d'autres icônes remplissent la boîte. Facile à manquer près de l'origine, où le résultat semble presque correct.
  • Un span sans dimensions. Les éléments en ligne ignorent width et height entièrement, donc un vide <span> avec une classe de sprite se réduit à une taille nulle et rien ne s’affiche. Définissez display: inline-block (ou block, ou faites-en un élément flex) dans la classe de base.
  • Planche périmée après reconstruction. Vous replacez les sprites, leurs coordonnées changent, mais un visiteur de retour possède encore l’ancienne planche en cache avec votre nouveau CSS : chaque icône est donc légèrement incorrecte chez lui et parfaite chez vous. Invalidez le cache en changeant le nom du fichier à chaque reconstruction — une empreinte de contenu, sprite.a1b2c3.png) plutôt qu'une chaîne de requête ou une actualisation forcée par les utilisateurs.
  • Décalages d'un demi-pixel. Des tailles d’icônes, espaces ou marges initiales impairs sur une planche 2x produisent des décalages fractionnaires après division par deux. Ce sont précisément ces décalages qui créent des franges aux contours. Gardez toutes les dimensions de la planche 2x paires.

Questions fréquentes

Les sprites CSS sont-ils dépassés en 2026 ?

La justification initiale est dépassée, pas la technique. Avec HTTP/2 et HTTP/3, réduire les requêtes ne suffit plus comme argument. Le cas des nombreux petits graphismes matriciels reste pertinent : une entrée de cache, une version atomique, aucune apparition tardive par icône et des états de survol instantanés. Pour les icônes d’interface monochromes, SVG a vraiment remplacé le sprite matriciel : l’utiliser là en 2026 revient à choisir le moins bon outil.

Sprite CSS ou SVG : lequel choisir ?

Décidez selon le dessin, pas selon la diffusion. Pour des icônes plates, géométriques et monochromes ou bicolores, utilisez un sprite SVG : il se redimensionne sans planche 2x et se recolore via currentColor, et reste dans le DOM, accessible aux technologies d'assistance. Pour photos, dégradés ou images matricielles multicolores — drapeaux, produits, logos, pixel art — SVG n'apporte rien et les sprites CSS matriciels conviennent.

Quand une grande planche devient-elle nuisible ?

Deux limites distinctes. En pratique, la planche bloque le rendu de ses icônes ; si elle retarde le premier affichage, elle n’aide plus. Gardez-la à quelques centaines de kilooctets au plus bas et séparez les icônes rares, comme administration ou paramètres, dans une seconde planche chargée au besoin. Techniquement, navigateurs et mobiles limitent dimensions décodées et mémoire des canevas ; une immense planche peut être réduite silencieusement ou échouer sur un téléphone peu doté. Au-delà d’environ 2000 pixels par côté, la séparer se justifie déjà, et une planche 2x double les dimensions conçues.

Puis-je recolorer une icône sprite avec CSS ?

Pas vraiment : les couleurs sont incorporées au raster. On peut approximer avec filter — enchaîner invert, sepia, saturate, et hue-rotate pour rapprocher une icône noire d’une teinte cible. Mais c’est réellement un bricolage : les valeurs sont trouvées par essais ou solveur, n’atteignent pas une couleur de marque exacte, échouent sur les dessins multicolores et sont illisibles pour la personne suivante dans le code. Une seconde planche dans l’autre couleur est plus honnête qu’une chaîne de filtres. Si la recoloration est un vrai besoin, ce besoin vous indique d’utiliser SVG.

Regroupez vos icônes dans une planche de sprites

Sprite Gen prend les images déposées, les compacte dans une planche avec l’espacement choisi et écrit les coordonnées, pour éviter de relever manuellement les décalages sur un canevas. Choisissez CSS et vous obtenez une règle de base commune contenant background-image, background-repeat: no-repeat et display: inline-block, et une règle par sprite avec sa valeur négative de background-position et la largeur et la hauteur en pixels, exactement selon la structure décrite ci-dessus. Choisissez SCSS et le même résultat arrive sous forme de %sprite-base espace réservé, chaque règle d’icône l’intégrant via @extend. Choisissez JSON pour obtenir plutôt les coordonnées brutes des images, à intégrer dans vos propres outils. Tout s’exécute dans votre navigateur : rien n’est téléversé où que ce soit.

Ouvrir le générateur de planches