Combinators & Attribute Selectors
Combinators select elements based on their relationship to other elements — inside them, directly inside, or next to them. Attribute selectors match elements by their attributes and values. Together they let you style precisely without adding a class to everything.
Descendant combinator (space)
.article a matches any <a> anywhere inside .article, at any depth.
Child combinator (>)
> matches only direct children, not grandchildren. Use it when nested structures would otherwise inherit styles they shouldn't.
Next-sibling combinator (+)
A + B matches B when it comes immediately after A, with the same parent.
Subsequent-sibling combinator (~)
A ~ B matches every B sibling that comes anywhere after A.
Combinator summary
| Selector | Matches |
|---|---|
A B | B anywhere inside A |
A > B | B that is a direct child of A |
A + B | B immediately after A |
A ~ B | Any B after A, same parent |
Combinators only look downwards and forwards. To style a parent based on its children, or an element based on what comes after it, use :has() (lesson 43).
Attribute selectors
Matching parts of a value
| Selector | Matches when the attribute… |
|---|---|
[attr] | exists |
[attr="v"] | equals v exactly |
[attr^="v"] | starts with v |
[attr$="v"] | ends with v |
[attr*="v"] | contains v anywhere |
[attr~="v"] | contains the word v in a space-separated list |
[attr|="v"] | is v or starts with v- (language codes) |
[attr="v" i] | case-insensitive match |
Styling by state attributes
Attribute selectors pair naturally with ARIA and data attributes, keeping visual state in sync with accessible state:
Keep selectors short
Long chains like body div.main ul li a span are fragile — any change to the HTML breaks them — and hard to override. Prefer a single meaningful class, with at most one or two levels of combinators.