Lesson 3 of 6

Selectors & Specificity

Imagine two memos land on your desk about the same meeting: one from your direct manager, one from the CEO. If they conflict, you follow the CEO's — not because it arrived first or last, but because of who has more authority. CSS rules work the same way when two rules target the same element with conflicting styles: the browser doesn't just take "whichever was written last," it weighs which selector has more specificity — more authority — first.

💡 The Bottom Line

Selectors decide which elements a rule targets; specificity decides which rule wins when two rules target the same element with conflicting properties. IDs beat classes, classes beat element tags, and !important beats almost everything — which is exactly why it's a trap: it wins by breaking the system, not by playing it.

The basic selector types

/* Element selector — targets every <p> */
p { color: #333; }

/* Class selector — targets every element with class="highlight" */
.highlight { background: yellow; }

/* ID selector — targets the one element with id="main-title" */
#main-title { font-size: 2rem; }

/* Combinator — targets <li> elements that are direct children of .menu */
.menu > li { list-style: none; }

/* Combinator — targets <a> elements that are descendants of nav, at any depth */
nav a { text-decoration: none; }

Element selectors are broad and reusable across a whole site. Class selectors are the workhorse of everyday CSS — reusable, but scoped to whatever you tag. ID selectors are meant for one unique element per page, which is exactly why they carry more weight than a class.

🙋 There Are No Dumb Questions

Q: If IDs are more specific, should I just use IDs for all my styling?
A: No — because an ID can only be used once per page, styling with IDs means you can't reuse that rule anywhere else without duplicating it under a new ID. Classes are the better default for anything you might want to apply to more than one element, which in practice is almost everything.

How specificity is actually calculated

Browsers score each selector as a tuple of (IDs, classes/attributes/pseudo-classes, elements). Higher score wins, compared left to right:

p                  /* 0 IDs, 0 classes, 1 element   -> (0,0,1) */
.highlight         /* 0 IDs, 1 class,   0 elements  -> (0,1,0) */
#main-title        /* 1 ID,  0 classes, 0 elements   -> (1,0,0) */
nav a.active       /* 0 IDs, 1 class,   2 elements   -> (0,1,2) */
#nav .menu li.item /* 1 ID,  2 classes, 1 element    -> (1,2,1) */

Compare tuples left to right: any single ID beats any number of classes, and any single class beats any number of bare element selectors. So #main-title at (1,0,0) beats nav a.active at (0,1,2), even though the second selector "looks" more complicated — the ID digit outranks everything after it.

The !important trap

p {
  color: blue !important;
}

#main-title {
  color: red; /* loses — !important overrides normal specificity rules */
}

⚠️ Watch Out

!important doesn't fit into the specificity system — it jumps the whole queue, overriding even ID selectors. The trap: once one rule uses !important to "win," the only way to override that rule later is another !important, which starts an arms race that makes the cascade impossible to reason about. Treat it as an emergency escape hatch, not a everyday tool — reach for a more specific selector instead.

🔧 Try It Yourself

Write out the specificity tuple (IDs, classes, elements) for each of these selectors, then rank them from strongest to weakest, before checking your work against the calculation rule above: .card .title, #sidebar p, a, .btn.primary.

✅ Quick Check

Both .nav-link (a class) and a (an element selector) set different colors on the same link, with no !important involved. Which wins?


Next up: now that you can target and style individual boxes, how do you arrange many of them side by side without every layout turning into a math problem? That's Flexbox.

← Back to
Next →