CSS Selectors Reference
Every selector type, specificity rules, and 2026 additions like :has() and @scope.
By Λ · Updated May 18, 2026
Specificity 101
Specificity decides which CSS rule wins when multiple rules target the same element. Three categories, scored as (a, b, c):
a
Inline + IDs
Inline
style="" attribute, then #id selectorsb
Classes + Attrs + Pseudo-classes
.class, [attr], :hoverc
Type + Pseudo-elements
div, ::beforeHigher tier always wins regardless of count. #id (1,0,0) beats .a.b.c.d (0,4,0). Inline style beats all selectors except !important. !important beats everything except later !important declarations.
Basic Selectors
| Selector | Matches | Specificity |
|---|---|---|
| * | Universal (any element) | (0,0,0) |
| element | By tag name (e.g. div, h1) | (0,0,1) |
| .class | By class name | (0,1,0) |
| #id | By id attribute | (1,0,0) |
| [attr] | Has attribute | (0,1,0) |
| [attr="value"] | Attribute equals value | (0,1,0) |
| [attr~="word"] | Attribute contains word (space-separated) | (0,1,0) |
| [attr|="lang"] | Equals "lang" or starts with "lang-" | (0,1,0) |
| [attr^="prefix"] | Starts with prefix | (0,1,0) |
| [attr$="suffix"] | Ends with suffix | (0,1,0) |
| [attr*="substring"] | Contains substring | (0,1,0) |
Combinators
| Combinator | Matches | Example |
|---|---|---|
| A B | Descendant (any depth below A) | article p |
| A > B | Direct child | ul > li |
| A + B | Adjacent sibling (immediately after) | h2 + p |
| A ~ B | General sibling (any after, same parent) | h2 ~ p |
Pseudo-classes (state)
| :hover | Mouse over |
| :focus | Keyboard focus |
| :focus-visible | Focus from keyboard (not mouse click) |
| :focus-within | Self or any descendant has focus |
| :active | Being clicked |
| :visited | Already-visited link |
| :checked | Checked checkbox/radio |
| :disabled / :enabled | Form control state |
| :required / :optional | Required attribute state |
| :valid / :invalid | Form validation state |
| :placeholder-shown | Input is showing placeholder |
| :read-only / :read-write | Editable state |
Pseudo-classes (structural)
| :first-child | First among siblings |
| :last-child | Last among siblings |
| :only-child | Only child |
| :nth-child(n) | nth child (1-based) |
| :nth-child(2n) | Every even child |
| :nth-child(2n+1) | Every odd child |
| :nth-of-type(n) | nth among same tag siblings |
| :first-of-type | First sibling of its type |
| :empty | No children (not even text) |
| :root | The root element (usually <html>) |
| :not(selector) | Does not match selector |
| :lang(en) | Element with lang attribute |
Modern selectors (2022-2026)
| :is(a, b, c) | Matches any selector in the list. Takes highest specificity of arguments. |
| :where(a, b, c) | Like :is() but always specificity (0,0,0). Great for resets. |
| :has(selector) | Parent selector. div:has(> img) matches divs with an img child. |
| :not(a, b) | Modern :not() accepts multiple selectors. |
| :dir(ltr) | Element with reading direction |
| :user-invalid | Invalid input but only after user interaction |
| :popover-open | Open popover element (Popover API) |
Pseudo-elements
| ::before | Inserted before content. Requires content: property. |
| ::after | Inserted after content. |
| ::first-letter | First letter of block. |
| ::first-line | First rendered line. |
| ::selection | Selected text. |
| ::placeholder | Placeholder text in input. |
| ::marker | List bullet/number. |
| ::backdrop | Backdrop of fullscreen / dialog element. |
Common patterns
- Zebra-striped rows:
tr:nth-child(even) { background: #f5f5f5; } - Style links that go to external sites:
a[href^="http"]:not([href*="example.com"]) { ... } - Style links that open PDFs:
a[href$=".pdf"]::after { content: " (PDF)"; } - First paragraph of an article:
article > p:first-of-type - Cards that have an image (2022+):
.card:has(img) - Reset specificity for a group:
:where(h1, h2, h3) { margin: 0; }stays low so users can override - Focus rings only for keyboard users:
:focus-visible { outline: 2px solid blue; }
Last updated