Difficulty: Beginner
Class and ID selectors are the two most commonly used CSS selectors for targeting specific elements. While both let you apply styles to particular elements, they serve different purposes and have different rules. Understanding when to use each one - and following good naming conventions - is a foundational CSS skill.
Class selectors, written with a dot prefix (.classname), are the workhorse of CSS styling. An element can have multiple classes (class="card featured large"), and multiple elements can share the same class. This makes classes perfect for reusable styling patterns. You can chain class selectors to target elements with multiple specific classes: .card.featured targets elements that have both the 'card' AND 'featured' classes. You can also combine class selectors with element selectors: p.intro targets only <p> elements with the class 'intro'.
ID selectors, written with a hash prefix (#idname), target a single unique element on the page. Each ID must appear only once per document. IDs have much higher specificity than classes (0,1,0,0 vs 0,0,1,0), which means ID-based styles are harder to override. For this reason, most CSS methodologies recommend avoiding ID selectors for styling and reserving IDs for JavaScript targeting (document.getElementById), fragment navigation (href="#section"), and form label association (for="inputId"). Use classes for all styling purposes.
Naming conventions make your CSS readable and maintainable. The most popular convention is BEM (Block, Element, Modifier): .block__element--modifier. A 'block' is a standalone component (.card), an 'element' is a part of the block (.card__title), and a 'modifier' is a variation (.card--featured). Other approaches include using simple descriptive names (.sidebar-nav), utility-first naming (.text-center, .mt-4), or camelCase (.mainHeader). Whatever convention you choose, be consistent throughout your project.
When deciding between class and ID, almost always choose class. Classes are more flexible (reusable, lower specificity, multiple per element) while IDs are rigid (unique, high specificity, one per element). The only situations where ID selectors make sense in CSS are when you genuinely need the higher specificity to override other styles, or when targeting a truly unique page element like a skip-navigation link. In modern CSS, even these cases are better handled with multiple chained classes.
/* Single class selector */
.card {
background: white;
border-radius: 8px;
padding: 24px;
border: 1px solid #e5e7eb;
}
/* Element can have multiple classes */
/* <div class="card featured"> */
.featured {
border-color: #3b82f6;
border-width: 2px;
}
/* Chained class: BOTH classes must be present */
.card.featured {
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}
/* Combined with element selector */
p.error {
color: #dc2626;
font-weight: 600;
}
span.error {
color: #dc2626;
font-size: 12px;
}
/* Multiple elements share a class */
/* <h2 class="section-title">...</h2>
<h3 class="section-title">...</h3> */
.section-title {
color: #1f2937;
margin-bottom: 16px;
font-weight: 700;
}
Classes are reusable across elements. Chain selectors (.card.featured) to target elements with multiple classes. Combine with element selectors (p.error) for type-specific styling.
/* ID selector: very high specificity */
#main-nav {
background: #1e293b;
padding: 12px 24px;
display: flex;
}
/* Problem: ID is hard to override */
#main-nav { color: white; } /* 0,1,0,0 */
.nav-light { color: #333; } /* 0,0,1,0 - loses! */
.nav-light.themed { color: #333; } /* 0,0,2,0 - still loses! */
/* You'd need another ID or !important to override */
#main-nav.nav-light { color: #333; } /* 0,1,1,0 - finally wins */
/* BETTER: Use classes instead */
.main-nav {
background: #1e293b;
padding: 12px 24px;
display: flex;
color: white;
}
.main-nav.light {
background: white;
color: #333; /* Easy to override with classes */
}
ID selectors have specificity of (0,1,0,0) which beats any number of classes (0,0,x,0). This makes them hard to override and inflexible. Use classes for styling and reserve IDs for JavaScript and accessibility.
/* Block: standalone component */
.search-form {
display: flex;
gap: 8px;
padding: 16px;
background: #f9fafb;
border-radius: 8px;
}
/* Element: part of the block (double underscore) */
.search-form__input {
flex: 1;
padding: 8px 12px;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 14px;
}
.search-form__button {
padding: 8px 20px;
background: #2563eb;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* Modifier: variation of block or element (double hyphen) */
.search-form--large {
padding: 24px;
}
.search-form__button--disabled {
background: #9ca3af;
cursor: not-allowed;
}
.search-form__button--secondary {
background: #6b7280;
}
BEM (Block__Element--Modifier) is a naming convention that creates clear, predictable class names. Blocks are components, elements are parts of blocks (joined with __), and modifiers are variations (joined with --).
Class Selectors, ID Selectors, Multi-class Selectors, Naming Conventions, BEM, When to Use Class vs ID