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.primary matches elements that have both classes.
  • OR: .button, .primary matches 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="button" class="button primary" class="primary"

Switch snippets and observe the difference between .button.primary and .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 .item targets any .item inside .menu, no matter how deep.
  • Direct child selector: .menu > .item targets only .item elements 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 .item blocks 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:

  • :hover when the pointer is over the element
  • :focus-visible when 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 am class="box special".

A Quick Specificity Cheat Sheet

  • .box has one class selector.
  • .wrap .box has two class selectors.
  • .box.special also 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;
}
  

  

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:

  1. Spelling mismatch: .primary in CSS will not match class="primay" in HTML.
  2. Wrong selector type: #card targets an id, not a class. Classes use a dot: .card.
  3. You targeted the parent but need the child: .menu styles the menu, not the items inside it. Use .menu .item when needed.
  4. Specificity / cascade conflict: another rule is winning. Try inspecting the element in DevTools to see which rule is applied.
  5. 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, .alert over .red or .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.b means “has both classes” (AND), while .a, .b is a selector list (OR).
  • Combine classes with relationships like .menu .item and .menu > .item.
  • When rules conflict, specificity and source order decide the winner.