CSSLesson 10 of 12

Pseudo-classes & Pseudo-elements

Some things you want to style do not exist in the markup. A link has a hovered state. A list has a first item. Those states and parts are invisible to normal selectors, so CSS gives you pseudo-classes and pseudo-elements to reach them.

Some things you want to style do not exist in the markup. A link has a hovered state. A list has a first item. Those states and parts are invisible to normal selectors, so CSS gives you pseudo-classes and pseudo-elements to reach them.

Pseudo-classes start with one colon and match a state or position. :hover fires when the pointer is over the element. :focus fires when the element is active via keyboard or click. :first-child matches the first child of a parent. They add interactivity and polish without any JavaScript.

Pseudo-elements start with two colons and create virtual parts of an element. ::before inserts content before the element, and ::after inserts content after it. Both need a content property to render at all. This is how you draw badges, icons, and decorative lines without touching the HTML. The classic example is a badge label on a card: the CSS draws it, the markup stays clean.

One colon versus two

The rule is easy to memorize: pseudo-classes use one colon, pseudo-elements use two. Pseudo-classes match a state or position the element is in, like :hover, :focus, :checked, and :first-child. Pseudo-elements create a virtual part of the element, like ::first-letter or ::selection. Older browsers accepted ::before with one colon, but two is the standard and works everywhere modern.

CSS
a:hover { color: #b91c1c; }        /* pseudo-class: a state */
p::first-letter { font-size: 24px; }  /* pseudo-element: a part */

::before and ::after need content

Pseudo-elements that insert content must declare a content property, or the browser renders nothing at all. That trips up everyone once. content can hold text, or an empty string when you only want a decorative shape. Because the inserted content is not in the markup, screen readers may ignore it, so keep real text in the HTML and use pseudo-elements for decoration.

CSS
.card::before {
  content: "New"; /* without content, nothing renders */
  color: white;
}

Focus is for everyone

:focus matters more than it gets credit for. Keyboard users move through a page with the Tab key, and the focus ring is their cursor. The habit of removing outlines, outline: none, on focus makes pages impossible to navigate without a mouse. If you style away the default ring, replace it with a visible one of your own.

CSS
button:focus-visible {
  outline: 3px solid #0891b2;
  outline-offset: 2px;
}

Syntax

CSS
button:hover {
  background-color: #0e7490;
}

p::first-letter {
  font-size: 24px;
}

::before Needs content:"" or It Renders Nothing

Pseudo-elements ::before/::after require content property or they are invisible — the #1 'why isn't my badge showing' bug.

CSS
.card::before{content:"New"; }

Good to know

  • Pseudo-classes use one colon and match states like hover and focus.
  • Pseudo-elements use two colons and create virtual parts.
  • ::before and ::after render nothing without a content property.
  • Keyboard users depend on :focus, so never remove the focus outline.
Try it Yourself Edit the code, press Run, then check what changed in the Result.
Ready
Code
Result

Practice makes it stick

Open the Playground and build your own page with the tags you just learned.

→ Open Playground