This article covers CSS selectors - the part of a CSS rule that decides which elements get styled. We'll also look at how the cascade decides which rule wins when two rules conflict.
If you're new to CSS, the previous article
"What is CSS?" covers how rules are written,
the box model, colors, units, and pseudo-classes like :hover -
good to read first if you haven't already.
That said, this article is designed to be readable on its own.
Basic Selectors
A CSS selector is the part of a rule that says "apply these styles to..." Here are the selectors you'll use most of the time.
Type Selector (Element)
The simplest selector. Matches every element of a given type.
<style>
/* Apply styles to all "p" elements */
p {
color: #333;
margin-bottom: 0.5em;
}
/* Apply styles to all "div" elements */
div {
color: #226600;
}
</style>
<p>Every paragraph on the page picks up the rule above.</p>
<p>So does this one - no class or id needed.</p>
<div>Every div turns green.</div>
Type selectors are handy for setting page-wide defaults - base font size, link color, paragraph spacing - that you'll occasionally override with more specific selectors later.
Class Selector (.)
You might be wondering, "what exactly is a class?" It's actually pretty simple. In CSS, a class is one of the most common ways to apply styles, and the same class can be attached to multiple elements.
A class is a label you put on one or more HTML elements,
using the class attribute.
In CSS, you target it with a dot followed by the class name, e.g. .highlight.
You simply pick any name you like (the class name) and attach it to the elements
you want to style.
As we'll see in the next section, an id can only be used on one element per page,
but the same class name can be attached to as many elements as you want.
A rule like p { ... } applies to every <p> on the page,
but often you only want to style some of them.
That's where classes come in - they let you target just the elements you choose.
For example, if you want your own highlight style,
you can use a class to apply it to specific paragraphs only:
<style>
.highlight {
background-color: yellow;
font-weight: bold;
}
</style>
<p class="highlight">Important paragraph.</p>
<p>Regular paragraph.</p>
<p class="highlight">Another important one.</p>
Tip:
Class names can contain letters (a-z, A-Z),
digits (0-9), hyphens (-), and underscores (_).
The main thing to watch out for: don't start a class name with a digit.
For example, class="my-class-1" works as expected,
but class="1st-class" can't be selected with .1st-class in CSS
without special escaping, so it's best avoided.
Class names are also case-sensitive, so .Highlight and .highlight
are treated as different classes.
Classes are the workhorse of CSS.
Any element can have a class, the same class can be applied to many elements,
and one element can have multiple classes (just separate them with spaces:
class="highlight large").
<style>
.highlight {
background-color: yellow;
font-weight: bold;
}
.large {
font-size: 1.5em;
}
</style>
<p>Regular paragraph.</p>
<p class="highlight">Highlighted paragraph.</p>
<!-- Specify multiple classes by separating them with spaces -->
<p class="highlight large">Highlighted large paragraph.</p>
<p class="large">Large paragraph.</p>
ID Selector (#)
An ID is similar to a class, but it should only be used once per page.
IDs are written with a # in CSS:
<h1 id="page-title">Welcome</h1>
#page-title {
color: navy;
text-align: center;
}
We introduced IDs in the previous article as In-page links and the id attribute, but they're also used as CSS selectors. Since the same ID acts as both an in-page link target and a CSS selector, IDs are typically used to uniquely identify a single element on the page.
IDs are also commonly used as targets for JavaScript.
That said, in modern CSS, classes are usually preferred over IDs for styling. IDs have very high priority in the cascade, so they override class rules, which can make them harder to deal with later. And since IDs already serve as in-page link targets, using them for styling on top of that tends to cause more problems than it solves.
<style>
#page-title {
color: navy;
text-align: center;
}
.page-title {
color: red;
}
</style>
<h1 id="page-title" class="page-title">Welcome</h1>
In this example, the heading stays navy rather than turning red,
because the ID selector has higher specificity than the class selector.
If you wanted to make it red, you'd have to either change the ID rule to use a class,
or add !important to the class rule (not recommended).
Because of this, using classes is often easier to manage when you want to override styles later or combine multiple styles on the same element.
Universal Selector (*)
The asterisk matches every element on the page. It's powerful, but since the styles you write end up applying to every element, it's easy to introduce unintended side effects. Use it sparingly.
<style>
* {
color: blue;
background-color: aqua;
border: 1px solid #333;
}
</style>
<h1>Heading</h1>
<p>Paragraph.</p>
<div>A div.</div>
<button type="button">A button</button>
<a href="#">A link</a>
This makes every element on the page blue, with an aqua background and a border -
not usually what you want!
And because * also matches root elements like <html>
and <body>, it can cause the layout to break in ways you didn't expect.
That said, the universal selector can be useful in certain cases. For example, it's sometimes used as a "reset" to wipe out the browser's default margins and padding:
* {
margin: 0;
padding: 0;
}
Some developers intentionally start from this blank slate and then build their styles back up, one piece at a time.
Grouping: One Rule for Multiple Selectors
Take this example - it defines the exact same styles for h1,
h2, and h3. The repetition adds up quickly:
h1 {
font-family: "Georgia", serif;
color: #222;
}
h2 {
font-family: "Georgia", serif;
color: #222;
}
h3 {
font-family: "Georgia", serif;
color: #222;
}
When you find yourself writing the same declarations for multiple selectors, you can group them together by separating them with commas:
<style>
h1, h2, h3 {
font-family: "Georgia", serif;
color: #222;
}
</style>
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<h3>Heading 3</h3>
<p>A paragraph - not affected by the rule above.</p>
This is exactly the same as writing three separate rules with the same declarations, but much shorter.
You can also group selectors together and then override specific properties
for just some of them. For example, the following rules share a font and color
for all three headings, but only h1 gets a background color:
h1, h2, h3 {
font-family: "Georgia", serif;
color: #222;
}
/* Only h1 gets a light red background */
h1 {
background-color: #ffcccc;
}
Descendant Selector (Space)
A space between two selectors means "match the second selector, but only when it's inside the first." This is called the descendant combinator.
<style>
/* Only paragraphs inside an article */
article p {
color: #226600;
}
</style>
<article>
<p>Inside the article - this turns green.</p>
<div>
<p>Still inside the article (just nested deeper) - also green.</p>
</div>
</article>
<p>Outside the article - stays the default color.</p>
The descendant doesn't have to be a direct child - it can be nested any number of levels deep. As long as it's somewhere inside, it matches.
The descendant combinator works with any selectors, not just type selectors like
article p. For example, .card .highlight selects elements
with the highlight class that are inside an element with the
card class.
<style>
.highlight {
background-color: #9af9f0; /* default highlight color: light blue */
}
.card {
background-color: #ffdb9b; /* card background color: light orange */
border: 1px solid #ffa61f;
padding: 16px;
max-width: 400px;
}
.card .highlight { /* Descendant Combinator (" ") */
background-color: #f9f99a; /* highlight color inside a card: light yellow */
}
</style>
<div class="card">
<p>This is a paragraph in a card.</p>
<p class="highlight">
This is also highlighted, because it's inside the card, even though it's nested deeper.
</p>
</div>
<p class="highlight">
This is highlighted, but not inside the card,
so it gets the default highlight color.
</p>
Child Selector (>)
If you want to match only direct children (one level deep, no deeper),
use the > combinator.
This is called the child combinator.
<style>
.highlight {
background-color: aqua;
}
.card {
background-color: orange;
border: 1px solid darkorange;
padding: 16px;
max-width: 400px;
}
.card > .highlight { /* Child Combinator (">") */
background-color: yellow;
}
</style>
<div class="card">
<p class="highlight">A direct child of the ".card" element: yellow background.</p>
<div>
<p class="highlight">
A descendant of ".card",
but not a direct child (because of the div in between): default aqua background.
</p>
</div>
<p class="highlight">Another direct child of the ".card" element: yellow background.</p>
</div>
Compare this with the descendant selector (a space) above.
The rule .card .highlight matches every element with the
.highlight class anywhere inside an element with the .card class,
no matter how deeply nested.
On the other hand, .card > .highlight only matches elements with
the .highlight class that are direct children of an element
with the .card class.
Next-sibling Selector (+)
The + combinator matches an element that comes
immediately after another element at the same level.
This is sometimes also called the adjacent sibling selector,
which is the older name from earlier CSS specifications.
<style>
/* A paragraph that comes right after an h2 */
h2 + p {
margin-top: 0;
font-weight: bold;
}
</style>
<h2>A heading</h2>
<p>This paragraph is right after the h2, so it's bold.</p>
<p>This paragraph comes after another paragraph, so it's not bold.</p>
This is useful for things like making the first paragraph after a heading stand out, without having to add a class to it.
Combining Selectors
All of these selectors can be combined. No space between two selectors means "match an element that satisfies both."
<style>
/* A paragraph that ALSO has the class "highlight" */
p.highlight {
background-color: yellow;
}
</style>
<p class="highlight">Paragraph with .highlight - gets the yellow background.</p>
<p>Paragraph without the class - no background.</p>
<div class="highlight">A div with the same class - NOT matched, because the rule requires a p element.</div>
Tip: There are many more selector types in CSS - attribute selectors, more pseudo-classes, pseudo-elements, and so on. The ones above cover the vast majority of what you'll write day-to-day. We'll look at the more advanced ones in a later article.
The Cascade: Which Rule Wins?
CSS lets multiple rules apply to the same element at the same time. When two rules conflict (say, both try to set the color of your headings), CSS uses a set of rules called the cascade to decide which one takes precedence.
Here's a simple example. Suppose you have these two rules:
p {
color: black;
}
p.highlight {
color: red;
}
And this paragraph:
<p class="highlight">What color am I?</p>
Both rules match the element.
The first one says "all paragraphs are black."
The second says "paragraphs with class highlight are red."
When they conflict, CSS picks the one with the more specific selector.
p.highlight is more specific than just p,
so the text comes out red.
The full priority system is called specificity. Here's the rough order, from highest to lowest:
- Inline styles (the
styleattribute on an element) - ID selectors (
#id) - Class selectors (
.class), attribute selectors, and pseudo-classes (:hover) - Type selectors (
p,h1) and pseudo-elements
On top of that, if two rules have exactly the same specificity, the one that appears later in the stylesheet wins.
The !important flag
CSS provides a special flag called !important that you can add
to a declaration. A rule marked with !important takes precedence
over rules with higher specificity, and even over inline styles.
<style>
#page-title {
color: navy;
}
.page-title {
color: red !important;
}
</style>
<h1 id="page-title" class="page-title">Welcome</h1>
Normally an ID selector would win over a class selector, so the heading
would be navy. But because the class rule is marked !important,
it overrides the ID rule, and the heading comes out red.
!important is a regular part of CSS and there are valid uses for it.
Common cases include:
- Overriding styles from a third-party library or framework when you can't change the source.
- Utility classes that are intended to win no matter what (e.g. a
.hiddenclass that should always hide the element). - User stylesheets and accessibility overrides.
That said, if a regular selector can solve the problem,
it's usually easier to maintain without !important,
since a rule marked !important can only be overridden by another
!important rule with equal or higher specificity.
Reach for it when you need it, but check first whether a more specific selector
would do the same job.
Putting It All Together
Here's a small but complete example that brings together the selectors and cascade ideas covered in this article, along with the pseudo-classes from the previous article.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Styled Page</title>
<style>
/* Type selectors: page-wide defaults */
body {
font-family: Arial, sans-serif;
color: #333;
line-height: 1.6;
margin: 24px;
}
/* Grouping: one rule for all main headings */
h1, h2, h3 {
font-family: "Georgia", serif;
color: #2a6df4;
}
/* Next-sibling selector: paragraph right after an h2 */
h2 + p {
margin-top: 0;
font-weight: bold;
}
/* Class selectors: reusable building blocks */
.highlight {
background-color: #9af9f0;
padding: 2px 6px;
}
.card {
background-color: #ffdb9b;
border: 1px solid #ffa61f;
padding: 16px;
margin: 16px 0;
}
/* Descendant selector: highlight inside a card looks different */
.card .highlight {
background-color: #f9f99a;
}
/* Pseudo-classes: link states in LVHA order */
a:link { color: #2a6df4; }
a:visited { color: #6a4ec2; }
a:hover { color: #d83b3b; text-decoration: underline; }
</style>
</head>
<body>
<h1>My Styled Page</h1>
<h2>About This Page</h2>
<p>This paragraph is bold because it comes right after the h2.</p>
<p>This one is regular, since it isn't directly after the heading.</p>
<p>
You can <span class="highlight">highlight</span> inline text with a class,
and visit <a href="https://example.com/">example.com</a> through a link.
</p>
<div class="card">
<h3>Inside a Card</h3>
<p>
The same <span class="highlight">highlight</span> class looks different in here,
because ".card .highlight" overrides the default.
</p>
</div>
</body>
</html>
Notice how each selector pulls its weight here.
Type selectors (body) set the page-wide defaults.
Grouping (h1, h2, h3) shares one rule across all the headings.
The next-sibling rule (h2 + p) makes the lead paragraph stand out
without needing an extra class.
Classes (.highlight, .card) act as reusable building blocks.
The descendant rule (.card .highlight) shows the cascade in action,
overriding the default highlight color only when it appears inside a card.
And the pseudo-classes give the link its three different states.
Summary
- Basic selectors include type (
p), class (.highlight), ID (#title), and universal (*). - Grouping (
h1, h2) applies the same rule to multiple selectors at once. - Combinators let you target elements based on position: descendant (space), child (
>), and next-sibling (+). - The cascade decides which rule takes precedence when two rules conflict. More specific selectors win: inline > ID > class/pseudo-class > type.
- When specificity is equal, the rule that appears later in the stylesheet wins.
- The
!importantflag overrides specificity. It's a regular part of CSS, but if a more specific selector can do the job, that's usually easier to maintain.
Try It Yourself
Open the SyncFiddle Editor, write some HTML, and experiment with selectors.
Add a class to a few elements and style them differently from the rest.
Try a :hover rule on a link and see it change color when you mouse over it.
Write two conflicting rules for the same element and see which one wins -
then flip their order and watch the result change.