Pseudo-classes & Pseudo-elements
While standard selectors target elements based on their HTML tag, attributes, or structural classes, pseudo-classes and pseudo-elements allow you to style elements based on dynamic user state, structural position, or virtual sub-parts of the DOM tree.
1. Pseudo-classes vs. Pseudo-elementsβ
The core syntax difference lies in the colon notation defined in CSS3 specifications:
- Pseudo-classes (
:) select existing DOM elements during specific dynamic states or structural conditions. - Pseudo-elements (
::) generate virtual abstraction nodes that do not explicitly exist in the HTML source code.
[ Dynamic State or Structural Condition ]
β
βββββββββββββββ΄ββββββββββββββ
βΌ βΌ
Pseudo-class ( : ) Pseudo-element ( :: )
Targets an existing node Creates/Targets a virtual sub-node
Example: a:hover Example: p::before
2. Deep Dive: Pseudo-Classesβ
Pseudo-classes alter an element's appearance based on user interaction, navigation history, or form field status.
User Action & State Pseudo-Classesβ
| Pseudo-class | Trigger Condition | Common Use Case |
|---|---|---|
:hover | Pointer device moves over element | Interactive button/link state |
:focus | Keyboard or mouse focuses form input/link | Accessibility outline indicators |
:focus-visible | Keyboard navigation focuses element (suppresses mouse focus outlines) | Clean modern accessibility indicators |
:active | Element is currently being clicked/pressed | Tactile "pressed" button feedback |
:disabled | Form control has disabled attribute | Dimmed styling for un-interactive inputs |
:checked | Checkbox or radio button is selected | Custom toggles and radio UI |
Structural Pseudo-Classesβ
Structural pseudo-classes target elements based on their position within their parent container's child list:
:first-child/:last-child: Targets the absolute first or last child of a parent.:nth-child(n): Selects elements based on an algebraic formula () or keywords (odd,even).:not(selector): Negates a matching condition.
/* Selects every even table row */
tr:nth-child(even) {
background-color: #f8fafc;
}
/* Selects every 3rd list item starting from index 1 (1, 4, 7...) */
li:nth-child(3n+1) {
font-weight: 700;
}
/* Styles buttons that DO NOT have the .btn-primary class */
button:not(.btn-primary) {
border: 1px solid #cbd5e1;
}
3. Deep Dive: Pseudo-Elementsβ
Pseudo-elements allow you to style specific fragments of an element's content or inject generated cosmetic elements into the render tree without polluting HTML markup.
Generated Content (::before & ::after)β
::before and ::after insert inline visual sub-nodes as the first or last child inside the targeted element. They strictly require the content property to render.
/* Creates a decorative external link arrow */
a.external-link::after {
content: " β";
font-size: 0.8em;
color: #2563eb;
}
/* Tooltip container indicator */
.tooltip::before {
content: "";
display: block;
width: 8px;
height: 8px;
background-color: #0f172a;
}
Typographic & Selection Pseudo-Elementsβ
::first-letter:Styles the very first letter of a block container (ideal for drop caps).::first-line:Styles the first rendered line of text dynamically.::selection:Controls the background and text color when a user highlights text on the page.
::selection {
background-color: #38bdf8;
color: #0f172a;
}
Interactive Playground: State & Generated Contentβ
Test dynamic state pseudo-classes alongside structural pseudo-elements in the live editor:
Best Practices & Gotchasβ
| Feature | Recommended Practice | Common Anti-Pattern |
|---|---|---|
| Colon Notation | Use double colons :: for pseudo-elements (::before) and single colon : for pseudo-classes (:hover). | Mixing up ::hover or :before across modern codebases. |
| Accessibility | Pair :hover styles with matching :focus or :focus-visible rules for keyboard users. | Removing outline focus indicators (outline: none) without custom alternatives. |