CSS Selectors
What are selectors?
A CSS selector tells the browser which elements to apply styles to. Mastering selectors is one of the most important CSS skills because everything else — colors, layout, animation — depends on targeting the right elements.
Basic selectors
Element selector — targets all elements of a type:
CSS
p {
color: #333;
}Class selector — targets elements with a specific class (prefix with
.):HTML
<p class="highlight">Important text</p>CSS
.highlight {
background-color: #fef3c7;
padding: 0.5rem;
}ID selector — targets one specific element (prefix with
#):CSS
#main-title {
font-size: 2.5rem;
}Use classes for styling, not IDs. IDs should be reserved for JavaScript or anchor links.
Combining selectors
Descendant — selects elements inside another:
CSS
article p {
line-height: 1.8;
}This targets every
<p> inside an <article>, no matter how deeply nested.Direct child — uses
>:CSS
nav > a {
text-decoration: none;
}This only targets
<a> elements that are direct children of <nav>.Multiple selectors — comma-separated:
CSS
h1, h2, h3 {
font-family: Georgia, serif;
}Pseudo-classes
Pseudo-classes target elements in a specific state:
CSS
a:hover {
color: #dc2626;
}
input:focus {
outline: 2px solid #2563eb;
}
li:first-child {
font-weight: bold;
}Specificity — which rule wins?
When multiple rules target the same element, specificity decides the winner:
- Inline styles (
style="...") — highest specificity - ID selectors (
#name) — high - Class selectors (
.name) — medium - Element selectors (
p) — lowest
CSS
p { color: black; } /* specificity: 0-0-1 */
.intro { color: blue; } /* specificity: 0-1-0 — wins over element */
#hero { color: red; } /* specificity: 1-0-0 — wins over class */When specificity is equal, the last rule in the stylesheet wins. This is the "cascading" part of CSS.
Attribute and pseudo-element selectors
Target elements by attribute:
CSS
input[type="email"] { border-color: #2563eb; }
a[href^="https://"] { /* external links */ }Pseudo-elements style specific parts of an element:
CSS
p::first-line { font-weight: 600; }
blockquote::before { content: "\201C"; }Use
:: (double colon) for pseudo-elements and : (single colon) for pseudo-classes in modern CSS.The :is() and :where() helpers
Group complex selectors without repeating specificity surprises:
CSS
:is(h1, h2, h3) { line-height: 1.2; }
:where(h1, h2, h3) {
margin-top: 0; /* zero specificity — easy for utilities to override */
}:where() is ideal for reset/normalize rules you expect components to override.Practical selector strategy
- Prefer classes for styling hooks (
.card,.btn-primary). - Use element selectors for base typography (
body,h1,a). - Reserve IDs for unique anchors or JavaScript, not everyday styling.
- Avoid deep chains like
div div div .title— they break when markup changes.
!important, step back and check whether a more specific selector or a better class name solves the conflict.Matching real-world markup
In production sites, you rarely style bare
p tags globally. Instead, you combine element and class selectors: article p.lead targets paragraphs with class lead inside articles only. Sibling combinators (+ and ~) let you style elements relative to neighbors — handy for form labels, error messages, and stacked headings. The universal selector * exists but should be used sparingly in long chains because it matches every element and can slow style recalculation.Learning to read selectors from right to left helps debugging: in
nav ul li a, the rule applies to a elements that are descendants of li, inside ul, inside nav. If a link is not styled, walk that chain in DevTools and confirm each ancestor matches. When two rules tie on specificity, source order wins — resets and base styles belong at the top of your file, overrides later. Organizing selectors from low to high specificity reduces surprise overrides without resorting to !important.Key takeaway
Use class selectors for almost everything. Learn how specificity works so you don't end up fighting your own CSS with
!important hacks.