What are CSS Pseudo-Classes?
Pseudo-classes are special keywords added to selectors that specify a special state of the selected elements. They allow you to style elements based on user interaction, position in the DOM, or other dynamic conditions, without adding extra classes or JavaScript.
- User Interaction:
:hover,:focus,:active,:focus-visible - Structural:
:first-child,:last-child,:nth-child(),:only-child,:empty - Form State:
:checked,:disabled,:required,:valid,:invalid - Negation & Logic:
:not(),:is(),:where(),:has()
Pseudo-classes are written after a selector, prefixed with a colon. Example: a:hover styles links when hovered.
New & Advanced Pseudo-Classes
- :is() — Matches any selector in a comma-separated list, making complex selectors easier to write and maintain. Example:
:is(h1, h2, h3) { color: purple; } - :where() — Like
:is()but always has zero specificity, useful for utility and reset styles. Example::where(nav a) { text-decoration: underline; } - :has() — Parent selector: matches elements that contain a certain child or descendant. Example:
article:has(img) { border: 2px solid #ffd200; } - :not() — Excludes elements matching a selector. Now supports complex selectors. Example:
li:not(.active) { opacity: 0.7; } - :focus-visible — Styles elements only when focused via keyboard navigation (not mouse), improving accessibility. Example:
button:focus-visible { outline: 2px solid #3498db; }
:is(), :where()
:is() and :where() simplify writing selectors for multiple elements. :where() always has zero specificity, so it won't override more specific rules.
Heading 2
Heading 3
Paragraph
.is-where-demo :is(h2, h3) { color: #8e44ad; }
.is-where-demo :where(p) { background: #e3f6fd; }
:has()
:has() lets you style a parent based on its children. For example, highlight cards that contain an image:
.has-demo .card:has(img) { border: 2px solid #ffd200; background: #f0e6fa; }
:not() (Advanced)
:not() can now accept complex selectors, not just simple classes or elements. For example, style all list items except those with a specific class or attribute:
- Apple
- Banana
- Skip Me
- Cherry
.not-advanced-demo li:not(.exclude, [data-skip]) { color: #8e44ad; font-weight: bold; }
:focus-visible
:focus-visible only applies focus styles when the user is navigating with a keyboard (Tab), not with a mouse click. This improves accessibility and avoids unwanted outlines.
.focus-visible-demo-btn:focus-visible { outline: 3px solid #3498db; background: #e3f6fd; }
Interactive Examples
:hover, :focus, :active
Style elements based on user interaction:
.demo-btn:hover { background: #8e44ad; color: #fff; }
.demo-btn:focus { outline: 2px solid #f7971e; }
.demo-btn:active { background: #3498db; }
:first-child, :last-child, :only-child
Target elements based on their position in the parent:
- First Child
- Middle Child
- Last Child
- Only Child
.child-demo li:first-child { color: #8e44ad; font-weight: bold; }
.child-demo li:last-child { color: #3498db; }
.child-demo.single li:only-child { background: #ffd200; }
:nth-child(), :nth-of-type()
Style elements based on their index (1-based):
- Item 1
- Item 2
- Item 3
- Item 4
- Item 5
- Apple
- Banana
- Cherry
- Banana
- Apple
.nth-demo li:nth-child(odd) { background: #f0e6fa; }
.nth-demo li:nth-child(3) { color: #f7971e; font-weight: bold; }
.nth-demo li:nth-child(2n) { background: #e3f6fd; }
.nth-of-type-demo li:nth-of-type(2) { background: #ffd200; color: #8e44ad; }
.nth-of-type-demo li:nth-of-type(3) { background: #8e44ad; color: #fff; }
:empty
Target elements with no children (including text):
.empty-box:empty { border: 2px dashed #8e44ad; background: #f0e6fa;}
:not()
Exclude elements from a selection:
- Apple
- Banana
- Cherry
.not-demo li:not(.exclude) { color: #3498db; font-weight: bold; }
Form State Pseudo-Classes
.form-demo input:required { border-color: #f7971e; }
.form-demo input:valid { border-color: #3498db; }
.form-demo input:invalid { border-color: #e74c3c; }
.form-demo input:checked + label { color: #8e44ad; font-weight: bold; }
Best Practices & Accessibility
- Use
:focusstyles for keyboard accessibility. - Combine pseudo-classes for advanced effects (e.g.,
a:hover:active). - Test pseudo-class behavior across browsers and devices.
- Use
:not()to simplify complex selectors. - Prefer semantic HTML for better selector targeting.