Master every CSS selector type with examples — type, class, ID, universal, attribute, pseudo-class, pseudo-element, combinators, and modern CSS4 selectors with specificity calculations.
CSS selectors determine which HTML elements your styles apply to. Understanding selectors deeply — from the basics to modern Level 4 selectors — makes you significantly faster at writing CSS and better at debugging specificity issues. This guide covers every selector type with copy-paste examples.
1. Basic Selectors
Universal Selector
/* Matches every element */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}Type / Element Selector
/* Matches all <p> elements */
p { line-height: 1.6; }
/* Multiple types */
h1, h2, h3 { font-family: 'Inter', sans-serif; }Class Selector
.card { border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
/* Element with class */
p.intro { font-size: 1.125rem; }
/* Multiple classes (AND) — matches elements with both classes */
.card.featured { border: 2px solid #0070DC; }ID Selector
#header { position: sticky; top: 0; z-index: 50; }
/* IDs have very high specificity — use sparingly, prefer classes */2. Attribute Selectors
/* Has attribute */
[disabled] { opacity: 0.5; cursor: not-allowed; }
/* Exact value */
[type="submit"] { background: #0070DC; color: white; }
/* Contains word in space-separated list */
[class~="btn"] { padding: 8px 16px; }
/* Starts with */
[href^="https"] { /* secure links */ }
[href^="mailto"] { /* email links */ }
/* Ends with */
[href$=".pdf"]::after { content: " (PDF)"; }
[src$=".webp"] { image-rendering: auto; }
/* Contains substring */
[href*="codercrafter"] { color: #0070DC; }
/* Case-insensitive matching (add i) */
[type="TEXT" i] { /* matches type="text", type="TEXT", type="Text" */ }3. Combinators
/* Descendant (space) — any level deep */
.nav a { text-decoration: none; }
.article p { margin-bottom: 1rem; }
/* Child (>) — direct children only */
.menu > li { display: inline-block; }
ul > li > a { color: inherit; }
/* Adjacent sibling (+) — immediately after */
h2 + p { font-size: 1.1rem; margin-top: 0; }
input:checked + label { font-weight: 600; }
/* General sibling (~) — all subsequent siblings */
h2 ~ p { color: #374151; }
.error ~ input { border-color: #ef4444; }4. Pseudo-Classes
User Interaction
a:hover { color: #0070DC; text-decoration: underline; }
button:active { transform: scale(0.98); }
input:focus { outline: 2px solid #0070DC; outline-offset: 2px; }
a:focus-visible { /* only shows on keyboard focus, not mouse */ }
a:visited { color: #7c3aed; }Form State
input:disabled { background: #f3f4f6; }
input:enabled { background: white; }
input:checked { accent-color: #0070DC; }
input:required { border-left: 3px solid #ef4444; }
input:optional { border-left: 3px solid #e5e7eb; }
input:valid { border-color: #22c55e; }
input:invalid { border-color: #ef4444; }
input:placeholder-shown { border-style: dashed; } /* input is empty */
input:read-only { background: #f9fafb; cursor: default; }Structural / Positional
/* First and last */
li:first-child { border-top: none; }
li:last-child { border-bottom: none; }
p:first-of-type { font-size: 1.1rem; }
p:last-of-type { margin-bottom: 0; }
/* nth-child — powerful formula */
tr:nth-child(even) { background: #f9fafb; } /* zebra striping */
li:nth-child(3) { color: red; } /* 3rd item */
li:nth-child(3n) { /* every 3rd: 3, 6, 9... */ }
li:nth-child(3n+1) { /* 1st of each group: 1, 4, 7... */ }
li:nth-child(-n+3) { /* first 3 items */ }
/* nth-of-type */
p:nth-of-type(2) { font-style: italic; }
/* only-child / only-of-type */
li:only-child { /* list with one item */ }
p:only-of-type { /* only p in its parent */ }Logical Pseudo-Classes
/* :not() — negate a selector */
a:not([href]) { color: gray; cursor: default; }
li:not(:last-child) { border-bottom: 1px solid #e5e7eb; }
input:not([type="submit"]) { border: 1px solid #d1d5db; }
/* :is() — matches any of the selectors (forgives invalid selectors) */
:is(h1, h2, h3, h4) { font-family: 'Inter', sans-serif; }
:is(article, section, aside) p { line-height: 1.8; }
/* :where() — same as :is() but specificity is always 0 */
:where(h1, h2, h3) { margin-top: 1.5em; }
/* :has() — parent selector! (CSS4, modern browsers) */
/* Select <p> that contains an <img> */
p:has(img) { display: flex; gap: 1rem; align-items: flex-start; }
/* Select <label> before a required input */
label:has(+ input:required)::after { content: " *"; color: red; }
/* Select <article> that has no <figure> */
article:not(:has(figure)) { padding-top: 2rem; }
/* Select <div> when its <input> is checked (replaces JS toggle!) */
.accordion:has(input:checked) .content { display: block; }5. Pseudo-Elements
/* ::before and ::after — insert generated content */
.required-label::after {
content: " *";
color: #ef4444;
}
blockquote::before {
content: '"';
font-size: 4rem;
color: #d1d5db;
line-height: 0;
vertical-align: -0.4em;
}
/* ::first-line — style the first line of text */
p::first-line {
font-variant: small-caps;
letter-spacing: 0.05em;
}
/* ::first-letter — drop cap effect */
p::first-letter {
float: left;
font-size: 3.5rem;
line-height: 0.8;
margin: 0 8px 0 0;
font-family: Georgia, serif;
}
/* ::placeholder */
input::placeholder { color: #9ca3af; font-style: italic; }
/* ::selection — text selection colour */
::selection { background: #bfdbfe; color: #1e3a5f; }
/* ::marker — list bullet/number */
li::marker { color: #0070DC; font-weight: 600; }
/* ::backdrop — behind dialogs */
dialog::backdrop { background: rgba(0, 0, 0, 0.5); }6. CSS Specificity
Specificity determines which rule wins when multiple selectors match the same element. Think of it as three columns (A, B, C):
| Selector | A (IDs) | B (Classes/Attrs) | C (Elements) |
|---|---|---|---|
| * | 0 | 0 | 0 |
| p | 0 | 0 | 1 |
| .card | 0 | 1 | 0 |
| #header | 1 | 0 | 0 |
| .card p | 0 | 1 | 1 |
| #nav .link:hover | 1 | 2 | 0 |
| :where(.card) p | 0 | 0 | 1 |
| inline style | Beats everything except !important | ||
/* :where() is identical to :is() but adds 0 specificity */
/* Use :where() in base styles to keep them easily overridable */
:where(h1, h2, h3) { color: #111827; } /* 0 specificity */
:is(h1, h2, h3) { color: #111827; } /* same as the most specific argument */7. Practical Selector Patterns
/* Lobotomised owl — add margin between stacked elements */
.stack > * + * { margin-top: 1rem; }
/* Card hover with child image zoom */
.card:hover .card-image { transform: scale(1.05); }
/* Show tooltip on hover */
.tooltip-wrapper:hover .tooltip { opacity: 1; visibility: visible; }
/* Dark mode with data attribute */
[data-theme="dark"] .card { background: #1f2937; color: #f9fafb; }
/* Focus within — style parent when child has focus */
.search-box:focus-within { box-shadow: 0 0 0 3px #bfdbfe; }
/* Empty state */
.list:empty::before {
content: "No items found";
display: block;
text-align: center;
color: #9ca3af;
padding: 2rem;
}Browser Support Notes
- :has() — Chrome 105+, Safari 15.4+, Firefox 121+. Use
@supports selector(:has(*)) {}for progressive enhancement - :is() and :where() — all modern browsers, universal support since 2022
- :focus-visible — all modern browsers, polyfill available for older targets
- ::marker — all modern browsers since 2021
- Attribute selectors (i flag) — all modern browsers
Conclusion
Mastering CSS selectors eliminates the need for JavaScript-driven class toggling in many scenarios. The modern :has() selector is particularly transformative — it enables parent selection, conditional styling based on child state, and form validation UI entirely in CSS. Understand specificity so you stop fighting it with !important, use :where() for low-specificity base styles, and leverage :is() to write concise multi-target rules. With these tools, your CSS becomes more declarative, readable, and powerful.









