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.
Deux de mes règles sont à égalité de spécificité — laquelle gagne vraiment ?
C'est le bug CSS le plus courant en pratique : deux sélecteurs ciblent le même élément avec le triplet de spécificité *identique*, p. ex. `.card .title` et `.panel .heading` tous deux à (0, 2, 0). La spécificité seule ne départage pas, alors le navigateur passe à l'étape suivante de la cascade.
L'ordre de résolution, une fois la spécificité égale, est :
1. **Couches de cascade** (`@layer`) — une déclaration dans une couche déclarée plus tard l'emporte sur une couche antérieure, et les styles sans couche battent ceux avec couche. Cela annule entièrement la spécificité au sein de l'origine auteur.
2. **Ordre source** — si les deux règles sont dans la même couche (ou sans couches), la règle déclarée en *dernier* l'emporte. La plus tardive gagne.
Voilà pourquoi le tableau de comparaison de ce calculateur inclut désormais une colonne **Gagnant de la Cascade** : collez vos sélecteurs à égalité dans l'ordre où ils apparaissent dans votre feuille de style, et l'outil marque le groupe à spécificité égale et signale le gagnant selon l'ordre source (la dernière ligne). Ainsi, si `.card .title` est à la ligne 12 et `.panel .heading` à la ligne 30, la ligne 30 gagne — l'outil le dit explicitement au lieu de vous laisser mémoriser la règle.
Notez que l'outil modélise uniquement le départage par *ordre source* ; il n'analyse pas `@layer` ni `!important`, qui se situent au-dessus de l'ordre source dans la cascade. Si vos règles sont dans des couches différentes, l'ordre des couches décide quelle que soit la ligne la plus tardive.

Comment battre une déclaration de style inline `style="..."` ?
Un attribut de style inline surpasse *toute* règle basée sur un sélecteur qui n'utilise pas `!important`, quelle que soit sa spécificité. `style="color:red"` bat même `#a#b#c.d.e div { color: blue; }` car le style inline occupe un slot plus haut de la cascade que la spécificité ordinaire des sélecteurs.
Vos options, par ordre de préférence :
1. **Retirer le style inline** — la solution propre. Les styles inline sont souvent injectés par un script ou un template hérité ; déplacez la valeur dans une règle de feuille de style.
2. **Utiliser `!important`** dans une règle de feuille de style — une déclaration `!important` d'auteur bat un style inline qui n'est *pas* lui-même `!important`. C'est le seul usage légitime de `!important` auquel beaucoup d'ingénieurs recourent ici.
3. **Un `!important` inline** (`style="color:red !important"`) bat un `!important` de feuille de style — et là vous revenez à éditer le balisage.
La spécificité n'entre jamais dans ce combat : vous ne pouvez pas surpasser un style inline avec des sélecteurs seuls. Ce calculateur note les sélecteurs, donc les styles inline sont hors champ par conception — mais savoir qu'ils gagnent à ce niveau est essentiel pour déboguer pourquoi une règle ne s'applique pas.
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
