En bref :
Sans consigne, l’IA écrit un CSS moyen. C’est normal parce qu’elle imite les conventions de la communauté, qu’elle copie le style CSS du projet, et qu’elle préfère la solution la plus sûre à la plus propre.
Ça fait un an que je travaille pour Matomo. On utilise l’IA quotidiennement pour développer. J’ai vite remarqué une chose : le CSS qu’elle écrit n’est pas génial.
Je pense même que c’est logique pour une IA de ne pas produire de code CSS maintenable par défaut. Je vous explique les trois raisons que j’ai identifiées.
Une IA apprend à partir du code qui existe déjà. Or, sur la plupart des projets, le CSS est de qualité moyenne. Peu d’équipes suivent une méthodologie, et celles qui le font appliquent les conventions de nommage sans respecter les contraintes d’architecture. Ça a donc juste l’apparence d’un code propre au départ, et très vite ça dérape : les feuilles de styles grossissent, les sélecteurs s’allongent, on connaît la suite…
L’IA a donc lu énormément de CSS « moyen ». Elle n’écrit pas moins bien que ce qu’on a l’habitude de voir sur les projets. Elle est dans la moyenne.
Avant d’écrire, un assistant de code comme Claude Code ouvre les fichiers du projet liés à la demande : le composant à modifier, ses voisins, leurs feuilles de styles. Ce code devient son modèle : l’IA l’imite. C’est plutôt une bonne chose : on veut du code homogène.
Sauf que la base CSS de Matomo était vieillissante. Matomo est open source, alors je me permets de vous partager un extrait pour être plus concret :
/* plugins/CoreHome/stylesheets/dataTable/_limitSelection.less */
.limitSelection.disabled > div {
opacity: 0.5;
cursor: not-allowed;
}
.limitSelection > ul > li {
cursor: pointer;
font-weight: bold;
}
Ces deux règles décrivent le chemin exact vers l’élément : des classes collées, le symbole >,
des noms de balises. Si on ajoute une <div> autour, ou si on remplace la <ul>, le style saute.
Rien ne signale l’erreur.
Autre exemple réel, le ciblage par position :
/* plugins/PrivacyManager/stylesheets/compliance.less */
th:nth-child(1),
td:nth-child(1) { width: 16%; }
th:nth-child(2),
td:nth-child(2) { width: 34%; }
La largeur dépend du numéro de la colonne. Si on ajoute une colonne, toutes les largeurs se décalent.
Quand l’IA voit ce code, elle en déduit que c’est « comme ça qu’on fait ici ». Elle reproduit donc le même style. Voici du code neuf, écrit avec l’IA fin 2025 :
/* plugins/Morpheus/stylesheets/uibase/_languageSelect.less */
nav .nav-wrapper .languageSelection {
.title {
display: block;
}
}
On y retrouve les mêmes habitudes que dans le vieux code : des noms de balises et une longue chaîne de sélecteurs.
C’est la raison que je trouve la plus intéressante. L’IA cherche la solution dont elle est la plus sûre. Elle veut que ça marche, tout de suite, sur ce cas précis.
En CSS, quand deux règles visent le même élément, c’est la plus « spécifique » qui
gagne. Un sélecteur plus long ou un id augmente cette spécificité. Un !important fait passer la règle
devant presque tout.
Alors, quand deux règles se battent, l’IA ne retire pas la cause du conflit. Elle ajoute de la force : un sélecteur
plus long, un !important, un id.
Voici un cas réel, écrit avec l’IA sur Matomo. Pour être sûre que sa règle l’emporte, elle a ajouté une classe parente devant le sélecteur :
/* plugins/UserCountryMap/stylesheets/realtime-map.less */
/* Avant */
.realTimeMap_overlay {
text-shadow: 1px 1px 1px #FFFFFF, -1px 1px 1px #FFFFFF, …;
}
/* Après */
.RealTimeMap_container .realTimeMap_overlay {
text-shadow: none;
}
La règle marche. Mais elle pèse maintenant deux classes, et la prochaine règle qui voudra la modifier devra en peser trois.
Je vous vois déjà faire la grimace. Même les experts ont fait ça à leurs débuts ou quand c’est l’urgence. Il ne faut pas oublier aussi que la majorité des développeurs ne connaissent pas d’autres façons de faire, et c’est normal.
Chaque correction de bug vient ajouter de la complexité. Au bout d’un moment, plus personne ne sait pourquoi ça marche. Surtout, personne ne peut deviner ce qu’il se passerait si on réduisait un sélecteur à sa version minimum.
C’est la même chose pour la réutilisation. Toucher à un composant existant, c’est risquer de casser un autre écran. L’IA préfère donc recréer un composant à côté, qu’elle maîtrise. C’est plus rapide aujourd’hui, mais on aura deux codes à maintenir demain.
Aucune de ces habitudes n’est absurde. Prises une par une, elles marchent. Le problème, c’est qu’elles suivent toutes la même logique : régler le cas visible.
À l’échelle d’un projet, cette logique donne un CSS qu’on ne peut plus maintenir. Le cercle se referme alors : l’IA apprend du CSS moyen, en écrit à son tour, les projets en contiennent davantage, et l’IA en apprend encore.
Il existe des méthodologies CSS pour sortir de ce cercle vicieux. Ma préférée, depuis longtemps, c’est BEM. Son idée centrale : des sélecteurs courts, presque toujours d’une seule classe. Elle apporte trois avantages :
On a donc décidé d’expliquer à notre IA comment écrire du code CSS propre. On lui a donné des règles à suivre à chaque fois que sa tâche en cours modifie du CSS. Spoiler : il y avait beaucoup plus de règles à écrire que ce que j’avais prévu ! C’est l’objet de l’article suivant, il est encore en cours de rédaction.
Si vous utilisez l’IA sur votre projet, votre CSS existant est son premier modèle. Tant qu’il reste désordonné, elle le reproduira. Pour l’aider, il faut lui donner des règles explicites, ou nettoyer le code qu’elle imite.