How Do CSS ::before and ::after Generate Layout Content?

CSS pseudo-elements ::before and ::after generate visual content by creating virtual child elements directly inside the target element's formatting box. Rather than mutating the HTML Document Object Model (DOM), the browser's layout engine injects these pseudo-elements into the render tree whenever a valid content property is declared. Once generated, they participate in the layout pipeline identically to standard HTML elements, following normal CSS box-model rules, formatting contexts, and positioning schemes.

The Render Tree vs. The DOM Tree

To understand how pseudo-elements generate layout, it is necessary to distinguish between the DOM tree and the render (or formatting) tree. The DOM represents the parsed HTML document structure. When the browser constructs the render tree to compute layout geometry and paint pixels, it combines the DOM nodes with their computed CSS styles.

Pseudo-elements do not exist in the DOM, meaning JavaScript methods like document.querySelector('::before') cannot access them directly. Instead, the CSS engine constructs them exclusively within the render tree. They act as the target element's first child (::before) or last child (::after), nested inside the element's container box.

The Critical Role of the content Property

A pseudo-element generates a box in the layout tree if and only if its content property is defined with a valid value other than none or normal.

If content is missing, set to none, or invalid, the browser suppresses box generation entirely. Setting content: "" generates an empty box that can be sized, colored, and positioned purely for visual layout.

.card::before {
  content: "";
  display: block;
  width: 100%;
  height: 4px;
  background-color: #007acc;
}

The content property accepts several types of values:

Box Generation and Flow Behavior

By default, the initial computed value of display for pseudo-elements is inline. This means they flow naturally with adjacent inline content and text runs. However, you can explicitly reassign the display property to any valid layout model.

Inline and Block Formatting

When styled with display: block, a pseudo-element breaks onto its own line and expands across the available inline axis of the parent container. When left as inline, it respects line heights, baseline alignment, and horizontal margins/padding, wrapping with the surrounding text.

Flex and Grid Containers

If the parent element uses display: flex or display: grid, its ::before and ::after pseudo-elements become direct flex items or grid items.

.badge-container {
  display: flex;
  align-items: center;
  gap: 8px;
}

.badge-container::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #28a745;
}

In this flexbox layout, the browser treats the ::before pseudo-element as the first flex item, automatically applying flex alignment, gap calculations, and ordering rules.

Positioning Contexts

Setting position: absolute or position: fixed removes the pseudo-element from the normal document flow. An absolutely positioned pseudo-element will position itself relative to the nearest positioned ancestor (an ancestor with position: relative, absolute, fixed, or sticky). This pattern is commonly used for decorative backgrounds, overlays, or custom interactive markers.

Sizing and the CSS Box Model

Once generated, pseudo-element boxes adhere to all standard CSS box-model specifications:

Because ::before and ::after generate fully compliant render boxes without cluttering the HTML markup with presentational <span> or <div> tags, they serve as a core tool for cleanly separating structure from design in modern web development.