Calculateur de Spécificité CSS
Calculez la spécificité CSS de tout sélecteur. Décomposition colorée, comparaison multi-sélecteur, supporte :is, :not, :has, :where selon W3C.
| Composant | Exemples | Poids |
|---|---|---|
| a IDs | #header, #main | (1, 0, 0) |
| b Classes / Attributs / Pseudo-classes | .btn, [type="text"], :hover, :nth-child(2) | (0, 1, 0) |
| c Types / Pseudo-éléments | div, p, ::before, ::placeholder | (0, 0, 1) |
| — | *, :where(...), +, >, ~ | (0, 0, 0) |
| — | :is(A, B), :not(A), :has(A) | max de l'argument |
Calculateur de Spécificité CSS — Analyse Sélecteurs, Calcule (a, b, c)
Collez n'importe quel sélecteur CSS — simple ou composé, avec combinateurs, pseudo-classes, pseudo-éléments, ou les modernes :is/:not/:where/:has — et le calculateur renvoie le triplet de spécificité W3C. La décomposition montre à quel composant chaque token contribue, et une vue de comparaison classe plusieurs sélecteurs pour que vous voyiez d'un coup d'œil quelle règle gagne la cascade.
Qu'est-ce que la spécificité CSS en un paragraphe ?
Quand deux règles CSS essaient de fixer la même propriété sur le même élément, le navigateur a besoin d'un départage. Le premier critère est *origine et importance* (user-agent < auteur < utilisateur, puis !important inverse cela). Ensuite, *spécificité* : chaque sélecteur reçoit un poids, exprimé en trois nombres — (a, b, c) — et le tuple le plus grand gagne, comparé de gauche à droite.
Les trois slots comptent :
- **a** — nombre de sélecteurs d'ID (`#foo`)
- **b** — nombre de sélecteurs de classe (`.bar`), d'attribut (`[type=text]`), et pseudo-classes (`:hover`)
- **c** — nombre de sélecteurs de type (`div`) et pseudo-éléments (`::before`)
Un sélecteur comme `#nav .item a:hover` a un ID, deux entrées classe/pseudo-classe, et un type — spécificité (1, 2, 1). Il bat `.menu .item a:focus` (0, 3, 1) car le premier slot gagne peu importe la hauteur des suivants.
Comment `:is()`, `:not()`, et `:has()` affectent-ils la spécificité ?
Les trois contribuent par la spécificité *maximale* de leurs arguments, pas zéro, pas la somme.
Exemple :
```
:is(.btn, #primary) { color: red; }
```
La spécificité est déterminée par `#primary` (1, 0, 0), pas `.btn` (0, 1, 0). Le token entier `:is(...)` contribue (1, 0, 0).
La même règle s'applique à `:not(...)` et `:has(...)` — ils prennent le sélecteur le plus lourd dans les parenthèses. C'est pourquoi `:not(#admin)` est un sélecteur lourd malgré son air innocent ; c'est spécificité (1, 0, 0) à lui seul.
Ce comportement est défini dans CSS Selectors Level 4. L'ancien `:not()` (Level 3, forme simple-sélecteur unique) suivait la même règle, donc la plupart des navigateurs ont toujours été d'accord.
Et `:where(...)` ?
`:where(...)` est la soupape de sécurité : il contribue par **zéro spécificité** quel que soit son contenu.
```
:where(#header, .nav, h1) { color: blue; }
```
Spécificité = (0, 0, 0). C'est intentionnel. `:where()` permet d'écrire un style de base que partagent des sélecteurs groupés sans verrouiller les futurs remplacements — n'importe quoi qui cible les mêmes éléments avec ne serait-ce qu'une classe gagnera.
Un motif courant est d'envelopper les règles reset ou par défaut dans `:where(...)` pour qu'elles n'aient effectivement aucun poids de spécificité, tandis que les styles de composants authoriaux restent libres de les surcharger avec des sélecteurs normaux. Ajouté en CSS Selectors Level 4 (2018) et livré dans tous les navigateurs evergreen pour 2021.
Pourquoi je vois parfois `(a, b, c, d)` avec quatre slots ?
Les articles plus anciens ajoutent un quatrième slot au début pour le **style inline** (l'attribut `style="..."`), donnant (1, 0, 0, 0) pour toute déclaration inline. W3C l'a retiré du tuple de spécificité formel parce que le style inline est une couche d'origine séparée — il gagne toutes les décisions de cascade au niveau auteur avant même que la spécificité soit comparée.
En pratique :
1. Les déclarations `!important` gagnent (couche d'origine).
2. Le style inline bat tout sélecteur sans `!important`.
3. Les sélecteurs sont ensuite classés par (a, b, c) — IDs, classes/attrs/pseudo-classes, types/pseudo-éléments.
4. En cas d'égalité de spécificité, la règle déclarée en dernier dans l'ordre source gagne.
Ce calculateur suit la convention moderne à trois slots. Si vous comparez avec un tutoriel plus ancien, complétez mentalement par un 0 initial — `(0, 1, 2, 1)` et `(1, 2, 1)` décrivent le même sélecteur.
Le sélecteur universel `*` ou les combinateurs ajoutent-ils de la spécificité ?
Non. Rien de ce qui suit ne contribue à la spécificité :
- `*` (sélecteur universel)
- ` ` (combinateur de descendance, écrit comme espace)
- `>` (combinateur d'enfant)
- `+` (combinateur de frère adjacent)
- `~` (combinateur de frère général)
- `||` (combinateur de colonne, pour les tableaux — Level 4)
Un sélecteur comme `* > * + *` a une spécificité (0, 0, 0) — trois universels et deux combinateurs, tous sans poids. Les combinateurs décrivent des *relations*, pas ce qui est sélectionné, donc ils n'ajoutent pas de poids.
C'est pourquoi `body > main p` (0, 0, 3) est à égalité avec `h1 ~ h2 ~ p` (0, 0, 3) — mêmes trois sélecteurs de type, combinateurs différents, même spécificité. La cascade décide ensuite par ordre source.
Quelle est la différence entre pseudo-classes et pseudo-éléments ?
**Pseudo-classes** décrivent un état d'un élément :
- `:hover`, `:focus`, `:active`, `:checked`, `:disabled`
- `:nth-child(2)`, `:nth-of-type(odd)`, `:first-child`
- `:lang(en)`, `:dir(rtl)`, `:state(custom)`
Elles contribuent au slot **b** — même poids qu'un sélecteur de classe.
**Pseudo-éléments** décrivent une sous-partie virtuelle d'un élément :
- `::before`, `::after`, `::first-line`, `::first-letter`
- `::placeholder`, `::marker`, `::backdrop`, `::selection`
Ils contribuent au slot **c** — même poids qu'un sélecteur de type.
La différence syntaxique est un seul deux-points (`:hover`) vs double deux-points (`::before`). CSS2 utilisait un seul deux-points pour les quatre pseudo-éléments les plus anciens (`:before`, `:after`, `:first-line`, `:first-letter`) — ce calculateur accepte les deux formes et classe ces quatre comme pseudo-éléments dans les deux cas.
Comment réduire la spécificité quand je suis en guerre avec une autre feuille de style ?
Plusieurs techniques par sévérité croissante :
1. **Utilisez `:where()`** — enveloppez les parties lourdes de votre sélecteur dans `:where()` pour qu'elles ne contribuent rien. `:where(.legacy) .button` → (0, 1, 0) au lieu de (0, 2, 0). Idéal pour les feuilles reset.
2. **Retirez les IDs des sélecteurs** — refactorisez `#nav .item` en `.nav .item` si le markup le permet. Les IDs sont le contributeur le plus lourd.
3. **Utilisez une seule classe** — les sélecteurs plats comme `.btn-primary` gagnent car ils sont à égalité à (0, 1, 0) contre la plupart des styles authoriaux, laissant l'ordre source décider.
4. **Dupliquez une classe** — `.btn.btn` (0, 2, 0) est un hack connu pour augmenter la spécificité *sans* avoir besoin d'IDs.
5. **Utilisez les cascade layers** — `@layer base, components, utilities;` vous permet de contrôler quel fichier gagne quelle que soit la spécificité. La couche déclarée en dernier gagne, point.
6. **Utilisez `!important`** — dernier recours. Ça marche mais crée une chaîne fragile de guerres de surcharge.
La stratégie moderne est : minimiser la spécificité en CSS de composants (classes uniques), envelopper les règles legacy/reset dans `:where()`, et utiliser les cascade layers pour la séparation tooling/utility.
Cet outil est-il privé et hors-ligne ?
Oui. Tout se calcule localement dans le navigateur par quelques centaines de lignes de JavaScript :
- Le parser de sélecteurs est écrit à la main, aucun moteur CSS externe appelé.
- Aucune télémétrie, aucune analytique pour les calculs eux-mêmes.
- Le bouton Exemples remplit le textarea avec des chaînes intégrées ; rien n'est téléchargé.
- La page partage les ressources normales du site (Bootstrap CSS, icônes) mais aucune API tierce n'est contactée pour le calcul de spécificité réel.
Vous pouvez vérifier en ouvrant DevTools → Réseau et en regardant pendant que vous tapez ou cliquez sur Calculer — aucune requête ne devrait se déclencher. Cela signifie aussi que le calculateur fonctionne hors-ligne une fois la page chargée, pratique si vous auditez une feuille de style en vol ou en zone à faible signal.
Caractéristiques Clés
- Tuple (a, b, c) correct selon W3C pour tout sélecteur CSS
- Décomposition colorée mettant en évidence quel token contribue à quel slot
- Vue de comparaison multi-sélecteur, triée par spécificité décroissante
- Gestion correcte de :is(), :not(), :has() — spécificité max de l'argument
- :where(...) reconnu comme spécificité zéro
- Pseudo-éléments (::before, ::after, ::first-line) classés séparément des pseudo-classes
- Pseudo-éléments CSS2 à un seul deux-points (:before, :after) acceptés
- Sélecteur universel * et combinateurs (>, +, ~, espace) correctement pondérés à zéro
- Sélecteurs d'attribut, dont [attr=value] et [attr^=value]
- Pseudo-classes fonctionnelles :nth-child(), :nth-of-type(), :lang(), :dir()
- Preset Exemples charge 10 sélecteurs représentatifs pour comparaison instantanée
- Détection d'égalité — met en évidence les sélecteurs à spécificité égale
- JavaScript pur, aucun moteur CSS externe
- Fonctionne hors-ligne après le premier chargement
- 100% côté client — vos sélecteurs restent dans votre navigateur
