Pseudo-Classes and Pseudo-Elements

Pseudo-classes and pseudo-elements let you style elements based on their state, position, or specific parts of their content, without adding extra HTML.

Pseudo-classes

A pseudo-class targets an element in a particular state. You add it after the selector with a single colon (:).

selector:pseudo-class {
  property: value;
}

User interaction

:hover, :focus, and :active respond to what the user is doing.

/* unvisited link */
a:link {
  color: #3b82f6;
}

/* visited link */
a:visited {
  color: #7c3aed;
}

/* mouse over link */
a:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

/* link being clicked */
a:active {
  color: #1e40af;
}

Form states

These pseudo-classes let you style form inputs based on their current state.

/* input when it has keyboard focus */
form input:focus {
  border: 2px solid #3b82f6;
  outline: none;
}

/* input when its value passes HTML validation */
form input:valid {
  border: 2px solid #16a34a;
}

/* input when its value fails HTML validation */
form input:invalid {
  border: 2px solid #dc2626;
}

/* disabled input */
input:disabled {
  background-color: #f3f4f6;
  cursor: not-allowed;
}

/* checked checkbox or radio button */
input:checked {
  accent-color: #3b82f6;
}

Structural pseudo-classes

These target elements based on their position in the document.

:first-child and :last-child target the first or last child of a parent.

ul li:first-child {
  font-weight: bold;
}

ul li:last-child {
  border-bottom: none;
}

:nth-child() is more flexible. It accepts a number, even, odd, or a formula like 3n.

/* Target every other table row */
tr:nth-child(even) {
  background-color: #f3f4f6;
}

/* Target every third list item */
li:nth-child(3n) {
  font-weight: bold;
}

/* Target the second item */
li:nth-child(2) {
  color: #3b82f6;
}

:not()

:not() selects elements that do NOT match a selector. Useful for styling everything except a specific case.

/* Style all buttons except the primary one */
button:not(.primary) {
  background-color: transparent;
  border: 1px solid #d1d5db;
}

:is() and :where()

Both let you group multiple selectors to avoid repetition. The difference is specificity: :is() takes on the specificity of its most specific argument; :where() always has zero specificity.

/* Instead of writing h1 a, h2 a, h3 a */
:is(h1, h2, h3) a {
  color: inherit;
}

/* Same result but with zero specificity, easier to override */
:where(h1, h2, h3) a {
  color: inherit;
}

For a full list of pseudo-classes, see the MDN pseudo-classes reference .

Pseudo-elements

A pseudo-element styles a specific part of an element rather than the element itself. Use a double colon (::), which is the modern standard.

selector::pseudo-element {
  property: value;
}

::before and ::after

These inject generated content before or after an element’s actual content. They require a content property (it can be an empty string).

/* Add a required field marker */
.required-field::after {
  content: " *";
  color: #dc2626;
}

A common use is decorative shapes or icons that should not be in the HTML.

::first-letter

Styles the first letter of a block of text. Good for drop caps.

p::first-letter {
  font-size: 2.5em;
  font-weight: bold;
  float: left;
  margin-right: 4px;
  line-height: 1;
}

::first-line

Styles the first line of a paragraph. The amount of text this covers depends on the viewport width.

p::first-line {
  font-weight: bold;
  font-size: 1.1em;
}

::selection

Styles text that the user has highlighted by clicking and dragging.

p::selection {
  background-color: #bfdbfe;
  color: #1e3a8a;
}

::placeholder

Styles the placeholder text in an input or textarea.

input::placeholder {
  color: #9ca3af;
  font-style: italic;
}

::marker

Styles the bullet or number on a list item.

li::marker {
  color: #3b82f6;
  font-weight: bold;
}

For a full list of pseudo-elements, see the MDN pseudo-elements reference .

  • Responsive Design : media queries, fluid units, and container queries
  • Transform : moving, rotating, scaling, and animating elements