Aller au contenu
Allin

Embellir ou minifier : à quoi sert chacun, et ce que ça change au poids

Publié le 10/08/2026 · 15 min de lecture · Outils pour développeurs

Daniel Okonkwo

Daniel OkonkwoDéveloppeur front-end et rédacteur Tech chez Allin

Performance web · Formats de fichiers

Vérifié à partir de 4 sources

Voir le profil
En bref

Embellir sert à lire ; minifier sert à livrer. L'argument du poids est bien plus faible que le nombre d'octets bruts ne le laisse croire, parce que tout ce que tu livres passe par gzip et que gzip gère déjà la répétition — or l'indentation est le texte le plus répétitif qui soit. Quatre feuilles de style sont passées dans ce minifieur, les deux tailles relevées. Un composant de carte écrit à la main est passé de 1 612 à 1 118 octets, soit 30,6 % en moins ; gzippé, de 659 à 555, un gain de 104 octets. Séparer les deux passes est la vraie leçon. Sur les mêmes quatre fichiers, retirer les espaces seules a fait gagner 48, 103, 104 et 147 octets compressés — une centaine environ, quelle que soit la taille, de 1,6 ko à 22 ko. Retirer les commentaires seuls a fait gagner 57, 1 358, 2 420 et 1 042 octets. Sur un fichier très commenté, les commentaires représentent 96 % du gain réel ; les espaces, une erreur d'arrondi. Minifie donc pour les commentaires et le code mort, pas pour les retours à la ligne, et jamais au prix de la justesse. Ce minifieur-ci tient en cinq expressions régulières et casse cinq choses : il supprime l'espace que le CSS exige de part et d'autre du + dans calc(), si bien que calc(100% + 16px) devient une déclaration invalide que le navigateur jette ; il modifie l'intérieur des chaînes, transformant content: "a; b" en "a;b" ; il efface une chaîne qui ressemble à un commentaire ; il avale une séquence de commentaire dans un data-URI ; et il réécrit a[title="hello, world"]. Le même site propose css-compressor, un analyseur qui n'en casse aucune et pèse 2 octets de plus sur un fichier de 22 746 octets.

Quatre vraies feuilles de style passées au minifieur, mesurées brutes puis après gzip. Retirer toutes les espaces a fait gagner 48, 103, 104 et 147 octets compressés ; retirer les commentaires, 57, 1 358, 2 420 et 1 042. Et les cinq entrées que ce minifieur casse.

La mesure que la plupart des articles sautent : après gzip

Un minifieur annonce le nombre d'octets bruts parce que c'est le seul qu'il sait calculer. Ce n'est pas celui qui voyage. Toute feuille de style servie en HTTP arrive compressée, et les deux opérations se recouvrent : gzip encode une suite d'octets répétés en une courte référence arrière, et quatre espaces d'indentation répétés trois cents fois sont la chose la moins chère qu'il rencontrera jamais. Retirer cette indentation avant de compresser supprime un travail que gzip faisait gratuitement.

Les deux passes ont donc été séparées et mesurées chacune de son côté. Retirer seulement les commentaires, puis seulement les espaces, puis les deux, sur quatre fichiers : un composant de carte écrit à la main de 1 612 octets, et trois feuilles de style prises telles quelles dans le dépôt de ce site. Compressée en gzip niveau 9, la passe d'espaces a fait gagner 48, 103, 104 et 147 octets. Pas des pourcentages : des octets, et à peu près la même centaine que le fichier fasse 1,6 ko ou 22 ko, parce qu'il n'y a qu'un nombre limité d'indentations distinctes dans une feuille de style, si longue soit-elle. La passe de commentaires, sur les mêmes quatre fichiers, a fait gagner 57, 1 358, 2 420 et 1 042 octets compressés.

Un fichier suffit à faire la démonstration. Le globals.css de ce site pèse 7 148 octets, dont une bonne part est de la prose expliquant le choix de chaque couleur. Retirer les espaces l'a fait passer de 3 140 octets gzippés à 3 036 : 104 octets, 3,3 %. Retirer les commentaires l'a fait tomber à 720, soit 2 420 octets gagnés, 77 %. Quatre-vingt-seize pour cent du gain réel venaient des commentaires, et rien des retours à la ligne. Voilà tout l'argument dans un seul fichier : minifie pour retirer ce que le navigateur ne peut pas utiliser, pas pour retirer ce dont le compresseur s'occupe déjà.

Embellir ne coûte presque rien une fois compressé

La symétrie vaut dans l'autre sens, et c'est la partie utile. L'exemple fourni par l'embellisseur HTML est une page minifiée de 485 octets. Embellie avec une indentation de deux espaces, elle passe à 627 octets : 142 de plus, une hausse de 29,3 %, le chiffre qui inquiète. Gzippée, elle passe de 343 à 369 octets : 26 octets, 7,6 %. Vingt-six octets, ce n'est rien. Si tu livres une page lisible pour une raison — un exemple de documentation, un modèle d'e-mail que quelqu'un doit modifier, une page dont tu veux que le code source soit consultable — le coût compressé de la lisibilité est inférieur à une requête de favicon.

L'embellisseur HTML mérite aussi qu'on comprenne ce qu'il refuse de faire. Il recopie le contenu de pre et textarea octet pour octet, parce que leurs espaces sont rendues. Il conserve une espace unique entre deux éléments en ligne, parce que la supprimer collerait deux mots à l'écran. Et il ne touche pas à un attribut entre guillemets, si bien que title="a > b" survit intact au lieu d'être coupé au chevron. Ces trois comportements ont été vérifiés directement et tiennent tous les trois.

Il a tout de même un angle mort, et c'est justement celui que son propre commentaire prétend avoir fermé. Deux boutons séparés par une espace — <div><button>A</button> <button>B</button></div> — ressortent du minifieur en <button>A</button><button>B</button>. Les boutons sont en inline-block : cette espace est donc dessinée, et l'écart entre les deux boutons disparaît. La règle appliquée est que l'espace entre deux balises de bloc est invisible, et sa liste d'éléments en ligne contient a, b, span, code et une vingtaine d'autres, mais pas button, ni select, ni une image dans un lien. Vérifie toute rangée de boutons après minification.

Cinq entrées que ce minifieur CSS traite mal

La page css-minifier tient en cinq expressions régulières : supprimer les commentaires, réduire les suites d'espaces à une seule, retirer les espaces autour d'un jeu de caractères de ponctuation, supprimer un point-virgule avant une accolade fermante, rogner. C'est suffisant pour une feuille de style que tu as écrite il y a une heure, et insuffisant pour tout le reste, parce qu'une expression régulière ne distingue pas la structure du contenu.

Le premier échec est le grave. CSS Values and Units Level 3 précise, dans la syntaxe de calc(), qu'une espace est obligatoire de part et d'autre des opérateurs + et -. La liste de ponctuation contient ici le +, donc calc(100% + 16px) ressort en calc(100%+16px) et la déclaration entière devient invalide : le navigateur la jette et retombe sur ce qui existait avant. Comme le - n'est pas dans la liste, calc(100% - 16px) survit intact, ce qui est pire — la moitié de tes expressions calc fonctionnent, l'autre moitié disparaît en silence. Ce n'est pas un cas inventé. Le fichier apps/web/components/app/app-shell.module.css de ce dépôt contient calc(74px + env(safe-area-inset-bottom)), la marge qui tient la barre d'onglets mobile à l'écart de l'indicateur d'accueil. Passe-le dans cet outil et cette marge a disparu.

Les quatre autres viennent de la même racine : les chaînes et les data-URI sont du contenu, et les passes les traitent comme de la structure. content: "a; b" devient content:"a;b", parce que le point-virgule est dans la liste de ponctuation. content: "{ }" devient content:"{}". a[title="hello, world"] devient a[title="hello,world"] et le sélecteur cesse de correspondre. Une feuille de style dont la chaîne content contient les caractères qui ouvrent et ferment un commentaire CSS — content: "/* not a comment */" — ressort en content:"", la chaîne vidée. Et une image de fond écrite en data-URI qui contient par hasard la même séquence de deux caractères perd tout ce qu'il y a entre : url("data:image/svg+xml,...%3E/*x*/%3C...") arrive avec le milieu supprimé et l'image morte.

Deux choses qu'il ne casse pas, pour l'équilibre. Les propriétés personnalisées survivent : --shadow: 0 2px 8px rgba(0, 0, 0, 0.1) ressort en --shadow:0 2px 8px rgba(0,0,0,0.1), soit la même valeur. Les requêtes média survivent aussi : @media screen and (min-width: 600px) devient @media screen and (min-width:600px), toujours valide, et la syntaxe d'intervalle moderne @media (400px <= width <= 700px) reste intacte, parce que <= n'est pas dans la liste de ponctuation.

Deux minifieurs sur le même site, et le sûr pèse 2 octets de plus

La page css-compressor utilise un autre moteur. Au lieu de reconnaître des motifs, il parcourt le fichier caractère par caractère et marque chacun comme structure ou littéral : tout ce qui se trouve dans une chaîne, un commentaire ou une paire de parenthèses est littéral, et aucune transformation n'a le droit d'y toucher. C'est ce seul indicateur qui préserve un data-URI, une expression calc() et les décimales d'un rgba().

Tout l'intérêt de l'approche naïve était censé être sa compacité. Elle ne l'est pas. Les deux ont tourné sur les trois mêmes feuilles de style réelles. Sur app-shell.module.css, 22 746 octets en entrée, la version à expressions régulières a produit 19 278 octets et l'analyseur 19 280 — 2 octets d'écart, et en gzip l'analyseur était même plus petit d'un octet, 4 191 contre 4 192. Sur admin.css l'écart était de 1 octet brut et 2 octets gzippés. Sur globals.css, de 8 octets bruts. Huit octets, sur un fichier où la version sûre conserve l'espace dans @media (min-width: 600px) et dans rgba(0, 0, 0, 0.1). Il n'y a aucun argument de taille en faveur de la version à expressions régulières ; c'est simplement celle qui détruit parfois le fichier.

Le SVG est l'exception, et il a son propre bug

Tout ce qui précède dit que l'argument du poids en faveur de la minification est faible. Le SVG est là où il est fort, parce que le poids d'un SVG exporté n'est pas fait d'espaces. Un petit dessin enregistré depuis un éditeur vectoriel transporte une déclaration XML, un commentaire de l'éditeur, un titre, une description, un bloc de métadonnées RDF, une vue nommée avec le dernier niveau de zoom, deux espaces de noms d'éditeur, une transformation matricielle identité, une épaisseur de trait et une opacité fixées à leur valeur par défaut, et des coordonnées écrites à sept décimales. Passe un tel fichier — 1 071 octets — dans svg-optimizer et il ressort à 256 octets, 76,1 % en moins. Gzippé : 603 puis 207, 65,7 % en moins. Ce gain est réel parce que la matière retirée est du texte unique, et le texte unique est précisément ce dont un compresseur ne peut rien faire.

Cette même exécution a fait apparaître un défaut à connaître avant de s'en servir. Les couleurs traversent deux étapes dans le mauvais ordre : elles sont d'abord raccourcies, puis remises à la passe d'arrondi des nombres, qui lit les chiffres hexadécimaux comme des nombres. stroke="#000000" est raccourci en #000 puis arrondi en #0. black devient #0. red devient #f0. Et comme le motif numérique accepte la notation scientifique, une couleur hexadécimale dont les chiffres encadrent un e explose : #e5e7eb ressort en #e50000000eb, #1e293b en #1e+293b, #0e7490 en #0. La même passe réécrit url(#g-0010) en url(#g-10) tout en laissant id="g-0010" intact, si bien que le dégradé visé disparaît. Tout cela a été reproduit dans un vrai navigateur, avec les réglages par défaut. Désactiver l'option d'arrondi des nombres évite tous ces cas, au prix de quelques octets de précision.

Deux autres points sur le même outil. Il retire title et desc au titre des métadonnées d'éditeur — or ce sont les deux éléments qu'un lecteur d'écran annonce pour un SVG, si bien qu'une icône accessible cesse de l'être. Et son contrôle du balisage mal formé, lui, est du genre soigneux : un navigateur signale une erreur XML en greffant un élément nommé parsererror, si bien que chercher ce seul nom refuserait tout dessin valide qui en porte un. L'outil interroge donc le moteur : une fois par session, il analyse quelque chose de délibérément cassé, lit l'espace de noms du marqueur qui revient, et ne regarde plus que là. Un dessin qui contient un élément parsererror s'optimise normalement ; une balise non fermée reste refusée.

Les deux passes mesurées séparément sur quatre fichiers réels, octets bruts et gzip niveau 9
FichierOriginal, brut / gzipEspaces seules, gain gzipCommentaires seuls, gain gzip
Composant de carte écrit à la main1 612 / 65948 octets57 octets
app-shell.module.css (dense, peu commenté)22 746 / 5 661103 octets1 358 octets
globals.css (très commenté)7 148 / 3 140104 octets2 420 octets
admin.css4 767 / 1 855147 octets1 042 octets
SVG exporté d'un éditeur, optimiseur complet1 071 / 603Métadonnées, pas espaces396 octets, 65,7 %
Minificateur CSSRéduis du CSS en supprimant les commentaires et les espaces inutiles.Essayer l'outil

Questions fréquentes

Minifier le CSS vaut-il encore le coup si le serveur gzippe tout ?
Oui, mais pour les commentaires, pas pour les espaces. Sur les quatre fichiers mesurés ici, retirer toutes les espaces et retours à la ligne a fait gagner 48, 103, 104 et 147 octets gzippés — une centaine d'octets chacun, que le fichier fasse 1,6 ko ou 22 ko, parce que gzip encode l'indentation répétée quasi gratuitement. Retirer les commentaires a fait gagner 57, 1 358, 2 420 et 1 042 octets gzippés sur les mêmes fichiers. Si ta feuille de style est documentée, les commentaires sont tout le gain ; sinon, la minifier te rapportera environ un dixième de kilo-octet et l'effort serait mieux placé dans une passe de suppression du CSS inutilisé. Ce à quoi la minification sert vraiment, dans une chaîne de compilation, c'est qu'elle est livrée avec les passes qui comptent : retirer les règles qu'aucun élément de la page n'utilise, fusionner les déclarations en double, raccourcir couleurs et unités.
Ma mise en page a cassé après minification. Où regarder en premier ?
Cherche calc( dans le fichier minifié et lis-les tous. Si l'un contient un plus sans espaces autour — calc(100%+16px) — cette déclaration est invalide et le navigateur l'ignore. Le CSS exige une espace de part et d'autre de + et - dans calc(), et un minifieur à expressions régulières qui resserre la ponctuation la supprime. Ensuite, cherche content: ainsi que tout sélecteur d'attribut contenant une virgule ou un point-virgule, parce qu'un minifieur naïf modifie l'intérieur des chaînes. Regarde ensuite les data-URI : si l'un contenait les deux caractères qui ouvrent un commentaire CSS puis, plus loin, les deux qui le ferment, tout ce qui se trouvait entre a été retiré. Enfin, compare le nombre de règles avant et après ; s'il a baissé, c'est du structurel qui a disparu, pas de l'espace.
Dois-je embellir un fichier minifié que je n'ai pas écrit, avant de le modifier ?
Oui, et c'est l'usage honnête d'un embellisseur. Reformater ne change que les espaces : cela ne peut pas altérer le comportement, et cela transforme un fichier d'une seule ligne en quelque chose qu'un diff sait décrire. Deux réserves. D'abord, un fichier embelli n'est pas automatiquement un fichier source — si l'original était généré depuis Sass, TypeScript ou une bibliothèque de composants, modifier la sortie signifie que ta modification disparaît à la compilation suivante. Ensuite, embellir puis reminifier n'est pas toujours l'identité : sur le moteur HTML testé ici, un aller-retour ajoute une espace de part et d'autre du texte de chaque élément non en ligne, faisant passer une page de 133 octets à 145 puis se stabilisant. Sans effet dans le navigateur, mais ne t'attends pas à retrouver un fichier identique à l'octet.
Pourquoi l'optimiseur SVG fait-il gagner tellement plus que le minifieur CSS ?
Parce qu'il retire une matière différente. Un minifieur CSS retire des espaces et des commentaires ; un compresseur gère déjà les espaces, donc seuls les commentaires comptent. Un optimiseur SVG retire des métadonnées d'éditeur, des attributs par défaut, des groupes vides et des décimales superflues — autant de texte unique qu'un compresseur ne peut pas replier. Dans le fichier mesuré ici, 1 071 octets sont devenus 256 en brut, et 603 octets gzippés sont devenus 207, une baisse de 65,7 % qui a survécu presque intacte à la compression. La leçon se généralise : tout minifieur qui se contente de reformater te décevra après gzip, et tout minifieur qui supprime du contenu ne te décevra pas. C'est aussi pourquoi la suppression de contenu est la partie à vérifier.
Brotli diffère-t-il assez de gzip pour changer la réponse ?
Non, elle la renforce légèrement. La feuille de style de carte s'est comprimée à 527 octets en Brotli contre 659 en gzip, et minifiée elle est descendue à 451 contre 555. Le gain absolu de la minification était de 76 octets en Brotli et de 104 en gzip : un meilleur compresseur laisse moins à retirer au minifieur, exactement comme on s'y attend, puisque les deux retirent la même redondance. Donc si ton hébergeur sert du Brotli — la plupart des CDN le font — l'argument en faveur de la minification des espaces est encore plus faible, et celui en faveur du retrait des commentaires est inchangé, parce qu'un commentaire est du texte unique quel que soit l'algorithme.

Articles qui pourraient t'intéresser

Tous les guides
GuideCe qu'un minifieur peut retirer, et ce qu'il ne doit pas toucherLa minification doit préserver le sens, et l'intéressant, ce sont les espaces qui portent du sens : le combinateur de descendance, les espaces dans calc(), l'écart entre deux éléments en ligne. Mesuré ici sur de vrais fichiers, y compris ce que brotli te faisait déjà gagner.TutorielConstruire un tableau Markdown de zéro, sans compter les tirets à la mainLa plus petite chose qui soit encore un tableau tient en deux lignes : une ligne d'en-tête et une ligne de séparation. Voici pourquoi la seconde est obligatoire en GitHub Flavored Markdown, où les tableaux à barres n'existent pas du tout, et ce qu'un générateur fait que la saisie manuelle ne peut pas faire.GuideColler un tableau dans une pull request : ce qui casse, et les deux caractères qui cassent toutUn tableau Markdown n'interdit que deux caractères dans une cellule : la barre verticale et le saut de ligne. Voici ce que fait chacun, comment un convertisseur les traite, pourquoi l'échappement doit être appliqué dans le bon ordre, et pourquoi l'alignement du source ne compte jamais.GuideTransposer un tableau dont les lignes auraient dû être des colonnesCe qu'il advient de la ligne d'en-tête, des lignes de longueurs inégales, des types — et la seule chose pour laquelle on confond régulièrement la transposition et qu'elle ne sait pas faire.ExplicationXML vers JSON : attributs, répétition et le piège du tableau à un seul élémentDeux documents qui ne diffèrent que par le nombre d'enfants produisent deux formes JSON différentes, et aucun convertisseur ne peut les distinguer sans schéma. Plus ce que celui-ci fait réellement des attributs, du contenu mixte et des espaces — et la seule chose qu'il ne sait toujours pas consigner.ExplicationCSV vers JSON : les cinq cas qui cassent tous les convertisseursDélimiteurs entre guillemets, sauts de ligne intégrés, types ambigus, en-têtes en double et encodage. Chaque cas a été passé dans le convertisseur et la sortie exacte est reproduite ici — y compris les deux qu'il ne rattrape pas.

Outils similaires

Ces chiffres viennent de l'exécution de ces outils sur de vrais fichiers, puis de la compression du résultat, pas d'une promesse d'éditeur. Le poids dépend entièrement du fichier : une feuille de style écrite avec de longs commentaires ne se comprime pas comme une feuille sans commentaires, et tes chiffres ne seront pas les nôtres. Les minifieurs ne se valent pas non plus — deux outils de ce site divergent sur la même entrée — alors traite toute sortie minifiée comme du code neuf, à relire avant mise en ligne. Garde l'original lisible dans le gestionnaire de versions, minifie à la compilation, et vérifie la page dans un navigateur avant de publier.

Sources

Tu as repéré une erreur dans cet article ?