CSS: Pseudo-elements

Style specific parts of an element or insert decorative content without touching the HTML.

::before and ::after

::before and ::after insert a virtual element as the first or last child of the selected element. They are not in the HTML, the browser generates them purely from CSS. They must have a content property to appear.

::before and ::after

::before inserts a generated element before the element's content; ::after inserts one after. Both are inline by default and must have content: '' (even if empty) to render.

  • content: ''required even if empty; without it the pseudo-element does not render
  • display: blockset this when you need width, height, or block-level behaviour
  • They are not part of the DOM, they cannot be selected by JavaScript
  • They inherit styles from the parent element
  • Use position: absolute on the pseudo-element with position: relative on the parent to place decorations precisely

::before and ::after

A label is prepended and a decorative mark appended using generated content.

content Property

The content property is exclusive to ::before and ::after. It defines what is inserted: a string, nothing, a counter, or a URL to an image.

content

content can hold a text string, an empty string, a URL, or a counter value. It cannot hold HTML, only plain text and CSS values.

  • content: ''empty string; renders the pseudo-element with no text (common for decorative shapes)
  • content: 'text'inserts the literal string 'text'
  • content: attr(href)inserts the value of the element's href attribute
  • content: counter(section)inserts an auto-incrementing counter value
  • content: url('icon.svg')inserts an image
  • content cannot render HTML tags, use it for plain text or decorative shapes only

content: attr()

The href value is appended after the link text using attr().

::first-letter and ::first-line

::first-letter targets the very first character of an element's text. ::first-line targets the first rendered line of text, which changes dynamically as the viewport resizes.

::first-letter and ::first-line

::first-letter only works on block-level elements. ::first-line targets the first visible line and updates automatically when the container resizes.

  • ::first-letterclassic drop-cap effect; only works on block or inline-block elements
  • ::first-linestyles only the first rendered line of text
  • Both only accept a limited set of CSS properties (font, color, background, text-decoration, etc.)
  • ::first-letter includes leading punctuation before the first letter

::first-letter

The first letter is enlarged and styled to create a drop-cap effect.

::selection

::selection styles the portion of text that the user highlights by dragging their cursor. By default browsers show a blue highlight, you can override the background and text color.

::selection

::selection only accepts color, background-color, and text-shadow. Keep the contrast high so selected text remains readable.

  • background-colorthe highlight colour behind selected text
  • colorthe text colour of the selected characters
  • Only these two properties (plus text-shadow) are supported inside ::selection
  • Apply ::selection globally with *::selection for a site-wide selection colour
  • Ensure sufficient contrast between the selection background and text colour

::selection

Selected text shows a custom steelblue background instead of the default blue.

::placeholder

::placeholder styles the placeholder text inside an input or textarea. By default browsers render placeholder text in a light grey, you can change its color, font, or style.

::placeholder

::placeholder only affects the placeholder text, not the user's input. Keep placeholder text low-contrast compared to real input text so users can tell them apart.

  • Placeholder text should never replace a proper label, it disappears when the user types
  • colorchanges the placeholder text colour
  • font-style: italica common convention to visually separate placeholder from real input
  • opacitysome browsers use opacity to dim the placeholder; set it explicitly if needed

::placeholder

Placeholder text is styled with a custom colour and italic style.

Common Use Cases

Pseudo-elements are most useful for adding decorative details, dividers, badges, icons, and overlays without adding extra HTML elements. Below are two of the most common real-world patterns.

Common Pseudo-element Patterns

These patterns keep the HTML clean by handling purely decorative details in CSS, where they belong.

  • Underline accentuse ::after with a coloured block to create a styled underline beneath a heading
  • Badge / ribbonuse ::before with position: absolute to attach a label to a card
  • Custom list bulletset list-style: none and use ::before with content to draw a custom bullet
  • Overlayuse ::before with position: absolute, inset: 0 and a semi-transparent background on images
  • Always set position: relative on the parent when the pseudo-element uses position: absolute

Heading underline accent

A coloured accent line is drawn under the heading using ::after, no extra HTML needed.

Badge with ::before

A 'Popular' badge is positioned on the card corner using ::before.

Generated Content Best Practices

Pseudo-elements are powerful but should be used thoughtfully. Because they are not real DOM elements, they have limitations that affect accessibility and maintainability.

Best Practices

Generated content via ::before and ::after is invisible to screen readers in most cases. Never use it for meaningful text, only for decorative details.

  • Use pseudo-elements for decorative content only, icons, dividers, shapes, badges
  • Never place important text inside content: '...', screen readers may ignore it
  • Keep content: '' empty when using the pseudo-element purely for visual shapes
  • Do not try to select pseudo-elements with JavaScript, they are not in the DOM
  • Set display: block or display: inline-block when you need width and height on the pseudo-element
  • box-sizing: border-boxapply to pseudo-elements just like regular elements if using padding and border

Custom bullets with ::before

Custom square bullets drawn with ::before, no list-style-image needed.

Knowledge Check

1. What is the correct syntax for a pseudo-element?

2. Which property is required for ::before and ::after to appear?

3. What does ::first-letter target?

4. Which pseudo-element styles the text the user highlights with their cursor?

5. Which pseudo-element targets the placeholder text inside an input?

6. Which statement about ::before and ::after is correct?