CSS Selectors
Learn how to target exactly the right HTML elements with powerful CSS selectors.
1. Introduction
A CSS selector is the part of a CSS rule that says "which HTML element should this style apply to?" Getting selectors right is one of the most important skills in CSS.
Imagine you have a page with 10 paragraphs and you only want to make one of them red. A selector lets you say "style this specific paragraph, not all of them". Without selectors you could only style every paragraph the same way.
CSS has many types of selectors — from very simple ones (target all <h1> elements) to very precise ones (target only the third list item inside a specific <nav>). In this lesson we cover all the essential selectors you will use every day.
2. Theory
Type (Element) Selector
Targets all elements of a given type.
p { color: grey; } /* all paragraphs */
h1 { font-size: 2rem; } /* all h1 headings */
a { color: blue; } /* all links */
Class Selector
Targets elements with a specific class attribute. Uses a dot . prefix. You can apply one class to many elements, and one element can have many classes.
/* CSS */
.btn { padding: 10px 20px; }
.btn-primary { background: blue; color: white; }
/* HTML */
<button class="btn btn-primary">Save</button>
ID Selector
Targets one specific element with a matching id. Uses a hash # prefix. IDs must be unique — only one element per page can have a given ID.
/* CSS */
#main-header { background: #1e293b; }
/* HTML */
<header id="main-header">...</header>
Universal Selector
Targets every element on the page. Uses *.
* {
box-sizing: border-box; /* a very common use case */
margin: 0;
padding: 0;
}
Attribute Selector
Targets elements with a specific attribute or attribute value.
a[target="_blank"] { color: orange; } /* links that open new tab */
input[type="email"] { border: 2px solid blue; } /* email inputs */
[disabled] { opacity: 0.5; } /* any disabled element */
Pseudo-class Selectors
Target elements in a specific state. Use a single colon :.
| Pseudo-class | Meaning |
|---|---|
:hover | User's mouse is over the element |
:focus | Element has keyboard or click focus |
:active | Element is being clicked |
:visited | Link has been visited |
:first-child | First child of its parent |
:last-child | Last child of its parent |
:nth-child(n) | The nth child (e.g. :nth-child(2)) |
:not(selector) | Any element that does NOT match |
a:hover { text-decoration: underline; }
li:first-child { font-weight: bold; }
li:nth-child(odd) { background: #f1f5f9; } /* zebra striping */
Pseudo-element Selectors
Target a specific part of an element. Use double colon ::.
p::first-line { font-weight: bold; } /* first line of paragraph */
p::first-letter { font-size: 2em; } /* drop cap effect */
.card::before { content: "★ "; color: gold; } /* insert content before */
.card::after { content: " ✓"; color: green; } /* insert content after */
Combinator Selectors
Target elements based on their relationship to other elements.
| Combinator | Syntax | Meaning |
|---|---|---|
| Descendant | A B | B anywhere inside A |
| Child | A > B | B that is a direct child of A |
| Adjacent sibling | A + B | B immediately after A |
| General sibling | A ~ B | All B siblings after A |
nav a { color: white; } /* all links inside nav */
nav > ul { list-style: none; } /* ul that is direct child of nav */
h2 + p { margin-top: 0; } /* paragraph right after an h2 */
Grouping Selectors
Apply the same styles to multiple selectors by separating them with commas.
h1, h2, h3 { font-family: Georgia, serif; }
.btn, .link { cursor: pointer; }
Specificity
When two rules target the same element, the more specific one wins. Specificity is calculated as:
| Selector Type | Specificity Points |
|---|---|
| Inline style | 1000 |
| ID selector | 100 |
| Class / pseudo-class / attribute | 10 |
| Type / pseudo-element | 1 |
| Universal (*) | 0 |
#header h1 { color: red; } /* specificity: 100 + 1 = 101 */
.title { color: blue; } /* specificity: 10 */
/* #header h1 wins */
3. Real World Example
On a real website, selectors are used constantly:
.nav-link:hover— changes colour when you hover over a navigation linkinput:focus— shows a blue outline when a form field is active.card:nth-child(odd)— alternating background colours in a listbutton[disabled]— greyed-out button when the form is incomplete.modal > .modal-content— only the direct child content div inside a modal, not all nested divs
4. Code Example
/* === SELECTOR SHOWCASE === */
/* Type selector */
body {
font-family: Arial, sans-serif;
background: #f8fafc;
}
/* Class selector */
.card {
background: white;
border-radius: 8px;
padding: 24px;
margin-bottom: 16px;
box-shadow: 0 1px 4px rgba(0,0,0,0.1);
}
/* ID selector */
#hero {
background: #2563eb;
color: white;
padding: 80px 40px;
text-align: center;
}
/* Pseudo-class: hover */
.btn:hover {
background: #1d4ed8;
transform: translateY(-1px);
}
/* Pseudo-class: focus (for accessibility) */
a:focus,
button:focus {
outline: 3px solid #f59e0b;
outline-offset: 2px;
}
/* Descendant combinator */
.nav ul {
list-style: none;
display: flex;
gap: 16px;
}
/* Child combinator */
.dropdown > .dropdown-menu {
display: none;
}
.dropdown:hover > .dropdown-menu {
display: block;
}
/* Attribute selector */
a[href^="https"] {
/* links starting with https */
color: green;
}
/* Grouping */
h1, h2, h3, h4 {
color: #0f172a;
line-height: 1.2;
}
/* Nth-child: zebra table rows */
tr:nth-child(even) {
background: #f1f5f9;
}
5. Code Breakdown
box-shadow: 0 1px 4px rgba(0,0,0,0.1);- Adds a soft shadow under the card.
0= no horizontal offset,1px= 1px down,4px= blur radius,rgba(0,0,0,0.1)= black at 10% opacity. transform: translateY(-1px);- Moves the element 1px upward when hovered — a subtle "lift" effect on buttons. The
transformproperty moves/rotates/scales elements without affecting layout. a:focus, button:focus- Comma grouping — applies to both
aandbuttonwhen focused. This is essential for keyboard accessibility. a[href^="https"]- Attribute selector with
^=which means "starts with". Targets only links whose href begins withhttps. Other operators:$=(ends with),*=(contains). .dropdown > .dropdown-menu- Child combinator — only the
.dropdown-menuthat is a direct child of.dropdown. Safer than descendant if menus are nested.
6. Common Mistakes
-
Confusing class and ID. Class uses
., ID uses#. Using the wrong prefix is very common./* Wrong: using # for a class */ #btn { background: blue; } /* Correct: class selector for class="btn" */ .btn { background: blue; } - Overusing ID selectors. IDs have very high specificity (100 points), making them hard to override. Prefer class selectors for styling.
-
Not understanding the descendant selector.
div ptargets ALL paragraphs inside ANY div — even deeply nested ones. Usediv > pif you only want direct children. -
Forgetting
::beforeand::afterneedcontent:. These pseudo-elements do not display at all unless you includecontent: "";(even an empty string). -
Using
*selector everywhere. The universal selector affects all elements and can be a performance concern and cause unexpected overrides. Use it sparingly.
7. Best Practices
- Prefer class selectors for styling — they are reusable and have manageable specificity.
- Keep specificity low — avoid chaining many selectors like
div#sidebar ul li a span. It becomes very hard to override. - Use meaningful class names —
.nav-link,.card-title,.btn-primarydescribe purpose, not appearance. - Never use
!importantunless absolutely forced to. It overrides all specificity and creates hard-to-debug CSS wars. - Style pseudo-classes for accessibility — always style
:focusso keyboard users can see where they are. - Use pseudo-elements for decorative content —
::beforeand::afterare great for icons, decorations that should not be in the HTML.
8. Practice Exercise
Create selectors.html and selectors.css. In the HTML, create:
- A
<nav>with an unordered list of 4 links - Three
<div class="card">elements each with a heading and paragraph - A button with
id="submit-btn"
In CSS, write rules using:
- A type selector to style the
body - A class selector to style
.card - An ID selector to style
#submit-btn - A descendant selector to style links inside
nav - A
:hoverpseudo-class on the button - A
:nth-child(odd)to give alternating card backgrounds
9. Assignment
Build a styled navigation menu. Requirements:
- An HTML
<nav>element containing a<ul>with 5<li>items, each containing an<a>link - Style the nav with a dark background using a type selector
- Use descendant selectors to remove bullet points and set link colour to white
- Add a
:hoverstyle that changes the link background colour - Mark the current page link with
class="active"and style it differently - Add a
:focusstyle for keyboard accessibility
10. Interview Questions
Q1: What is the difference between a class and an ID in CSS?
Answer: A class (.) can be used on multiple elements and one element can have multiple classes. An ID (#) must be unique per page — only one element should have a given ID. IDs have much higher specificity (100) than classes (10), making them harder to override.
Q2: What is CSS specificity?
Answer: Specificity is the algorithm browsers use to decide which CSS rule applies when two rules conflict. It is calculated based on the types of selectors used: inline styles (1000), IDs (100), classes/pseudo-classes/attributes (10), types/pseudo-elements (1). The rule with the higher specificity wins.
Q3: What is the difference between a descendant selector and a child selector?
Answer: A descendant selector (A B — space) matches B anywhere inside A, no matter how deeply nested. A child selector (A > B) only matches B elements that are direct children of A, one level deep.
Q4: What is a pseudo-class? Give three examples.
Answer: A pseudo-class selects an element in a specific state. Examples: :hover (mouse over the element), :focus (element has focus), :nth-child(n) (element is the nth child of its parent).
Q5: How do you target every other table row?
Answer: Use tr:nth-child(even) or tr:nth-child(odd) to style alternating rows — commonly called "zebra striping" to improve table readability.
11. Additional Resources
- MDN Web Docs — CSS Selectors (search "MDN CSS selectors")
- CSS Specificity Calculator — specificity.keegan.st
- CSS Diner — flukeout.github.io (interactive selector practice game)
- MDN — Pseudo-classes reference (search "MDN CSS pseudo-classes")
- MDN — Pseudo-elements reference (search "MDN CSS pseudo-elements")