CSS Selector Basics: Type, Class, ID, and When to Use Each
The three selectors you will use constantly are the type selector, the class selector, and the id selector, and each one targets elements a different way.
Syntax
p {
/* type: every <p> element */
}
.card {
/* class: every element with class="card" */
}
#header {
/* id: the one element with id="header" */
} Type Selectors
A type selector matches every element of a given tag name, so p styles all your paragraphs at once. These are great for setting sensible defaults across a whole document, like a base line-height or a heading color:
h2 {
margin-block: 1.5rem 0.5rem;
}
a {
color: var(--color-link);
} Because a type selector hits every matching tag, it reaches wide by design. Use it for the baseline look of an element, then narrow things down with a class wherever a specific instance needs to differ.
A type selector also carries the lowest specificity score of the three, so any class or id touching the same element wins a conflict without a fight. That makes it the safe choice for broad defaults you expect a class to override later, and a poor choice anywhere you need a style to stick no matter what else touches that element.
Class Selectors
A class selector starts with a dot and matches any element carrying that class in its class attribute. This is the selector you will reach for most, since one class can apply to many elements and one element can carry several classes:
.button {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
}
.button.is-primary {
background: var(--color-accent);
} That second rule only matches elements with both button and is-primary on them. Classes give you reusable, predictable styling hooks, which is why component-driven CSS leans on them so heavily.
You can also stack classes on one element the way .button.is-primary does above, layering a specific variant on top of a general pattern instead of writing a whole new rule. That is the idea behind naming systems like BEM, where a block class supplies the shared look and a modifier class swaps out one piece of it. Reusing classes this way keeps a stylesheet shorter and keeps your intent readable when you come back to it months later.
Id Selectors
An id selector starts with a hash and targets the single element whose id matches. An id is supposed to be unique on the page, so reach for one when you genuinely mean one specific element:
#main-nav {
position: sticky;
top: 0;
} An id is not only a styling hook. It anchors in-page links, so a URL ending in #main-nav jumps the browser straight to that element, and it gives JavaScript a fast way to grab one specific node with document.getElementById. That dual duty is exactly why you want styling-by-id to stay light. The moment a script or an anchor link depends on that id, a styling change means touching code elsewhere too.
Choosing Between Them
Reach for a type selector when a style should apply everywhere that tag shows up. Reach for a class when you need a repeatable pattern that some elements opt into and others skip. Save an id for the rare element that truly is one of a kind, like a page header or a single modal root. Most real stylesheets lean hard on classes for exactly this reason, using type selectors sparingly for real document-wide defaults and ids sparingly for genuine landmarks. Writing an id selector just to win a specificity fight is usually a sign to add a class instead.
The Gotcha: Ids Carry Heavy Specificity
An id selector scores far higher than a class selector in the cascade, so a rule written with an id can be stubborn to override later. You set a #main-nav color, then a .dark-theme class fails to change it, and the page looks broken for no obvious reason. Lean on classes for styling and save ids for anchors and JavaScript hooks, and your overrides will keep working. The specificity guide walks through exactly how that scoring adds up.
Example
/* type: all paragraphs get comfortable spacing */
p {
margin-block: 0 1rem;
}
/* class: reusable highlight you can drop anywhere */
.highlight {
background: #fff3cd;
}
/* id: one specific landmark element */
#page-footer {
border-top: 1px solid var(--color-border);
} <p class="highlight">This paragraph gets both the type rule and the class rule.</p>
<footer id="page-footer">This footer gets the id rule.</footer> The paragraph picks up its spacing from the type rule and its yellow background from the class, since both selectors match it. The footer takes its top border from the id rule. Each element ends up wearing every rule whose selector it matched.
Frequently Asked Questions
Can an element have more than one class?
Yes. The class attribute takes a space-separated list, and every matching rule applies. This is how utility and component classes combine on one element, and why a card can carry both its component class and a modifier.
Is it valid to reuse the same id on more than one element?
No. An id must be unique in the document. Browsers still style duplicates, so the mistake often goes unnoticed, but getElementById returns only the first and fragment links jump only to that one.
Can a class name start with a number?
Only a leading digit needs escaping. A name starting with a letter, an underscore, or a hyphen followed by a letter is fine unescaped, which is why -webkit-box works. A hyphen followed by a digit, such as -4col, needs escaping to be selectable.
What does the universal selector do?
The asterisk matches every element. It is mostly used for resets, such as setting box-sizing across the document. It carries no specificity at all, so any other selector will beat it on a tie.
Do inline styles count as a selector?
No, they have no selector at all, which is why they sit above every stylesheet rule in the cascade. Only !important in a stylesheet can override an inline declaration, and needing that is usually a sign the styling should have been a class.
Related
- CSS Selectors for the full category overview
- Combinators for targeting elements by their relationships
- Specificity for how the cascade picks a winner
Sources
-
[1]
Type selectors(developer.mozilla.org)
-
[2]
Class selectors(developer.mozilla.org)
-
[3]
ID selectors(developer.mozilla.org)
-
[4]
Specificity(developer.mozilla.org)
Read Next
Descendant, child, and sibling combinators in CSS: how the space, the child symbol, and the sibling symbols target elements by relationship.
CSS pseudo-classes like :hover, :focus, and :nth-child(): how they match elements by state and position without extra classes in your markup.
How CSS selectors target elements: type, class, and id selectors, combinators, pseudo-classes, and how specificity decides which rule wins.