Les types de sélecteurs
Un sélecteur CSS désigne les éléments HTML à styliser. Il en existe trois principaux :
- Sélecteur d'élément : cible toutes les balises d'un type :
h1,p,a - Sélecteur de classe : cible les éléments avec une classe donnée :
.card,.btn-primary - Sélecteur d'id : cible un élément unique :
#header,#menu
En pratique, on utilise surtout les classes (90% du temps). Les id sont réservés aux cas uniques.
Les combinateurs
Les combinateurs permettent de cibler des éléments selon leur position dans l'arbre HTML :
div p: descendant : tous les<p>à l'intérieur d'un<div>, même imbriquésdiv > p: enfant direct : seulement les<p>directement enfants du<div>h2 + p: frère adjacent : le<p>juste après un<h2>
Les états et les fantômes : pseudo-classes et pseudo-éléments
Les sélecteurs que vous connaissez ciblent des éléments selon ce qu'ils sont. Les pseudo-classes ciblent des éléments selon ce qu'ils font, leur état au moment précis où le navigateur applique le style.
Les pseudo-classes : l'état d'un élément
Une pseudo-classe s'écrit avec un seul deux-points (:) après le sélecteur :
a:hover: le lien quand la souris est dessusinput:focus: le champ sélectionné au clavier ou au clictr:nth-child(odd): une ligne sur deux dans un tableau (zébrage)li:first-child/li:last-child: premier ou dernier élément de la liste
✦ Accessibilité clavier : Ne retirez jamais l'outline de :focus sans le remplacer par un style visible. outline: none seul rend le site inutilisable au clavier (personnes en situation de handicap, utilisateurs avancés). Remplacez-le par quelque chose de clair : outline: 2px solid #2b6cb0; outline-offset: 2px.
Les pseudo-éléments : des fantômes créés par CSS
Un pseudo-élément s'écrit avec deux deux-points (::). Il crée un élément virtuel que vous ne tapez pas dans le HTML : le navigateur l'insère pour vous.
a::before: insère quelque chose avant le contenu du liena::after: insère quelque chose après
La propriété content est obligatoire pour qu'ils apparaissent :
/* Le ► qui précède chaque lien de navigation */
.nav-link::before {
content: "► ";
color: #2b6cb0;
}
/* Un badge "Nouveau" inséré via CSS */
.card--new::after {
content: "Nouveau";
background: #329e5a;
color: #fff;
font-size: 0.75rem;
padding: 2px 6px;
border-radius: 4px;
margin-left: 8px;
}
Vous avez déjà croisé *::before dans la leçon sur le box model. C'est le même mécanisme : on cible le pseudo-élément de chaque élément de la page (* = tout) pour réinitialiser le box-sizing globalement.
💡 Une lettre d'histoire : La notation avec un seul : (comme :before) vient de CSS 2. CSS 3 a introduit le double :: pour distinguer pseudo-classes (un seul :) et pseudo-éléments (deux ::). Les navigateurs acceptent encore :before, mais écrivez toujours ::before.
Dernier point avant la prochaine section : une pseudo-classe compte comme une classe dans le calcul de spécificité. a:hover poids 0-1-1, exactement comme a.active.
Le « Cascading » : la cascade
Le C de CSS signifie Cascading (cascade). Quand plusieurs règles ciblent le même élément, le navigateur les applique dans un ordre de priorité, comme une cascade d'eau où chaque couche recouvre la précédente :
- 1. Styles du navigateur : les styles par défaut (les titres sont gros, les liens sont bleus)
- 2. Votre CSS : vos règles écrasent les styles par défaut
- 3. Ordre d'apparition : si deux règles identiques, la dernière gagne
- 4. Spécificité : une règle plus spécifique l'emporte (voir ci-dessous)
C'est pour ça que l'ordre de vos fichiers CSS compte, et que mettre un style en bas du fichier peut « écraser » celui du haut.
La spécificité
Avant de dérouler : Ces deux règles s'appliquent au même lien (#menu a { color: blue; } et .lien-rouge { color: red; }), et .lien-rouge est écrite après dans le fichier. De quelle couleur sera le lien, et pourquoi l'ordre d'écriture ne suffit-il pas à trancher ?
Voir la réponse
Le lien sera bleu. Ce n'est pas l'ordre d'écriture qui tranche ici, mais la spécificité : #menu a contient un ID, ce qui lui donne un poids bien plus élevé que la classe .lien-rouge. La hiérarchie : style en ligne > ID > classe > élément. L'ordre du fichier ne départage que les règles de spécificité égale ; ici les spécificités sont différentes, donc #menu a gagne, peu importe où il est écrit. C'est pour ça qu'on évite les ID en CSS : ils sont très difficiles à surcharger.
La spécificité est le système de points qui décide quelle règle gagne quand plusieurs ciblent le même élément :
- Sélecteur d'élément (
p) : spécificité faible - Classe (
.card) : spécificité moyenne - Id (
#header) : spécificité forte - Inline (
style="...") : spécificité très forte !important: écrase tout (mais c'est une mauvaise pratique)
Règle d'or : Ne jamais utiliser !important sauf cas de force majeure. Si vous en avez besoin, c'est que la structure de vos sélecteurs est à revoir.
Propriétés courantes
color: couleur du textebackground-color: couleur de fondfont-family: police de caractèresfont-size: taille du textefont-weight: graisse (normal,bold,600...)text-align: alignement (left,center,right)text-decoration: soulignement, barré... (none,underline)
Les variables CSS : définir une valeur une fois
Imaginez que votre charte graphique utilise un bleu partout : dans les boutons, les titres, les bordures des cartes. Vous avez écrit #2b6cb0 quarante fois dans votre fichier. Le client rappelle : il veut du violet. Vous passez une heure à chercher-remplacer et vous en ratez trois. Ce scénario, les variables CSS (propriétés personnalisées) l'éliminent.
Une variable CSS se déclare avec deux tirets devant son nom, le plus souvent sur le sélecteur :root qui couvre tout le document :
:root {
--primary: #2b6cb0;
--gap: 16px;
}
/* On lit la valeur avec var() */
.btn {
background-color: var(--primary);
padding: var(--gap);
}
.card {
border: 2px solid var(--primary);
margin-bottom: var(--gap);
}
/* Redéfinition locale : une carte "danger" a sa propre couleur */
.card--danger {
--primary: #c0392b; /* écrase --primary pour ce sélecteur et ses descendants */
border-color: var(--primary);
}
On peut aussi fournir une valeur de repli : var(--ma-couleur, black) utilise black si --ma-couleur n'est pas définie. Pratique pour les composants réutilisables dans des contextes variés.
Ce qui rend les variables CSS plus puissantes que les variables Sass ou Less, c'est qu'elles vivent dans le navigateur, à l'exécution. Elles suivent la cascade et l'héritage : déclarées sur :root elles valent partout, mais on peut les redéfinir sur n'importe quel sélecteur, dans une media query ou même via JavaScript. Un préprocesseur Sass calcule ses variables à la compilation et produit du CSS statique ; une variable CSS peut changer en direct, sans recompiler.
✦ Bonne pratique : Centralisez toutes vos couleurs de marque, tailles de police récurrentes et espacements sur :root sous forme de variables CSS. Pour changer de thème, vous n'aurez qu'à modifier quelques lignes dans ce bloc, au lieu de traquer chaque occurrence dans vos règles.
Types of selectors
A CSS selector designates which HTML elements to style. There are three main types:
- Element selector — targets all tags of a type:
h1,p,a - Class selector — targets elements with a given class:
.card,.btn-primary - ID selector — targets a unique element:
#header,#menu
In practice, you'll use classes 90% of the time. IDs are reserved for unique cases.
Combinators
Combinators let you target elements based on their position in the HTML tree:
div p— descendant: all<p>inside a<div>, even nesteddiv > p— direct child: only<p>directly inside the<div>h2 + p— adjacent sibling: the<p>right after an<h2>
States and ghosts: pseudo-classes and pseudo-elements
The selectors you know target elements based on what they are. Pseudo-classes target elements based on what they are doing, their state at the exact moment the browser applies the style.
Pseudo-classes: an element's state
A pseudo-class is written with a single colon (:) after the selector:
a:hover— the link while the mouse is over itinput:focus— the field selected by keyboard or clicktr:nth-child(odd)— every other row in a table (striping)li:first-child/li:last-child— first or last item in a list
✦ Keyboard accessibility: Never remove the :focus outline without replacing it with a visible style. outline: none alone makes the site unusable by keyboard (people with disabilities, power users). Replace it with something clear: outline: 2px solid #2b6cb0; outline-offset: 2px.
Pseudo-elements: ghosts created by CSS
A pseudo-element is written with two colons (::). It creates a virtual element you don't type in the HTML — the browser inserts it for you.
a::before— inserts something before the link's contenta::after— inserts something after
The content property is required for them to appear:
/* The ► in front of every nav link */
.nav-link::before {
content: "► ";
color: #2b6cb0;
}
/* A "New" badge inserted via CSS */
.card--new::after {
content: "New";
background: #329e5a;
color: #fff;
font-size: 0.75rem;
padding: 2px 6px;
border-radius: 4px;
margin-left: 8px;
}
You already saw *::before in the box model lesson. Same mechanism: it targets the pseudo-element of every element on the page (* = all) to reset box-sizing globally.
💡 A bit of history: The single-colon notation (like :before) comes from CSS 2. CSS 3 introduced the double :: to distinguish pseudo-classes (one colon) from pseudo-elements (two colons). Browsers still accept :before, but always write ::before.
One last point before the next section: a pseudo-class counts as a class in the specificity calculation. a:hover weighs 0-1-1, exactly like a.active.
The "Cascading" — the cascade
The C in CSS stands for Cascading. When multiple rules target the same element, the browser applies them in a priority order, like layers of water where each one overrides the previous:
- 1. Browser defaults — built-in styles (headings are big, links are blue)
- 2. Your CSS — your rules override defaults
- 3. Source order — if two identical rules exist, the last one wins
- 4. Specificity — a more specific rule wins (see below)
That's why the order of your CSS files matters, and why a style at the bottom of your file can "override" one at the top.
Specificity
Before you scroll down: Two rules target the same link — #menu a { color: blue; } and .lien-rouge { color: red; } — and .lien-rouge appears later in the file. What color will the link be, and why doesn't source order settle it?
See the answer
The link will be blue. Source order doesn't settle it here — specificity does. #menu a contains an ID, which carries far more weight than the class .lien-rouge. The hierarchy: inline style > ID > class > element. Source order only breaks ties between rules of equal specificity; here the specificities differ, so #menu a wins regardless of where it appears in the file. That's why ID selectors are avoided in CSS: they are very hard to override.
Specificity is the point system that decides which rule wins when multiple target the same element:
- Element selector (
p) — low specificity - Class (
.card) — medium specificity - ID (
#header) — high specificity - Inline (
style="...") — very high specificity !important— overrides everything (but it's a bad practice)
Golden rule: Never use !important unless absolutely necessary. If you need it, your selector structure needs rethinking.
Common properties
color— text colorbackground-color— background colorfont-family— font familyfont-size— text sizefont-weight— weight (normal,bold,600...)text-align— alignment (left,center,right)text-decoration— underline, strikethrough... (none,underline)
CSS variables: define a value once
Imagine your design system uses one blue everywhere: buttons, headings, card borders. You've written #2b6cb0 forty times across your file. The client calls: they want purple now. You spend an hour doing find-and-replace and miss three occurrences. CSS variables (custom properties) kill that scenario entirely.
A CSS variable is declared with two dashes before its name, most often on the :root selector, which covers the entire document:
:root {
--primary: #2b6cb0;
--gap: 16px;
}
/* Read the value with var() */
.btn {
background-color: var(--primary);
padding: var(--gap);
}
.card {
border: 2px solid var(--primary);
margin-bottom: var(--gap);
}
/* Local override: a "danger" card has its own color */
.card--danger {
--primary: #c0392b; /* overrides --primary for this selector and its descendants */
border-color: var(--primary);
}
You can also provide a fallback value: var(--my-color, black) uses black if --my-color is not defined. Handy for reusable components dropped into varied contexts.
What makes CSS variables more powerful than Sass or Less variables is that they live in the browser, at runtime. They follow the cascade and inheritance: declared on :root they apply everywhere, but you can redefine them on any selector, inside a media query, or even via JavaScript. A Sass preprocessor computes its variables at compile time and outputs static CSS; a CSS variable can change on the fly, no recompilation needed.
✦ Best practice: Centralise all your brand colors, recurring font sizes and spacing values on :root as CSS variables. To switch themes, you only need to update a handful of lines in that one block, rather than hunting down every occurrence across your rules.
🎯 Pratique
S'entraîner (clique pour ouvrir) :
✨ Prompt IA
Copiez ce prompt dans Claude ou ChatGPT :
Donne-moi 5 exemples de sélecteurs CSS du plus simple au plus complexe, avec une explication de la spécificité de chacun et un cas d'utilisation concret.
💬 Ré-explique sans regarder
Sans relire la réponse de l'IA : avec tes mots, comment décides-tu lequel de .menu a et #nav a l'emporte ?
#nav a vaut 1 id + 1 élément, .menu a vaut 1 classe + 1 élément. On compare colonne par colonne de gauche à droite : la colonne id (1 contre 0) tranche tout de suite, donc #nav a gagne, quel que soit l'ordre dans le fichier. Un id pèse plus lourd que n'importe quel nombre de classes.L'IA a abusé de !important partout. Corrigez la spécificité en utilisant des classes au lieu de !important.
⚖️ Juge le code de l'IA
Tu demandes à l'IA de styliser le lien actif du menu. Voici sa réponse. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.
#sidebar nav ul.menu li.item a.link.active {
color: red;
}
!important qu'on cherche justement à éviter. Et il casse dès que tu déplaces le menu hors du #sidebar. Le réflexe pro : une seule classe ciblée, par exemple .menu-link.active, suffit et reste maintenable.🧠 Rappel libre
Sans remonter dans la leçon : que ciblent respectivement .card, #card et card, et lequel des trois a la spécificité la plus forte ?
.card (avec un point) cible tous les éléments ayant la classe card ; #card (avec un dièse) cible l'unique élément dont l'id vaut card ; card (sans rien) cible une balise <card>, qui n'existe pas en HTML standard. C'est #card qui a la spécificité la plus forte : un id l'emporte sur une classe, qui l'emporte sur un élément..btn uniquement quand il est sélectionné au clavier. Quel sélecteur utiliser ?div > p ?!important ?Vous savez maintenant cibler n'importe quel élément. Reste à comprendre comment il occupe l'espace. Dans la prochaine leçon, on décortique le box model : contenu, padding, bordure et margin, ces quatre couches qui expliquent pourquoi un bloc prend la taille qu'il prend.
Leçon 3 : Le box model →