CSS Pseudo-Elements: ::before and ::after
Introduction
CSS ::before and ::after create generated boxes associated with an existing element. They are commonly used for decorative icons, quotation marks, badges, overlays, separators, underlines, tooltip arrows, and visual effects that do not deserve extra HTML. Before is generated before the originating element's actual content, while after is generated after that content.
These boxes are not ordinary DOM nodes. JavaScript cannot select them with normal element-query APIs, and essential content placed only in their content property may be copied, translated, printed, or announced inconsistently. The strongest pattern is semantic HTML for meaning and pseudo-elements for presentation that can disappear without making the page incomplete.
What Is a Pseudo-Element?
A pseudo-element selects or creates a stylable part associated with an element rather than selecting another real element. Some pseudo-elements target existing content fragments, such as ::first-line or ::selection. Before and after generate content-bearing boxes at defined positions around the originating element's contents.
The originating element remains the selector subject. In .note::before, CSS matches an element with the note class and styles its generated before box. The pseudo-element participates in layout and painting according to its display, positioning, and other properties, but it does not become a child node in the DOM API.
Pseudo-Elements Versus Pseudo-Classes
A pseudo-class such as :hover, :checked, or :first-child matches an existing element in a condition. A pseudo-element such as ::before targets a generated or defined part associated with that element. Pseudo-classes use one colon; modern pseudo-element syntax uses two.
They can be combined. button:hover::before styles the button's before box while the button is hovered. The state belongs to the button, and the generated box receives the declarations. Reading the selector in that order makes complex effects easier to debug.
Why the Double-Colon Syntax Exists
CSS introduced double-colon syntax to visually distinguish pseudo-elements from pseudo-classes. Modern code should use ::before and ::after. Browsers continue supporting the older single-colon forms :before and :after for compatibility with historical stylesheets.
Using two colons improves consistency with newer pseudo-elements and communicates that a generated box, not an element state, is being styled. A project should choose one modern convention rather than mixing legacy and current spelling without a compatibility reason.
Basic Syntax
.message::before {
content: "";
display: inline-block;
}
.message::after {
content: "";
display: block;
}
The selector identifies the originating message element. Each pseudo-element needs a computed content value that generates it; an empty string is common for purely decorative shapes. Display controls whether the generated box behaves inline, block, flex, grid, or in another supported mode.
What Is ::before?
The ::before pseudo-element generates a box as the first child-like box of the originating element, before its actual content. It can appear inline with text, occupy layout space, or be removed from normal flow through absolute positioning. It inherits inheritable properties from the originating element unless declarations specify otherwise.
Before does not mean “before the element itself” in the DOM. Its generated content is inside the element's formatting context. Margins, positioning, overflow, flex behavior, and stacking therefore depend on the originating element and surrounding layout.
What Is ::after?
The ::after pseudo-element generates a box after the originating element's actual content, as its last child-like box. It is useful for trailing icons, external-link hints, overlays, decorative rules, clearing techniques in legacy layouts, and state indicators.
Like before, after is tied to the originating element. It can be inline or block, participate as a flex or grid item in relevant layouts, or become absolutely positioned. It does not insert a sibling after the element in the DOM.
The content Property
The content property controls whether and what generated content appears. For before and after, content: normal generally computes to no generated box. Authors commonly use content: "" to create a box for shapes or overlays. Text strings, counters, quotes, attribute values, and images can also be generated.
Content is not an HTML parser. A string such as "<strong>New</strong>" renders as text rather than creating markup. Generated text is presentation and should not be trusted for critical labels, instructions, or data.
A Basic Text Example
<p class="tip">Save your work frequently.</p>
.tip::before {
content: "Tip: ";
color: #0b5d91;
font-weight: 700;
}
The visual result begins with “Tip:” without extra markup. However, this label carries meaning and may be handled inconsistently by assistive technology, translation tools, copying, and search extraction. A real text span is safer when users need the prefix to understand the message. Reserve generated strings for redundant or nonessential presentation.
Using Empty Content for Decoration
Decorative lines, dots, rings, arrows, and overlays generally use an empty content string. Width, height, border, background, transform, and border radius then shape the box. Because no text is generated, the effect is less likely to introduce duplicate spoken content.
Empty content does not automatically make a design accessible. Decorative boxes can obscure text, intercept clicks, disappear in forced-colors mode, or create motion. Position, contrast, pointer events, stacking, and user preferences still require testing.
Inline Generated Content
Before and after default to inline behavior in common cases, so generated strings or icons can sit alongside text. Vertical alignment, line height, font fallback, and bidirectional text affect the result. A Unicode glyph that aligns in one font may shift or display differently in another platform font.
Use inline-block when dimensions, transforms, or controlled spacing are needed. Prefer logical margins such as margin-inline-end so icons adapt to left-to-right and right-to-left interfaces.
Block Generated Content
Setting display: block allows a pseudo-element to occupy its own line and use width, margin, and block formatting behavior. A heading can use after as a decorative rule beneath the text without adding a separate span.
.section-title::after {
content: "";
display: block;
inline-size: 3rem;
block-size: 3px;
margin-block-start: 0.5rem;
background: currentColor;
}
CurrentColor keeps the decoration aligned with the heading color, including theme changes. The rule remains decorative and can disappear without changing the heading's meaning.
Absolute Positioning
Many designs remove the generated box from normal flow with position: absolute. The originating element is commonly set to position: relative, establishing a containing block. Logical inset properties place the pseudo-element in writing-mode-aware directions.
.badge {
position: relative;
}
.badge::after {
content: "";
position: absolute;
inset-block-start: -0.25rem;
inset-inline-end: -0.25rem;
inline-size: 0.6rem;
block-size: 0.6rem;
border-radius: 50%;
background: #b42318;
}
Reserve enough space or allow safe overflow so the dot is not clipped. Absolute positioning does not add layout space.
Stacking and z-index
Pseudo-elements participate in the originating element's stacking behavior. Positioned generated boxes can use z-index, but the surrounding stacking context limits where they can paint. A negative z-index may place a box behind its parent's background or other content in unexpected ways.
When an overlay hides text, inspect stacking contexts created by position, opacity, transform, isolation, filters, and containment. A simple approach gives the overlay a lower layer and the actual content a positioned higher layer. Avoid z-index escalation without understanding the local stacking context.
Pointer Events
An absolutely positioned pseudo-element can cover links or buttons and intercept hit testing as part of the originating element's rendering. Decorative overlays commonly use pointer-events: none so they do not block interaction with underlying content. Whether that is correct depends on the intended target.
Pseudo-elements cannot receive independent DOM event listeners. Events are associated with actual elements and hit-testing behavior. If a visual part needs its own action, focus, accessible name, or tooltip, create a real interactive element instead of simulating one with generated content.
Decorative Icons
A pseudo-element can display a text glyph, background image, mask, or generated image before a label. Decorative icons should generally inherit or adapt to currentColor and remain visible in high-contrast modes. Icon fonts can fail to load, map glyphs incorrectly, or produce unwanted spoken characters.
For meaningful icons, use real markup with accessible text or an appropriate accessible-name strategy. A pseudo-element is best for redundant decoration, such as a chevron beside text that already says “Next.”
Unicode and Escaped Characters
CSS strings can contain Unicode characters directly when file encoding is correct or use CSS escapes. For example, a check mark may be represented by an escape followed by suitable whitespace or termination. Direct Unicode is often easier to read in UTF-8 source, while escapes can avoid tooling or font issues in certain pipelines.
Font support still determines the rendered glyph. Emoji presentation, color fonts, baseline alignment, and platform variation make emoji unreliable for precise UI iconography. Always provide meaningful text independently.
Using attr() in content
The attr() function can insert an originating element's attribute value into generated content. A print stylesheet might append a link destination with a[href]::after { content: " (" attr(href) ")"; }. A data attribute can supply a short decorative label.
Attribute-derived text can become stale, untranslated, duplicated, or exposed unexpectedly. Do not place secrets in data attributes, and do not rely on attr-generated content for accessible names. Typed attr capabilities are evolving, so verify support when using attr outside basic content strings.
Quotes and Quotation Marks
Generated content can use open-quote, close-quote, no-open-quote, and no-close-quote with the quotes property. This supports nested quotation marks and language-sensitive designs more effectively than hard-coded curly characters.
Semantic q and blockquote elements should represent quotations. Pseudo-elements can control visual marks, but source markup must still identify quoted content. Test localized quote conventions and avoid duplicate browser-provided marks.
CSS Counters
CSS counters can generate ordered labels through before. A parent resets a counter, repeated items increment it, and counter() or counters() outputs the current value. This supports figure numbers, section labels, and nested outline numbering without hard-coding each number.
Use native ordered lists when the content is fundamentally a sequence. They provide semantics, copy behavior, and accessibility more reliably. Counters are useful for supplementary numbering across arbitrary semantic elements or print-oriented layouts.
Custom List Markers
Before can create custom bullets, but ::marker is often a better fit because it specifically targets list markers while preserving list layout. If authors remove native list-style and generate bullets manually, spacing, wrapping, high contrast, and list semantics need testing.
Keep ul or ol and li markup even when marker appearance changes. A collection of divs with decorative before dots is not automatically a semantic list. Presentation should not replace document structure.
Required Field Indicators
A label may use after to display an asterisk when a related field is required. The visual marker should be driven by truthful markup or a reliable component class, but CSS cannot generally select a previous label from a later required input without structural help or has. A wrapper can expose required state.
The word “required” or an explained symbol should be available to all users. The input needs the native required attribute when applicable, and generated punctuation alone must not carry the complete requirement. Validation must also occur on the server.
Badges and Ribbons
Before or after can render a “New” ribbon, status dot, or corner shape. If “New,” “Sale,” or another status changes user decisions, it is meaningful content and belongs in the DOM or an accessible attribute-backed pattern, not only in CSS. Generated decoration can reinforce the real status label.
Absolute badges need responsive positioning and must avoid clipping long translated text. A real element handles flexible text, accessibility, and localization more reliably than a fixed-size pseudo-element.
Tooltip Arrows
A tooltip arrow is commonly a border triangle or rotated square generated with before or after. The tooltip container establishes position, and the arrow aligns to its edge. Modern CSS anchor positioning may change placement strategies, but the generated arrow remains decorative.
The tooltip itself requires an accessible trigger, keyboard and pointer availability, dismissal behavior, and readable content. An arrow does not create tooltip behavior. Do not put interactive content in a hover-only tooltip or depend on the pseudo-element as its hit target.
Overlays
A full-cover pseudo-element can add a tint, gradient, or hover layer over media without another wrapper. The originating element becomes positioned, and the generated box uses inset zero. Text or controls then need a deliberate higher stacking layer.
Overlays must preserve image visibility when users need to inspect the subject, maintain text contrast, and avoid blocking pointer input. Do not use a dark overlay as a substitute for choosing a legible layout. Test forced colors, print, and reduced-transparency preferences where relevant.
Decorative Borders and Underlines
Pseudo-elements can create animated underlines, corner accents, or partial borders independently of the element's normal border. A navigation link might use after as a transform-scaled underline that expands on hover and focus-visible. The underlying link should remain identifiable without animation.
Use logical dimensions and transform origins for bidirectional support. Ensure focus feedback is visible immediately and does not rely only on a slowly animated line. Reduced-motion preferences may require disabling or shortening movement.
Clearfix as a Historical Pattern
Older float-based layouts often used .group::after { content: ""; display: table; clear: both; } to make a container enclose floated children. This clearfix pattern remains in legacy code but is usually unnecessary in modern flexbox and grid layouts.
When maintaining old pages, understand the pseudo-element before deleting it: it may be the only clearing mechanism. For new layouts, choose flex or grid and avoid generated boxes whose sole purpose is repairing float containment.
Flex and Grid Participation
Generated before and after boxes can become anonymous-like items in an originating element's flex or grid formatting context. They may consume gap, affect item order, or occupy tracks. A decorative pseudo-element added to a flex container can therefore change alignment unexpectedly.
If the decoration should overlay rather than participate, absolute positioning may be more suitable. Inspect layout overlays in developer tools and account for generated items when item counts or nth-child assumptions seem wrong; pseudo-elements do not affect DOM child indexes but can affect rendered layout.
Replaced Elements
Before and after are generally unreliable or unavailable on replaced elements whose content comes from an external object, such as img, video, iframe, and many form controls depending on browser implementation. These elements do not expose ordinary child content around which generated boxes can be inserted.
Wrap the replaced element in a semantic container and apply pseudo-elements to the wrapper. For an image badge or media overlay, figure, picture wrapper, or a component div can provide a stable originating box. Avoid relying on inconsistent input pseudo-element behavior across browsers.
Specificity
Pseudo-elements contribute to the type category of selector specificity. p::before has 0-0-2: one count for p and one for before. .note::before has 0-1-1: one class and one pseudo-element. The double colon does not create class-level weight.
Pseudo-classes before the pseudo-element add according to their rules. .button:hover::after has two class-category counts and one type-category count. Cascade layers, importance, and source order still affect which declarations win.
Combining with Pseudo-Classes
Generated boxes can respond to state: .toggle[aria-expanded="true"]::after can rotate a decorative chevron, while .link:focus-visible::after can change an underline. The originating element must expose truthful state and remain fully operable.
State changes often animate transform or opacity. Keep transitions short, preserve reduced-motion preferences, and make sure the resting and changed states are understandable without the decorative pseudo-element.
Inheritance and currentColor
Inheritable properties flow from the originating element to before and after. This includes color, font, and line-height unless overridden. The currentColor keyword lets borders, fills, shadows, and backgrounds follow the computed text color, simplifying hover, theme, and forced-color adaptation.
Inheritance can also cause surprises when generated icons adopt a font or line height intended for text. Set display, dimensions, font family, and alignment explicitly when consistent geometry matters.
Custom Properties
Pseudo-elements can consume custom properties defined on the originating element or inherited from ancestors. A component may expose --accent, --icon-size, or --overlay-opacity so themes and variants customize decoration without overriding the complete pseudo-element rule.
Provide fallbacks and document the public variables. Custom properties make pseudo-elements flexible, but they do not solve missing semantics or unstable layout. Keep token names meaningful and avoid leaking private implementation details unnecessarily.
Animations and Transitions
Before and after can be transitioned and animated like generated boxes. Transform, opacity, background, border, and other animatable properties support indicators, spinners, underlines, and reveal effects. Avoid animating large overlays or expensive filters without measurement.
Motion must not be the only state signal. Respect prefers-reduced-motion, keep focus indicators immediate, and avoid infinite decorative animation that distracts from reading. A real progress indicator requires semantics and status updates in addition to a spinning pseudo-element.
Responsive and Logical Properties
Use logical properties such as inline-size, block-size, margin-inline, inset-inline-start, and border-inline-end so decorations adapt to writing direction and mode. Fixed left and right offsets can place badges or icons incorrectly in right-to-left interfaces.
Test text enlargement, translated content, narrow containers, and dynamic wrapping. A pseudo-element positioned against one line of English may overlap a two-line label in another language. Prefer flexible layout participation when decoration must move with content.
Accessibility and Generated Content
Assistive technology treatment of generated content varies with browser, platform, content type, and accessibility tree implementation. Some text may be announced, ignored, duplicated, or presented without the intended context. Therefore, essential instructions, labels, validation messages, status, and controls belong in HTML.
Decorative pseudo-elements should not interfere with focus, hit targets, text selection, or high-contrast rendering. If removing the stylesheet makes the page confusing or incomplete, the pseudo-element is carrying too much meaning.
Copying, Searching, and Translation
Generated text may not behave like DOM text during copy and paste, browser search, translation, spell checking, content extraction, or automated indexing. Behavior varies, and users may receive text different from what they see. Data produced through attr can also remain untranslated because attributes are not part of a localization workflow.
Use real text for content users need to retain, quote, translate, or search. Pseudo-elements can add visual punctuation or redundant cues, but should not become an invisible content-management layer.
Print Styles
Pseudo-elements can improve print output by appending link destinations, labels, or section numbers, but generated content must be scoped so printed pages remain readable. Long URLs can overflow, repeated icons can waste ink, and screen-only overlays may obscure content in print.
Use print media rules to disable decorative backgrounds and enable only useful generated information. Test actual print preview across representative pages. Essential print data should still originate from reliable document content or attributes.
JavaScript and Pseudo-Elements
Normal DOM queries cannot return a pseudo-element because it is not a DOM node. JavaScript can inspect computed styles with getComputedStyle(element, "::before") and can influence the result by changing classes, attributes, or custom properties on the originating element.
Scripts cannot attach an independent event listener or focus to before or after. If separate interaction is required, create a real button, link, or control. Testing should assert the originating element's state and user-visible result rather than pretending the pseudo-element is an independent node.
Debugging Missing Pseudo-Elements
When before or after does not appear, first confirm the originating selector matches. Check that content generates a box, the element supports generated children, display and dimensions are appropriate, and color is not transparent. For positioned boxes, inspect containing blocks, inset values, overflow clipping, stacking contexts, and z-index.
Browser developer tools often show pseudo-elements beneath their originating element and expose computed styles. Disable declarations incrementally, add a temporary visible background and dimensions, and test on a non-replaced wrapper if browser support is uncertain.
Common Mistake: Omitting content
Authors may set width, height, background, and position but forget content. Without a generating content value, before or after usually does not create a box. For decoration, add content: "". Ensure another rule does not override content to normal or none.
Keep the content declaration close to the base pseudo-element rule so variants change appearance without accidentally controlling existence. State rules should usually transform or recolor an already defined box.
Common Mistake: Adding Essential Text
Generated “Required,” “Error,” “Download,” or pricing labels may be visible but inconsistently announced, copied, translated, or indexed. They can also disappear when CSS fails. Put essential language in semantic HTML and use pseudo-elements only to reinforce it visually.
If an icon conveys a new action or status, include accessible text and a real element. Decoration must remain optional from a meaning perspective.
Common Mistake: Blocking Interaction
A full-size overlay pseudo-element can sit above links, form controls, or selected text. Users may see the control but be unable to click or drag across it. Set pointer-events appropriately, constrain the overlay, and test nested interactive content.
If the overlay itself must respond differently, it should probably be a real element with its own semantics and event behavior. Pseudo-elements are not independent controls.
Common Mistake: Using It on img or input
Generated children on replaced elements and native controls are inconsistent or unsupported. A design may work in one browser and vanish in another. Wrap the element and place the pseudo-element on the wrapper, label, or surrounding component.
Cross-browser tests should include native select, input types, images, video, and high-contrast rendering. Platform controls intentionally retain implementation-specific behavior.
Performance Considerations
Pseudo-elements can avoid purely decorative DOM wrappers and are efficient for ordinary component use. They still create rendered boxes that consume style, layout, paint, compositing, and animation work. A full-screen blurred overlay or thousands of animated generated boxes can be expensive even though the DOM is smaller.
Measure real rendering performance, limit complex effects, and avoid assuming fewer nodes always means negligible cost. Maintainability and semantics should guide whether decoration belongs in CSS or markup.
Best Practices
Use before and after for decoration, redundant cues, counters, and layout effects tied to one originating element. Always define content deliberately, scope selectors to components, use logical properties, preserve pointer access, and understand containing blocks and stacking contexts.
Keep essential content and interactive controls in HTML. Prefer marker for list markers, wrappers for replaced elements, currentColor and custom properties for themes, and reduced-motion handling for animations. Test screen, print, keyboard, touch, forced colors, zoom, localization, and stylesheet failure.
Interview Questions and Answers
What are before and after? Generated pseudo-elements associated with an originating element, placed before or after its actual content. Are they DOM nodes? No. Why is content needed? It controls generation; an empty string creates a decorative box. What specificity do they add? Type-category specificity. Can JavaScript select them? Not as DOM elements, though computed style can be inspected.
Should essential text be generated? No, because accessibility and content-processing behavior is inconsistent. Why may they fail on images or inputs? Replaced elements and native controls do not reliably support generated child boxes. Can they animate? Yes. What should receive events? The real originating element or another real interactive element, not an independent pseudo-element node.
Practical Review Checklist
- Use a real semantic element for essential content and every interactive control.
- Define
contentexplicitly for generated before and after boxes. - Choose inline, block, flex, grid, or positioned layout deliberately.
- Verify containing blocks, overflow, stacking contexts, and pointer events.
- Use wrappers instead of relying on pseudo-elements on replaced elements.
- Prefer logical properties,
currentColor, and documented custom properties. - Keep generated icons and labels decorative or redundant.
- Respect focus visibility, forced colors, reduced motion, zoom, and localization.
- Test copy, print, search, translation, and stylesheet-failure behavior.
- Inspect generated boxes and computed styles in browser developer tools.
Key Takeaway
::before and ::after generate stylable boxes before and after an element's actual content. With the content property, display, positioning, transforms, counters, attributes, and normal CSS styling, they can produce rich decoration without presentation-only markup. They participate in layout and painting but are not ordinary DOM elements.
The safest boundary is clear: HTML carries meaning and interaction; pseudo-elements carry optional visual enhancement. When authors account for generation, replaced elements, stacking, pointer behavior, specificity, accessibility, print, and responsive content, before and after become precise component tools rather than hidden sources of essential content or blocked controls.