CSS selectors target HTML elements for styling. Here's a comprehensive guide from basic to advanced.
Basic Selectors#
1/* Universal selector - all elements */
2* {
3 box-sizing: border-box;
4}
5
6/* Type selector - element name */
7p {
8 color: black;
9}
10
11h1, h2, h3 {
12 font-weight: bold;
13}
14
15/* Class selector */
16.button {
17 padding: 10px 20px;
18}
19
20/* ID selector (use sparingly) */
21#header {
22 position: sticky;
23}
24
25/* Multiple classes */
26.btn.primary.large {
27 /* Element with all three classes */
28}Attribute Selectors#
1/* Has attribute */
2[disabled] {
3 opacity: 0.5;
4}
5
6/* Exact value */
7[type="text"] {
8 border: 1px solid gray;
9}
10
11/* Value starts with */
12[href^="https"] {
13 color: green;
14}
15
16/* Value ends with */
17[href$=".pdf"] {
18 background-image: url('pdf-icon.png');
19}
20
21/* Value contains */
22[class*="icon"] {
23 display: inline-block;
24}
25
26/* Value in space-separated list */
27[class~="featured"] {
28 border: 2px solid gold;
29}
30
31/* Value starts with (or value-) */
32[lang|="en"] {
33 /* Matches en, en-US, en-GB, etc. */
34}
35
36/* Case insensitive */
37[type="submit" i] {
38 /* Matches Submit, SUBMIT, submit */
39}Combinators#
1/* Descendant (any level) */
2article p {
3 line-height: 1.6;
4}
5
6/* Child (direct only) */
7ul > li {
8 list-style: disc;
9}
10
11/* Adjacent sibling (immediately after) */
12h2 + p {
13 font-size: 1.2em;
14}
15
16/* General sibling (any after) */
17h2 ~ p {
18 color: #333;
19}
20
21/* Complex combinations */
22nav > ul > li > a {
23 text-decoration: none;
24}
25
26article > p:first-child + p {
27 /* Second paragraph of article */
28}Pseudo-Classes#
1/* Link states */
2a:link { color: blue; }
3a:visited { color: purple; }
4a:hover { color: red; }
5a:active { color: orange; }
6a:focus { outline: 2px solid blue; }
7
8/* Form states */
9input:focus { border-color: blue; }
10input:disabled { opacity: 0.5; }
11input:enabled { background: white; }
12input:checked { accent-color: green; }
13input:required { border-left: 3px solid red; }
14input:optional { border-left: 3px solid gray; }
15input:valid { border-color: green; }
16input:invalid { border-color: red; }
17input:in-range { background: lightgreen; }
18input:out-of-range { background: lightcoral; }
19input:placeholder-shown { font-style: italic; }
20input:read-only { background: #f5f5f5; }
21input:read-write { background: white; }
22input:default { /* Default button/option */ }
23input:indeterminate { /* Checkbox neither checked nor unchecked */ }
24
25/* Interactive */
26button:hover { background: lightblue; }
27details:open { background: lightyellow; }
28dialog:open { display: block; }Structural Pseudo-Classes#
1/* First/last child */
2li:first-child { font-weight: bold; }
3li:last-child { border-bottom: none; }
4p:only-child { margin: 0; }
5
6/* First/last of type */
7p:first-of-type { font-size: 1.2em; }
8h2:last-of-type { margin-bottom: 0; }
9img:only-of-type { display: block; margin: 0 auto; }
10
11/* nth-child patterns */
12tr:nth-child(odd) { background: #f9f9f9; }
13tr:nth-child(even) { background: white; }
14li:nth-child(3) { color: red; } /* Third item */
15li:nth-child(n+4) { color: gray; } /* 4th and after */
16li:nth-child(-n+3) { font-weight: bold; } /* First 3 */
17li:nth-child(3n) { border-top: 1px solid; } /* Every 3rd */
18li:nth-child(3n+1) { clear: left; } /* 1st, 4th, 7th... */
19
20/* nth-last-child (from end) */
21li:nth-last-child(1) { /* Same as :last-child */ }
22li:nth-last-child(2) { /* Second to last */ }
23li:nth-last-child(n+3) { /* All except last 2 */ }
24
25/* nth-of-type */
26p:nth-of-type(2) { /* Second paragraph */ }
27img:nth-of-type(odd) { float: left; }
28img:nth-of-type(even) { float: right; }
29
30/* Empty elements */
31p:empty { display: none; }
32td:empty::after { content: '-'; }Negation and Matching#
1/* :not() - negation */
2p:not(.special) { color: #333; }
3input:not([disabled]) { background: white; }
4li:not(:last-child) { border-bottom: 1px solid; }
5:not(p) { /* All except paragraphs */ }
6
7/* Multiple negations */
8button:not(.primary):not(.secondary) {
9 background: gray;
10}
11
12/* :is() - matches any (forgiving) */
13:is(h1, h2, h3, h4) {
14 font-family: serif;
15}
16
17article :is(h1, h2, h3) {
18 color: navy;
19}
20
21/* :is() reduces repetition */
22/* Instead of:
23section h1, section h2, article h1, article h2 { } */
24:is(section, article) :is(h1, h2) {
25 margin-top: 1em;
26}
27
28/* :where() - like :is() but zero specificity */
29:where(h1, h2, h3) {
30 /* Easy to override */
31 color: black;
32}
33
34/* :has() - parent selector */
35article:has(img) {
36 padding: 20px;
37}
38
39form:has(input:invalid) {
40 border: 1px solid red;
41}
42
43/* Card with image vs without */
44.card:has(> img) {
45 padding: 0;
46}
47
48.card:not(:has(> img)) {
49 padding: 20px;
50}Pseudo-Elements#
1/* First letter/line */
2p::first-letter {
3 font-size: 2em;
4 float: left;
5}
6
7p::first-line {
8 font-weight: bold;
9}
10
11/* Before/after content */
12.required::after {
13 content: ' *';
14 color: red;
15}
16
17blockquote::before {
18 content: '"';
19 font-size: 3em;
20}
21
22/* Clear fix */
23.clearfix::after {
24 content: '';
25 display: table;
26 clear: both;
27}
28
29/* Selection */
30::selection {
31 background: yellow;
32 color: black;
33}
34
35/* Placeholder */
36input::placeholder {
37 color: #999;
38 font-style: italic;
39}
40
41/* List markers */
42li::marker {
43 color: blue;
44 font-weight: bold;
45}
46
47/* File input button */
48input[type="file"]::file-selector-button {
49 background: blue;
50 color: white;
51}
52
53/* Scrollbar (webkit) */
54::-webkit-scrollbar {
55 width: 8px;
56}
57
58::-webkit-scrollbar-thumb {
59 background: #888;
60 border-radius: 4px;
61}
62
63/* Backdrop */
64dialog::backdrop {
65 background: rgba(0, 0, 0, 0.5);
66}Target and Focus#
1/* URL fragment target */
2:target {
3 background: yellow;
4 animation: highlight 1s;
5}
6
7section:target {
8 border-left: 3px solid blue;
9}
10
11/* Focus states */
12:focus {
13 outline: 2px solid blue;
14}
15
16:focus-visible {
17 /* Only keyboard focus, not click */
18 outline: 2px solid blue;
19}
20
21:focus-within {
22 /* Parent contains focused element */
23 border-color: blue;
24}
25
26/* Form with focused input */
27form:focus-within {
28 box-shadow: 0 0 0 2px blue;
29}State and Language#
1/* Language */
2:lang(en) { quotes: '"' '"' "'" "'"; }
3:lang(fr) { quotes: '«' '»' '"' '"'; }
4
5/* Direction */
6:dir(ltr) { text-align: left; }
7:dir(rtl) { text-align: right; }
8
9/* Root (html element) */
10:root {
11 --primary-color: blue;
12 font-size: 16px;
13}
14
15/* Scope (with @scope) */
16@scope (.card) {
17 :scope { border: 1px solid; }
18 p { margin: 0; }
19}Specificity#
1/* Specificity: (inline, id, class, type) */
2
3/* (0,0,0,1) - type */
4p { }
5
6/* (0,0,1,0) - class */
7.text { }
8
9/* (0,0,1,1) - class + type */
10p.text { }
11
12/* (0,1,0,0) - id */
13#main { }
14
15/* (0,1,1,1) - id + class + type */
16div#main.container { }
17
18/* :is() uses highest specificity inside */
19:is(#header, .nav) {
20 /* Specificity of #header (0,1,0,0) */
21}
22
23/* :where() has zero specificity */
24:where(#header, .nav) {
25 /* Specificity (0,0,0,0) */
26}
27
28/* :not() uses argument specificity */
29p:not(.highlight) {
30 /* (0,0,1,1) */
31}Practical Examples#
1/* Navigation highlighting */
2nav a[href]:not([href^="#"]):not([href^="mailto"]) {
3 /* External links */
4 color: blue;
5}
6
7/* Form validation styling */
8form:has(:invalid) button[type="submit"] {
9 opacity: 0.5;
10 pointer-events: none;
11}
12
13/* Table with hover */
14tr:nth-child(odd):hover:not(:first-child) {
15 background: lightblue;
16}
17
18/* Card grid */
19.cards > article:nth-child(3n+1) {
20 clear: left;
21}
22
23/* Skip link */
24a[href="#main"]:not(:focus) {
25 position: absolute;
26 left: -9999px;
27}
28
29/* Custom checkbox */
30input[type="checkbox"]:checked + label::before {
31 content: '✓';
32 color: green;
33}Best Practices#
Selector Choice:
✓ Use classes for styling
✓ Use BEM or similar methodology
✓ Keep specificity low
✓ Prefer :where() for resets
Performance:
✓ Avoid deep nesting
✓ Avoid universal with descendants
✓ Use efficient selectors
✓ ID is fastest (but avoid for styling)
Maintainability:
✓ Be specific as needed
✓ Document complex selectors
✓ Use :is() to reduce repetition
✓ Keep selectors readable
Avoid:
✗ Over-qualified selectors (div.class)
✗ Deep descendant chains
✗ !important battles
✗ ID selectors for styling
Conclusion#
CSS selectors range from simple type and class selectors to powerful pseudo-classes like :has() and :is(). Master attribute selectors for dynamic styling, structural pseudo-classes for patterns, and understand specificity to write maintainable CSS. Modern selectors like :has() enable parent selection, while :where() allows zero-specificity utility styles.