Au sein de notre agence web, nous sommes particulièrement… tatillons (voilà, c'était ça le mot) sur la qualité du code que nous produisons, notamment côté front-end qui demeure notre spécialité. Nous avons donc mis en place des conventions CSS et des outils pour nous aider à les appliquer, c'est le cas de Stylelint.
Stylelint à la rescousse
Stylelint est un outil de linting pour CSS qui joue le rôle du collègue un peu pénible mais indispensable : celui qui vous fait remarquer vos erreurs de syntaxe, vos problèmes de formatage et vos violations des conventions de codage. Sauf qu'au lieu de vous faire des remarques devant la machine à café, il le fait directement dans votre éditeur.
Une adoption à tâtons
Chez Alsacréations, de nombreuses conventions internes garantissent la qualité de nos productions et nous avons dès le départ adopté Stylelint dans nos projets web. Mais, aussi curieux que cela puisse paraître, nous l'avons mis de côté en février 2024 pour deux raisons principales :
- Projets full classes utilitaires : dans les projets utilisant Tailwind ou UnoCSS, Stylelint nous semblait (assez logiquement) moins pertinent.
- Conflits avec Prettier : parfois, les règles de Stylelint entraient en conflit avec celles de Prettier, ce qui compliquait notre quotidien de développeurs.
Ces deux obstacles ont désormais été levés : nos projets basés exclusivement sur des classes utilitaires se font rares, et les conflits avec Prettier ont disparu depuis la version 16 de Stylelint qui a abandonné la gestion des règles purement stylistiques (indentations, espaces, sauts de lignes), laissant ce domaine entièrement à Prettier.
Stylelint a été (ré)intégré dans notre process global car c'est un outil réellement puissant pour maintenir la qualité du code CSS. Il permet de :
- Corriger les erreurs automatiquement (ou du moins les signaler).
- Appliquer des limites strictes sur notre code.
- Forcer l'application de conventions d'équipe.
- Rendre notre écosystème cohérent : on utilisait déjà ESLint pour JS/Vue et Markdownlint pour MarkDown, alors pourquoi pas CSS ?
Stylelint en mode « solo »
Dans sa version basique, Stylelint est déjà très efficace pour détecter les erreurs de syntaxe et les problèmes courants dans les fichiers CSS, et surtout de les corriger automatiquement.
Sous sa configuration standard, il suffit d'enregistrer son fichier CSS pour que Stylelint corrige la majorité des erreurs de syntaxe, les préfixes vendor inutiles, les déclarations en double, etc. C'est un gain de temps considérable pour les développeurs qui n'ont plus à se soucier de ces détails.
Configurer Stylelint pour qu'il ne fasse pas de cadeaux
Adapter Stylelint à nos besoins nous a paru essentiel pour qu'il soit vraiment utile. Voici quelques règles spécifiques que nous avons trouvées particulièrement efficaces et que nous appliquons dans nos projets. À vous de les adapter à votre style et à vos conventions d'équipe, évidemment !
Sélecteurs
// Sélecteurs
"selector-max-id": 0, // on refuse les ID (oui, tous !)
"selector-max-class": 3, // on limite le nombre de classes
"selector-max-type": 3, // on limite le nombre de sélecteurs d'éléments
Exemples :
/* ❌ ID interdit */
#mon-element {
color: red;
}
/* ❌ Trop de classes */
.header.nav.menu.item.active {
font-weight: bold;
}
/* ❌ Trop de sélecteurs d'éléments */
header nav ul li a span {
text-decoration: none;
}
Unités
// Unités
"declaration-property-unit-disallowed-list": {
"/^font|^font-size/": ["px"], // pas de pixels pour les polices
}
Exemples :
/* ❌ Pixels interdits pour les polices */
.titre {
font-size: 24px;
}
/* ✅ Alternatives acceptées */
.titre {
font-size: 1.5rem;
}
Polices
// Polices
"font-weight-notation": "numeric", // on force la notation numérique (400, 600, 700…)
Exemples :
/* ❌ Mots-clés interdits (ils seront automatiquement corrigés) */
.titre {
font-weight: bold;
}
/* ✅ Notation numérique obligatoire */
.titre {
font-weight: 700;
}
Nesting
// Nesting
"max-nesting-depth": 3, // on limite la profondeur de l'imbrication
Exemples :
/* ❌ Trop d'imbrication (niveau 4) */
.header {
.nav {
.menu {
.item {
.link {
color: #ff69b4;
} // Niveau 4 interdit
}
}
}
}
Media Queries
// Media Queries
"media-feature-range-notation": "context", // on force la notation moderne
"media-feature-name-unit-allowed-list": { width: "rem" }, // on autorise uniquement les rem
Exemple :
/* ❌ Ancienne syntaxe interdite */
@media (min-width: 768px) {
}
/* ❌ Pixels interdits */
@media (width >= 48rem) and (width < 1200px) {
}
/* ✅ Notation moderne avec rem uniquement */
@media (width >= 48rem) {
}
@media (48rem <= width < 75rem) {
}
Couleurs
// Couleurs
"color-hex-length": "long", // on force la notation longue (#ffffff au lieu de #fff)
"color-named": "never", // on refuse les couleurs nommées (adieu "hotpink")
"color-function-notation": "modern", // on force la notation moderne (oklch, etc.)
Exemple :
/* ❌ Notation courte interdite (elle sera automatiquement corrigée) */
.element {
background: #f0f;
}
/* ❌ Couleurs nommées interdites */
.alert {
background: hotpink;
}
/* ❌ Ancienne syntaxe de couleur (sera automatiquement corrigée) */
.gradient {
background: rgb(255, 0, 0);
color: rgba(255, 0, 0, 0.5);
}
/* ✅ Formats acceptés */
.element {
background: #ff00ff;
}
.alert {
background: #ff69b4;
}
.gradient {
background: rgb(255 0 0);
color: rgb(255 0 0 / 50%);
}
.moderne {
color: oklch(70% 0.15 180);
}
Préfixes vendeurs
// Préfixes
"property-no-vendor-prefix": [
true, // on refuse les préfixes vendeurs
{
ignoreProperties: [
"mask",
"mask-size",
"mask-position",
"line-clamp",
"backdrop-filter",
"user-select",
"initial-letter",
"box-decoration-break",
"text-fill-color",
"text-stroke",
"tap-highlight-color",
"box-orient"
] // … sauf pour certaines propriétés encore expérimentales
}
]
Exemple :
/* ❌ Préfixes vendeurs interdits (sera corrigé automatiquement) */
.element {
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
/* ✅ Propriétés natives uniquement */
.element {
border-radius: 10px;
transform: scale(1.1);
}
/* ✅ Exceptions autorisées pour les propriétés expérimentales */
.element {
-webkit-mask: url(mask.svg);
mask: url(mask.svg);
-webkit-line-clamp: 3;
line-clamp: 3;
}
Imports
// Imports
"import-notation": "string" // pas de "url()" pour les imports
Exemple :
/* ❌ Notation url() interdite pour les imports (sera corrigé automatiquement) */
@import url("reset.css");
@import url("components/buttons.css") layer(components);
/* ✅ Notation string (moderne) préférée */
@import "reset.css";
@import "components/buttons.css" layer(components);
Laisser passer certaines exceptions
Parfois, il faut savoir faire des exceptions. Stylelint propose plusieurs moyens élégants de contourner les règles quand c'est vraiment nécessaire.
Documentation : Ignorer du code avec Stylelint
Ignorer une ligne spécifique
.class {
color: hotpink; /* stylelint-disable-line */
}
Ignorer un bloc de règles
/* stylelint-disable */
.classe-legacy {
color: red;
font-size: 12px;
}
/* stylelint-enable */
Ignorer la ligne suivante
.class {
/* stylelint-disable-next-line */
color: hotpink;
}
Ignorer des fichiers entiers
Particulièrement utile pour des fichiers externes (reset) qui ne respectent pas les conventions internes. Créez un fichier .stylelintignore
à la racine :
assets/**/*.css
legacy/**/*.scss
Installation et configuration
Nous mettons à disposition sur Github un Guide d'initialisation de projet où nous détaillons chaque étape du processus d'installation et de configuration de nos outils.
L'étape d'installation de Stylelint et de ses dépendances est celui-ci :
pnpm install --save-dev stylelint stylelint-config-standard stylelint-config-html stylelint-order stylelint-config-property-sort-order-smacss
Cette commande installe :
- stylelint : l'outil principal
- stylelint-config-standard : configuration de base
- stylelint-config-html : support HTML et Vue (React, Angular, etc.)
- stylelint-order : gestion de l'ordre des propriétés
- stylelint-config-property-sort-order-smacss : force l'ordre des propriétés selon SMACSS
Configuration dans VS Code
Voici comment nous ajoutons Stylelint à notre éditeur de code préféré.
- Installer l'extension Stylelint dans VS Code
- Créer le fichier
stylelint.config.js
à la racine du projet Configurer VS Code : dans les settings (
Cmd+,
), ajouter :{ "stylelint.validate": ["css", "scss", "html", "vue"] }
Important : Relancer VS Code pour activer les linters (
Cmd+Maj+P
→ «Developer: Reload Window»)
Exemple de configuration complète
Voici un exemple de fichier stylelint.config.js
que nous appliquons à nos projets :
/** @type {import('stylelint').Config} */
export default {
extends: ["stylelint-config-standard", "stylelint-config-html", "stylelint-config-property-sort-order-smacss"],
plugins: ["stylelint-order"],
rules: {
// Sélecteurs
"selector-max-id": 0, // on refuse les ID
"selector-max-class": 3, // on limite le nombre de classes
"selector-max-type": 3, // on limite le nombre de sélecteurs d'éléments
"no-descending-specificity": null, // on désactive la règle de spécificité descendante
// Sélecteurs spécifiques
"selector-pseudo-class-no-unknown": [true, { ignorePseudoClasses: ["deep", "global"] }],
"selector-pseudo-element-no-unknown": [true, { ignorePseudoElements: ["v-deep"] }],
"at-rule-no-unknown": [true, { ignoreAtRules: ["theme", "utility"] }],
"declaration-property-value-no-unknown": [
true,
{
ignoreProperties: {
"/^animation-/": "auto",
top: "/^anchor/",
right: "/^anchor/",
bottom: "/^anchor/",
left: "/^anchor/",
},
},
],
// Préfixes
"property-no-vendor-prefix": [
true, // on refuse les préfixes vendeurs
{
ignoreProperties: [
"mask",
"mask-size",
"mask-position",
"line-clamp",
"backdrop-filter",
"user-select",
"initial-letter",
"box-decoration-break",
"text-fill-color",
"text-stroke",
"tap-highlight-color",
"box-orient",
],
},
],
// Raccourcis
"declaration-block-no-redundant-longhand-properties": [true, { ignoreShorthands: ["grid-template"] }],
// Unités
"declaration-property-unit-disallowed-list": {
"/^font|^font-size/": ["px"], // pas de pixels
},
// Imports
"import-notation": "string", // pas de "url()" pour les imports
// Nesting
"max-nesting-depth": 3, // on limite la profondeur de l'imbrication
// Media Queries
"media-feature-range-notation": "context", // on force la notation moderne
"media-feature-name-unit-allowed-list": { width: "rem" }, // on autorise uniquement les rem
// Polices
"font-family-no-duplicate-names": null,
"font-weight-notation": "numeric", // on force la notation numérique pour les poids de police
// Couleurs
"color-hex-length": "long", // on force la notation longue pour les couleurs hexadécimales
"color-named": "never", // on refuse les couleurs nommées
"color-function-notation": "modern", // on force la notation moderne pour les fonctions de couleurs
"lightness-notation": "percentage", // on force la notation en pourcentage pour la luminosité
"alpha-value-notation": "percentage", // on force la notation en pourcentage pour l'alpha
"hue-degree-notation": "number",
},
};
Stylelint, l'essayer c'est… ben essayez-le et vous verrez !
Stylelint n'est pas juste un outil de plus dans votre boîte à outils : c'est votre garde du corps CSS qui veille à maintenir la cohérence et la qualité de votre code, même quand vous êtes en rush ou que votre équipe grandit.
Oui, parfois il peut nous sembler un peu trop strict. Oui, il faut parfois négocier avec lui via les commentaires d'exception. Mais au final, il nous fait gagner un temps précieux en maintenance et améliore significativement la qualité de notre code CSS.
Et vous, utilisez-vous Stylelint également ? Quelles sont vos règles préférées ou celles que vous avez dû adapter pour votre équipe ? Partagez vos expériences et astuces dans les commentaires.
Commentaires
Mé euh, c'est pas du spam mon précédent message :'-\