Pendant des années, colorer trois mots dans un bloc de code signifiait souvent fabriquer trente éléments HTML.

Un tokenizer reconnaît const, une chaîne, un nombre ou un commentaire, puis le résultat devient une forêt de <span class="token …">. La méthode fonctionne très bien, au point que Prism et Highlight.js ont coloré une bonne partie du Web technique de cette manière.

Microlighter 2.1.0 choisit désormais un autre endroit pour ranger les tokens : hors du DOM.

La bibliothèque de Dave Rupert crée des Range sur le texte existant, groupe ces plages dans des objets Highlight, les enregistre dans CSS.highlights, puis laisse ::highlight() appliquer les couleurs.124

Le code reste du texte. La coloration devient une couche de peinture du navigateur.

C’est une idée plus importante que le chiffre « 2 Ko » affiché sur le projet.

Un nœud

Le cœur de Microlighter ne remplace pas le contenu du <code> par du HTML tokenisé. Lorsqu’une règle TextMate correspond à une portion de texte, le code construit un Range, fixe son début et sa fin dans le nœud texte, puis ajoute cette plage à une catégorie comme keyword, string ou comment.2

Le registre CSS reçoit ensuite les groupes de ranges. Un thème peut écrire :

::highlight(keyword) {
  color: var(--syntax-keyword);
}

MDN décrit précisément cette propriété du CSS Custom Highlight API : cibler des plages arbitraires sans affecter la structure DOM sous-jacente.45

Restait à vérifier si cette promesse survivait à autre chose qu’un exemple de README.

Après build du dépôt au commit d7fec9e, les 15 tests Node passent. Un Chromium Playwright temporaire a ensuite servi à lancer la suite navigateur en mode CI, où 26 tests sur 26 passent, notamment les 37 grammaires, les thèmes, les syntaxes imbriquées Svelte/Vue/HEEx, le composant optionnel et l’édition en direct.2

Un test est particulièrement révélateur : après modification d’un bloc contenteditable, il exige que celui-ci possède toujours un seul enfant, un Text node, tout en vérifiant que des ranges de highlight pointent bien dedans.2

Comparaison entre le markup à spans de Prism et Highlight.js et les Range CSS de Microlighter autour d’un même extrait JavaScriptDans notre extrait de 185 caractères, Prism produit 46 spans et 1 998 caractères de markup ; Highlight.js 18 spans et 858 caractères. Microlighter garde le texte dans le DOM et stocke la coloration dans CSS.highlights. Benchmark IRZ avec Microlighter 2.1.0, Prism 1.30.0 et Highlight.js 11.12.0

Quarante-six spans

Le même extrait JavaScript de 185 caractères a ensuite été envoyé aux API Node de Prism 1.30.0 et Highlight.js 11.12.0 afin de rendre la différence tangible.

Prism renvoie 1 998 caractères de HTML et 46 <span>. Highlight.js renvoie 858 caractères et 18 spans.

Ce ratio ne mesure ni le temps de rendu ni la mémoire. Il montre simplement où vit l’information de tokenisation : dans le markup pour ces deux sorties, dans le registre de highlights pour Microlighter.

La distinction devient particulièrement séduisante dans un éditeur : modifier du texte tokenisé traditionnellement oblige à gérer une structure susceptible d’être reconstruite au fil des frappes, alors que Microlighter peut conserver le même nœud texte et recalculer les ranges.2

Le navigateur n’a pas supprimé le travail. Il a offert un canal de rendu distinct du DOM.

Deux kilo-octets

Le deuxième argument de Microlighter est sa taille. Le build 2.1.0 impose même un budget : son bundle auto-runner doit rester sous 2,05 KiB gzip. Notre build reproduit exactement ce chiffre : 4,41 KiB minifiés, 2,05 KiB gzip, 1,88 KiB Brotli.2

Mais télécharger le noyau ne colore encore aucun JavaScript. Les grammaires sont chargées à la demande et le thème est un fichier CSS séparé.

Les versions utilisées dans ce test, Microlighter 2.1.0, Prism 1.30.0 et Highlight.js 11.12.0, ont donc été empaquetées depuis npm avant d’additionner la taille gzip des ressources nécessaires à des scénarios comparables.367 Chaque format reçoit son thème, sans supposer de fusion par bundler, de cache CDN ni de compression conjointe.

Barres comparant les tailles gzip de Microlighter, Prism et Highlight.js pour JavaScript puis HTML CSS JavaScript avec thèmeAvec JavaScript et un thème, Microlighter totalise 4,08 Ko gzip, Prism 5,89 Ko et Highlight.js 28,61 Ko. Pour HTML+CSS+JS : 5,77 Ko, 7,55 Ko et 35,82 Ko. Benchmark IRZ sur les paquets npm Microlighter 2.1.0, Prism 1.30.0 et Highlight.js 11.12.0

Pour JavaScript + thème :

  • Microlighter : 4 081 octets gzip ;
  • Prism : 5 890 octets ;
  • Highlight.js : 28 610 octets.

Pour HTML + CSS + JavaScript + thème :

  • Microlighter : 5 766 octets ;
  • Prism : 7 552 octets ;
  • Highlight.js : 35 816 octets.

Sur ces configurations précises, Microlighter est donc environ 31 % plus petit que Prism en JavaScript et 24 % sur le trio Web. L’écart avec le core modulaire de Highlight.js est beaucoup plus grand.

Ces chiffres ne sont pas des octets réseau universels. Un CDN peut Brotli-compresser autrement, un bundler fusionner les ressources, le cache supprimer certains transferts et un projet réel charger d’autres langues. Ils donnent seulement une comparaison reproductible des artefacts npm que nous avons réellement ouverts.

Le prix caché

Le cœur peut rester à 2 Ko parce que beaucoup de matière arrive plus tard.

Microlighter 2.1.0 distribue 37 fichiers de grammaire. Notre build mesure une moyenne de 0,73 KiB gzip par grammaire, de 0,24 KiB pour JSON jusqu’à 1,37 KiB pour JavaScript. Les dix thèmes tournent autour de 0,56 KiB gzip chacun.2

Certaines langues tirent aussi leurs dépendances. HTML déclare CSS, JSON et JavaScript. TypeScript dépend de JavaScript, tandis que TSX dépend de JavaScript et TypeScript.2

Le lazy loading est donc la vraie stratégie : ne pas vendre 37 langues à une page qui n’en montre qu’une.

Un petit détail du dépôt illustre d’ailleurs la vitesse d’évolution du projet. Le haut du README annonce encore « 35 languages », alors que la section Languages en énumère 37 et que le build produit effectivement 37 modules.2 Rien de grave, simplement un nombre marketing devenu périmé plus vite que le code.

À titre de contexte, les paquets inspectés contiennent 298 composants minifiés prism-* et 386 fichiers sous highlight.js/lib/languages. Ce ne sont pas des nombres directement comparables de langues supportées : aliases, dépendances et variantes font diverger fichiers et langages. Ils montrent néanmoins que Microlighter vise volontairement une surface plus petite.

Le README pose lui-même la limite : la couverture et la précision des grammaires restent inférieures à celles d’une solution plus lourde comme Shiki.29

Regex natives

Cette économie vient aussi du tokenizer.

Microlighter réutilise des grammaires inspirées de TextMate mais les exécute avec le RegExp natif du navigateur. Il n’embarque ni Oniguruma, ni WebAssembly, ni moteur de tokenisation massif.2

Le code parcourt les règles, résout les include, compile les expressions au besoin, trouve le prochain match puis descend récursivement dans les paires begin/end.2

Cela suffit pour les 37 grammaires fournies et leur suite de tests. Cela ne signifie pas que n’importe quelle grammaire TextMate complexe peut être déposée telle quelle et obtenir la fidélité d’un moteur qui implémente l’ensemble des constructions attendues.

Le projet choisit un sous-ensemble utile contre un moteur général.

Dans de petits articles techniques, une documentation ou un éditeur léger, ce compromis peut être excellent. En revanche, reproduire au pixel près la coloration d’un éditeur sur des dizaines de syntaxes exotiques risque de faire revenir le poids économisé sous forme de travail de grammaire.

La technique dépend aussi d’une capacité Web relativement jeune.

MDN classe le CSS Custom Highlight API Baseline 2025 et indique une disponibilité dans les navigateurs récents depuis juin 2025, tout en avertissant que les appareils ou navigateurs plus anciens peuvent ne pas la prendre en charge.4

C’est très différent d’une solution à spans, dont le mécanisme de rendu repose sur du HTML et du CSS presque intemporels.

Sur un site qui exige de vieux navigateurs, la matrice de compatibilité devient une vraie décision produit. Dans un outil moderne qui contrôle son environnement, le navigateur peut enfin prendre en charge une partie que les bibliothèques devaient auparavant matérialiser elles-mêmes.

Pas de gras

Le Custom Highlight API possède une autre restriction intéressante : ::highlight() n’accepte qu’un ensemble limité de propriétés CSS.5

Dave Rupert le souligne directement : pas de changement arbitraire de graisse ou de famille de police pour reproduire certains thèmes riches.1 Microlighter organise donc ses thèmes principalement autour de couleurs de catégories sémantiques.

Ce qui ressemble à une limitation stylistique est aussi une conséquence cohérente de l’architecture : le pseudo-élément de highlight peint une plage de texte, il ne devient pas un nouvel élément typographique complet.

Accessibilité

« DOM propre » peut facilement devenir une formule magique, alors nous avons testé quelque chose de beaucoup plus banal : le contraste des couleurs fournies.

L’audit prend les dix thèmes de Microlighter 2.1.0 et calcule le ratio entre leurs 14 variables de couleur de tokens et le fond, en mode clair puis sombre. Le seuil retenu est 4,5:1, celui de WCAG 2.2 pour le texte normal.8

Tableau des dix thèmes Microlighter indiquant combien de leurs quatorze couleurs de tokens passent sous 4,5 contre le fond en modes clair et sombre45 combinaisons sur 280 passent sous 4,5:1 dans cet audit. GitHub, Vesper et VSCode Plus n’en ont aucune ; d’autres thèmes privilégient parfois une couleur plus discrète, souvent celle des commentaires. Audit de contraste IRZ sur Microlighter 2.1.0

Résultat : 45 combinaisons thème × token × mode sur 280 sont sous 4,5:1.

Trois thèmes n’en ont aucune dans notre grille : github, vesper et vscode-plus. Le thème GitHub descend au minimum à 4,55:1 sur les commentaires en mode clair et 6,15:1 en sombre. À l’opposé, la palette claire de solarized-light place les quatorze couleurs de tokens sous 4,5 dans notre calcul.

Ce tableau ne vaut évidemment pas certificat de conformité : tailles réelles, couleurs adjacentes, modes forcés, contexte de page et besoins des utilisateurs exigent toujours un audit complet. Il montre seulement qu’une architecture de rendu moderne ne garantit pas, à elle seule, des choix chromatiques accessibles.

Le CSS Highlight API n’ajoute pas non plus une sémantique d’accessibilité aux mots-clés. const reste du texte const, seulement peint différemment. C’est plutôt une bonne propriété pour la robustesse du contenu, mais un lecteur d’écran ne reçoit pas soudain l’étiquette « keyword » parce qu’un Highlight porte ce nom.

Éditer

C’est dans un bloc éditable que l’idée prend le plus de relief.

La démo Microlighter utilise contenteditable="plaintext-only" et recalcule le surlignage à chaque input.2 Le même parcours rejoué avec Playwright conserve le texte intact après la saisie, avec toujours un seul Text node dans le bloc et de nouveaux ranges correctement enregistrés.

L’API optionnelle <micro-lighter> ajoute copie et numéros de ligne. Son bundle complet fait 3,54 KiB gzip, contre 2,05 pour l’auto-runner, avant grammaire et thème.2

La frontière devient alors assez nette : la coloration syntaxique peut rester hors du DOM, tandis que contrôles, numéros, boutons et comportements d’éditeur, eux, sont de vrais composants et reviennent naturellement dans la structure.

Cette séparation évite surtout de traiter la peinture syntaxique et l’interface comme un seul problème.

Le navigateur paye

Microlighter ne prouve donc pas seulement qu’un syntax highlighter peut être petit.

Il montre ce qui arrive quand une primitive autrefois absente du Web devient native. Une bibliothèque cesse alors de fabriquer des centaines d’éléments juste pour accrocher de la couleur et peut se concentrer sur le morceau qu’elle apporte réellement, comprendre le code et produire des ranges.

Le gain possède des contreparties claires : navigateur moderne, 37 grammaires plutôt que des centaines, interprétation TextMate simplifiée, propriétés de thème limitées, validation du contraste toujours nécessaire.

C’est précisément ce qui rend le projet intéressant.

Les 2 Ko ne sont pas une preuve de supériorité. Ils sont la conséquence visible d’une meilleure question architecturale : est-ce encore à JavaScript de matérialiser dans le DOM quelque chose que le navigateur sait désormais peindre tout seul ?