18 minLesson 5 of 40
CSS3 & Responsive Design
CSS Selectors & Specificity
CSS Selectors & Specificity
Selectors tell CSS which elements to style. Understanding how specificity determines which rule wins when selectors conflict is one of the most important skills for writing maintainable CSS.
Selector Types
/* Universal */
* { box-sizing: border-box; }
/* Type (element) */
h1 { font-size: 2rem; }
p { line-height: 1.6; }
/* Class */
.card { border-radius: 8px; }
.btn-primary { background: blue; }
/* ID */
#nav { position: sticky; }
#hero { min-height: 100vh; }
/* Attribute */
input[type="email"] { border: 1px solid #ccc; }
a[href^="https"] { color: green; } /* starts with */
a[href$=".pdf"] { color: red; } /* ends with */
a[href*="example"] { font-weight: bold; } /* contains */
[data-theme="dark"] { background: #1a1a1a; }
Combinators
/* Descendant (space) — any level deep */
.card img { border-radius: 4px; }
/* Child (>) — direct children only */
.nav > li { display: flex; }
/* Adjacent sibling (+) — immediately after */
h2 + p { margin-top: 0; }
/* General sibling (~) — all following siblings */
h2 ~ p { color: #666; }
Pseudo-classes
/* State */
a:hover { color: blue; }
a:active { opacity: 0.8; }
a:focus { outline: 2px solid blue; }
a:visited { color: purple; }
input:focus { border-color: blue; }
input:disabled { opacity: 0.5; cursor: not-allowed; }
input:checked + label { font-weight: bold; }
/* Structural */
li:first-child { border-top: none; }
li:last-child { border-bottom: none; }
li:nth-child(2) { background: lightyellow; }
li:nth-child(even) { background: #f9f9f9; }
li:nth-child(3n+1) { color: red; } /* every 3rd starting at 1 */
li:nth-last-child(2) { /* 2nd from end */ }
p:only-child { /* only if it's the only child */ }
/* Content */
p:empty { display: none; }
p:not(.special) { color: #333; }
:is(h1, h2, h3) { line-height: 1.2; } /* matches any in list */
:where(h1, h2, h3) { line-height: 1.2; } /* same but 0 specificity */
:has(img) { border: 1px solid #ccc; } /* parent selector! */
/* Form states */
input:valid { border-color: green; }
input:invalid { border-color: red; }
input:required { border-left: 3px solid orange; }
input:placeholder-shown { font-style: italic; }
Pseudo-elements
/* Insert content before/after */
.required::before {
content: "* ";
color: red;
}
blockquote::before {
content: "\201C"; /* open quote " */
font-size: 3rem;
color: #ccc;
}
/* Style parts of text */
p::first-line { font-variant: small-caps; }
p::first-letter { font-size: 2em; float: left; }
/* Text selection */
::selection {
background: #4a90e2;
color: white;
}
/* Placeholder text */
input::placeholder { color: #aaa; font-style: italic; }
/* Scrollbar (Webkit/Blink only) */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-thumb { background: #888; border-radius: 4px; }
Specificity
When two rules target the same element, specificity determines which wins. It's calculated as a three-part score: (IDs, Classes/Attributes/Pseudoclasses, Types/Pseudoelements).
Selector IDs Classes Types → Score
────────────────────────────────────────────────────────────────
* 0 0 0 → 0-0-0
p 0 0 1 → 0-0-1
.class 0 1 0 → 0-1-0
#id 1 0 0 → 1-0-0
p.class 0 1 1 → 0-1-1
#id .class p 1 1 1 → 1-1-1
inline style="..." → 1-0-0-0 (wins!)
!important → override all
/* Lower specificity — 0-0-1 */
p { color: blue; }
/* Higher specificity — 0-1-1 — this wins */
.intro p { color: red; }
/* Same specificity — later rule wins */
.intro p { color: red; }
.content p { color: green; } /* ← wins if both match */
The Specificity Hierarchy
/* Order of precedence (highest to lowest): */
/* 1. !important (avoid — makes maintenance hell) */
.override { color: red !important; }
/* 2. Inline styles */
/* <p style="color: blue;"> */
/* 3. IDs */
#header { color: blue; }
/* 4. Classes, attributes, pseudo-classes */
.nav-link:hover { color: blue; }
[type="text"] { color: blue; }
/* 5. Elements, pseudo-elements */
p { color: blue; }
p::first-line { color: blue; }
/* 6. Universal, combinators */
* { color: blue; }
Practical Rules
/* Write selectors as low-specificity as possible */
/* Bad — hard to override */
nav ul li a.active { color: blue; }
/* Better — one class does the job */
.nav-active { color: blue; }
/* Use :is() to reduce repetition without adding specificity */
:is(h1, h2, h3, h4) a { color: inherit; }
/* CSS Layers (modern) — explicit specificity control */
@layer base, components, utilities;
@layer base {
p { color: #333; }
}
@layer utilities {
.text-red { color: red; } /* wins over base layer regardless of specificity */
}
Next lesson: The Box Model & Display Modes — how CSS lays out elements on the page.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises