πŸ“‹ Cheat Sheets

CSS Selectors Cheat Sheet β€” Every Selector You Need to Know


Click any selector to expand the explanation and examples.

🎯 Basic Selectors

element, .class, #id, * basics
p { }           /* All paragraphs */
.card { }       /* Class */
#header { }     /* ID */
* { }           /* Everything */

/* Combine / p.intro { } / Paragraphs with class β€œintro” / div.card.active { } / div with both classes */

πŸ”— Combinators

A B β€” descendant combinator
Selects B anywhere inside A (any depth).
/* Any link inside nav, no matter how deep */
nav a { color: blue; }

/* Any span inside .card */ .card span { font-weight: bold; }

A > B β€” direct child combinator
Selects B only if it's a direct child of A.
/* Only direct li children of ul */
ul > li { list-style: none; }

/* Direct paragraphs in .content, not nested ones */ .content > p { margin-bottom: 1em; }

A + B β€” adjacent sibling combinator
Selects B immediately after A (same parent).
/* Paragraph right after an h2 */
h2 + p { font-size: 1.2em; }

/* Input right after a label */ label + input { margin-left: 0.5em; }

A ~ B β€” general sibling combinator
Selects all B siblings after A.
/* All paragraphs after an h2 (same parent) */
h2 ~ p { color: gray; }

πŸ“‹ Attribute Selectors

[attr], [attr=val], [attr*=val] attribute
[href] { }           /* Has href attribute */
[type="text"] { }    /* Exact match */
[class~="card"] { }  /* Contains word "card" */
[href^="https"] { }  /* Starts with "https" */
[href$=".pdf"] { }   /* Ends with ".pdf" */
[href*="example"] { } /* Contains "example" */
[data-theme="dark" i] { } /* Case-insensitive match */
/* Practical examples */
a[href^="http"] { }       /* External links */
a[href$=".pdf"]::after {  /* PDF icon after PDF links */
  content: " πŸ“„";
}
input[type="email"] { }   /* Email inputs */
[data-active="true"] { }  /* Custom data attributes */

🎭 Pseudo-Classes

:hover, :focus, :active state
a:hover { color: red; }
a:active { color: darkred; }  /* While clicking */

/* Focus styles (accessibility!) / input:focus { outline: 2px solid blue; } button:focus-visible { outline: 2px solid blue; } / focus-visible = only keyboard focus, not mouse clicks */

:disabled { opacity: 0.5; } :enabled { } :checked { } /* Checked checkboxes/radios */ :required { border-color: red; } :valid { border-color: green; } :invalid { border-color: red; } ::placeholder { color: gray; }

:first-child, :last-child, :nth-child() structural
li:first-child { }
li:last-child { }
li:nth-child(2) { }        /* Second item */
li:nth-child(odd) { }      /* 1st, 3rd, 5th... */
li:nth-child(even) { }     /* 2nd, 4th, 6th... */
li:nth-child(3n) { }       /* Every 3rd (3, 6, 9...) */
li:nth-child(3n+1) { }     /* 1st, 4th, 7th... */
li:nth-last-child(2) { }   /* 2nd from end */
li:only-child { }           /* Only child of parent */

/* Type variants (only count same element type) */ p:first-of-type { } p:last-of-type { } p:nth-of-type(2) { }

/* Practical: zebra stripes */ tr:nth-child(even) { background: #f5f5f5; }

/* Remove border from last item */ li:last-child { border-bottom: none; }

:not() β€” negation logic
/* All inputs except submit buttons */
input:not([type="submit"]) { border: 1px solid gray; }

/* All links except those with class β€œnav” */ a:not(.nav) { text-decoration: underline; }

/* Can chain */ li:not(:first-child):not(:last-child) { }

:is() and :where() β€” grouping modern
/* Without :is() β€” repetitive */
header a, nav a, footer a { color: blue; }

/* With :is() β€” clean */ :is(header, nav, footer) a { color: blue; }

/* Nested */ .card :is(h1, h2, h3) { margin-top: 0; }

/* :where() is the same but with 0 specificity / :where(header, nav, footer) a { color: blue; } / Easy to override because specificity is 0 */

:has() β€” parent selector modern
The "parent selector" CSS never had until now. Supported in all modern browsers.
/* Card that contains an image */
.card:has(img) { padding: 0; }

/* Form with invalid input */ form:has(:invalid) { border: 2px solid red; }

/* Section that has an h2 */ section:has(> h2) { padding-top: 2em; }

/* Label when its input is focused */ label:has(+ input:focus) { color: blue; }

/* Page with dark mode toggle checked */ body:has(#dark-mode:checked) { background: #1a1a1a; }

✨ Pseudo-Elements

::before, ::after pseudo
/* Add content before/after an element */
.required::after {
  content: " *";
  color: red;
}

/* Decorative elements */ .quote::before { content: """; font-size: 3em; color: gray; }

/* Clearfix */ .container::after { content: ""; display: table; clear: both; }

::before and ::after require the content property (even if empty).

::first-line, ::first-letter, ::selection pseudo
/* Style first line of a paragraph */
p::first-line { font-weight: bold; }

/* Drop cap */ p::first-letter { font-size: 3em; float: left; line-height: 1; margin-right: 0.1em; }

/* Custom text selection color */ ::selection { background: #3b82f6; color: white; }

/* Style placeholder text */ input::placeholder { color: #9ca3af; }

πŸ“Š Specificity Quick Reference

How specificity works reference
Specificity is calculated as (ID, Class, Element):
*                    β†’ (0, 0, 0)
p                    β†’ (0, 0, 1)
p.intro              β†’ (0, 1, 1)
#header              β†’ (1, 0, 0)
#header .nav li      β†’ (1, 1, 1)
#header .nav li.active β†’ (1, 2, 1)
style="..."          β†’ wins over everything
!important           β†’ wins over inline styles
Higher specificity wins. If equal, the last rule wins. :is() takes the specificity of its most specific argument. :where() always has 0 specificity.