CSS Class Selectors
Class selectors are one of the most important building blocks in CSS. They’re the “apply this style to anything with this label” tool, and they scale from tiny buttons to entire design systems.
In this tutorial, you’ll learn how class selectors work, how to combine them, how they interact with the cascade and specificity, and how to avoid the classic “why is my CSS not working?!” moment.
What Is a Class Selector?
A class selector targets elements that have a specific class attribute value.
In CSS, class selectors start with a dot:
.card, .button, .is-active, etc.
-
In HTML, classes look like:
class="card" -
In CSS, you target it with:
.card { ... } -
One element can have multiple classes, separated by spaces:
class="button button-primary"
.card {
background: white;
border: 2px solid #111;
}
.card {
background: #f2f2f2;
border: 2px dashed #111;
}
.card {
background: #111;
border: 2px solid #111;
color: white;
}
*,
::before,
::after {
box-sizing: border-box;
}
.demo-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(2, minmax(0, 1fr));
font-family: system-ui, sans-serif;
}
.card {
padding: 14px;
border-radius: 12px;
}
.card h3 {
margin: 0 0 8px 0;
font-size: 16px;
}
.card p {
margin: 0;
opacity: 0.9;
line-height: 1.4;
}
Card One
I have
class="card".Card Two
Same class, same selector, same styles.
How Classes Are Applied In HTML
The class attribute is a space-separated list. That means:
the order of classes in HTML does not matter.
This:
class="button primary"
is the same as:
class="primary button".
CSS class selectors match the presence of the class name, not its position.
Multiple Classes On One Element
Multiple classes are a superpower. A common approach is:
-
A base class that provides the foundation (example:
.button) -
A variation class that changes the look (example:
.primary)
AND vs OR In Class Selectors
-
AND:
.button.primarymatches elements that have both classes. -
OR:
.button, .primarymatches elements that have either class (it is a list of selectors).
.button {
background: #f2f2f2;
border: 2px solid #111;
color: #111;
}
.primary {
background: #111;
border: 2px solid #111;
color: white;
}
.button.primary {
background: #111;
border: 2px solid #111;
color: white;
}
.button,
.primary {
background: #111;
border: 2px solid #111;
color: white;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
display: grid;
gap: 10px;
}
.row {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.button {
padding: 10px 14px;
border-radius: 999px;
display: inline-block;
font-weight: 700;
}
.note {
font-size: 14px;
opacity: 0.85;
}
class="primary"Switch snippets and observe the difference between
.button.primaryand.button, .primary.
Learn more about OR and AND selectors in the CSS OR and AND Selectors Interactive Tutorial.
Class Selectors With Descendants And Children
Class selectors become extra useful when you combine them with relationships:
-
Descendant selector:
.menu .itemtargets any.iteminside.menu, no matter how deep. -
Direct child selector:
.menu > .itemtargets only.itemelements that are direct children of.menu.
.menu .item {
background: #f2f2f2;
border-color: #111;
}
.menu > .item {
background: #f2f2f2;
border-color: #111;
}
.menu .group .item {
background: #111;
border-color: #111;
color: white;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
display: grid;
gap: 10px;
}
.menu {
border: 2px solid #111;
border-radius: 12px;
padding: 10px;
display: grid;
gap: 8px;
}
.item {
padding: 10px 12px;
border: 2px solid #bbb;
border-radius: 10px;
}
.group {
border: 2px dashed #bbb;
border-radius: 12px;
padding: 10px;
display: grid;
gap: 8px;
}
.label {
font-size: 13px;
opacity: 0.85;
margin: 0;
}
The
.itemblocks include one nested inside.group.
Learn more about the direct child > selector in the CSS Direct Child Selector
(>) Interactive Tutorial.
Class Selectors With States And Helpers
A super common pattern is using “state classes” like:
.is-open, .is-active, .is-loading.
These classes usually represent UI state.
A nice way to think about it:
base class describes what it is (example: .panel),
and a state class describes what’s happening (example: .is-open).
.panel {
display: none;
}
.panel {
display: none;
}
.panel.is-open {
display: block;
}
.panel {
display: none;
}
.panel.is-open {
display: block;
outline: 3px solid #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
display: grid;
gap: 10px;
}
.panel {
border: 2px solid #111;
border-radius: 12px;
padding: 12px;
background: #f2f2f2;
}
.panel h3 {
margin: 0 0 6px 0;
font-size: 16px;
}
.panel p {
margin: 0;
line-height: 1.4;
}
Open Panel
I have
class="panel is-open".Closed Panel
I have
class="panel"only.
Class Selectors With Pseudo-Classes
Pseudo-classes describe a state that’s not stored as a class, like hover or keyboard focus:
-
:hoverwhen the pointer is over the element -
:focus-visiblewhen focused in a keyboard-friendly way -
:not(...)to exclude matches -
:is(...)and:where(...)to group options
.pill:hover {
transform: translateY(-2px);
}
.pill:is(:hover, :focus-visible) {
transform: translateY(-2px);
outline: 3px solid #111;
}
.pill:not(.quiet) {
background: #111;
color: white;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
}
.pill {
border: 2px solid #111;
border-radius: 999px;
padding: 10px 14px;
display: inline-block;
background: #f2f2f2;
color: #111;
font-weight: 700;
transition: transform 150ms ease;
}
.quiet {
opacity: 0.7;
}
Learn more about pseudo-classes in the CSS Pseudo-Classes Interactive
Tutorial, about :not() in the CSS :not Interactive Tutorial, and about :is() and :where() in the CSS :is() and :where() Pseudo-Classes Interactive Tutorial.
Specificity: Why Some Class Styles Win
When multiple rules target the same element and property, CSS decides the winner using:
importance (like !important), then specificity, then source
order.
For beginners, the most relevant part is usually specificity:
- A selector with more class selectors is usually more specific.
- If specificity ties, the rule that appears later in the CSS wins.
.box {
border-style: solid;
}
.box {
border-style: solid;
}
.wrap .box {
border-style: dashed;
}
.box {
border-style: solid;
}
.box.special {
border-style: dotted;
}
.box {
border-style: dotted;
}
.box {
border-style: solid;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
padding: 12px;
border: 2px solid #111;
border-radius: 12px;
}
.box {
border-width: 4px;
border-color: #111;
border-radius: 12px;
padding: 14px;
background: #f2f2f2;
font-weight: 700;
}
I amclass="box special".
A Quick Specificity Cheat Sheet
-
.boxhas one class selector. -
.wrap .boxhas two class selectors. -
.box.specialalso has two class selectors (same “weight” as.wrap .box). - If specificity ties, the later rule wins.
Learn more about specificity in the CSS Specificity Interactive Tutorial.
Practical Class Naming And Organization
Beginners often ask: “How do I name classes?” There’s no single correct answer, but there are reliable patterns.
Component Classes vs Utility Classes
-
Component class: describes a reusable thing, like
.card,.modal,.navbar. -
Utility class: describes one tiny style, like
.text-center,.mt-2,.rounded.
You can mix them, but start simple: components first, then add utilities when you feel the pain of repeating the same small rules everywhere.
BEM-Style Naming (A Common Pattern)
BEM is a naming approach you’ll often see:
block__element--modifier.
Example:
card__title
and
card--featured.
.card__title {
text-transform: uppercase;
}
.card--featured {
border-width: 4px;
}
.card--featured .card__title {
text-transform: uppercase;
letter-spacing: 0.08em;
}
*,
::before,
::after {
box-sizing: border-box;
}
.card {
font-family: system-ui, sans-serif;
border: 2px solid #111;
border-radius: 14px;
padding: 14px;
background: #f2f2f2;
max-width: 520px;
}
.card__title {
margin: 0 0 8px 0;
font-size: 16px;
}
.card__body {
margin: 0;
line-height: 1.4;
}
Featured Card
Classes can represent structure (
card__title) and variation (card--featured).
A Slider Example: Tweaking Padding On A Class
Sliders are great when the value is numeric.
Here we’ll live-edit padding on a class.
.tag {
padding: 12px;
}
*,
::before,
::after {
box-sizing: border-box;
}
.wrap {
font-family: system-ui, sans-serif;
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: center;
}
.tag {
border: 2px solid #111;
border-radius: 999px;
background: #f2f2f2;
font-weight: 700;
}
Padding demo Same class Same selector
Common Class Selector Mistakes And Fixes
“My Class Selector Is Not Working”
Here are the usual suspects, in beginner-friendly order:
-
Spelling mismatch:
.primaryin CSS will not matchclass="primay"in HTML. -
Wrong selector type:
#cardtargets an id, not a class. Classes use a dot:.card. -
You targeted the parent but need the child:
.menustyles the menu, not the items inside it. Use.menu .itemwhen needed. - Specificity / cascade conflict: another rule is winning. Try inspecting the element in DevTools to see which rule is applied.
- The file isn’t loading: the CSS file link is wrong, or caching is showing an older version.
Class Names Are Case-Sensitive
In HTML, class matching is case-sensitive for CSS selectors in practice.
Treat class names like exact strings:
.Card and .card are different.
Special Characters In Class Names
Hyphens and underscores are easy:
.nav-item,
.nav_item.
If you use unusual characters (like a slash), you may need escaping in CSS.
As a beginner rule: stick to letters, numbers, hyphens, and underscores.
Best Practices For Beginners
-
Prefer meaningful class names:
.card,.button,.alertover.redor.big. - Compose with multiple classes instead of making one mega-class for every variation.
-
Keep selectors reasonably short.
If you find yourself writing
.page .main .section .card .title, pause and simplify. - When something “doesn’t work”, check spelling, then check DevTools, then check specificity.
Recap
-
Class selectors use a dot:
.thing - Multiple classes in HTML are space-separated and order doesn’t matter.
-
.a.bmeans “has both classes” (AND), while.a, .bis a selector list (OR). -
Combine classes with relationships like
.menu .itemand.menu > .item. - When rules conflict, specificity and source order decide the winner.
