Skip to main content

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-classTrigger ConditionCommon Use Case
:hoverPointer device moves over elementInteractive button/link state
:focusKeyboard or mouse focuses form input/linkAccessibility outline indicators
:focus-visibleKeyboard navigation focuses element (suppresses mouse focus outlines)Clean modern accessibility indicators
:activeElement is currently being clicked/pressedTactile "pressed" button feedback
:disabledForm control has disabled attributeDimmed styling for un-interactive inputs
:checkedCheckbox or radio button is selectedCustom 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 (an+ban + b) 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:

Loading Interactive Editor...

Best Practices & Gotchas​

FeatureRecommended PracticeCommon Anti-Pattern
Colon NotationUse double colons :: for pseudo-elements (::before) and single colon : for pseudo-classes (:hover).Mixing up ::hover or :before across modern codebases.
AccessibilityPair :hover styles with matching :focus or :focus-visible rules for keyboard users.Removing outline focus indicators (outline: none) without custom alternatives.