Precise selectors are how you style exactly the elements you mean and nothing else.
Selectors choose which elements a rule applies to.
Simple selectors:
- type:
p,button - class:
.card(any element withclass="card"; elements can have many classes) - id:
#main(unique on the page; avoid for styling because it's very specific) - attribute:
[disabled],[type="email"],[href^="https"](starts with),[href$=".pdf"](ends with) - universal:
*
Chain them with no space to require all at once: button.primary[disabled] is a disabled button that also has class primary.
Combinators select by relationship:
nav a(space): anaanywhere inside anav(descendant).ul > li: anlithat is a direct child of aul.h2 + p: thepimmediately after anh2(adjacent sibling).h2 ~ p: everypsibling after anh2(general sibling).
A comma makes a selector list: h1, h2, h3 { ... } applies to all three.
Browsers match selectors right to left: for .sidebar a they look at every a and then check for a .sidebar ancestor. That's fast in practice, so write selectors for clarity. The bigger risk is fragility: long chains like main div > ul li a break when the markup changes. Prefer short selectors built around classes that describe what a component is.
Example
/* <nav class="menu"><a href="/">Home</a> <a href="/docs.pdf">Docs</a></nav> */
.menu a { text-decoration: none; } /* descendant */
.menu > a { padding: 4px 8px; } /* direct child */
a[href$=".pdf"]::after { content: " (PDF)"; } /* attribute: ends with */
h2 + p { margin-top: 0; } /* first p right after an h2 */
input[type="checkbox"] { accent-color: teal; }
button.primary { background: royalblue; color: white; } /* both at once */
button .icon { width: 1em; } /* .icon INSIDE a button */Remember
- Class selectors are the workhorse; avoid ids for styling
- space = descendant, > = child, + = next sibling, ~ = later siblings
- No space means 'same element': a.b is different from a .b
Watch out for
- Writing 'button .primary' (descendant) when you meant 'button.primary' (same element).