What are ::before and ::after used for?

Short answer

::before and ::after insert generated content as the first and last child of an element. They require a content property — without it, nothing renders at all.

css
.badge::before {
  content: '';           /* required, even when empty */
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
}

Forgetting content is the number one reason a pseudo-element "does not work".

#What they are good for

Decoration without markup — the dot above, gradient overlays, custom list bullets, quote marks, corner ribbons.

Overlays that must sit above an image:

css
.hero { position: relative; }
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(transparent, rgb(0 0 0 / 0.6));
}

Larger tap targets without changing the visible size:

css
.small-link::after {
  content: '';
  position: absolute;
  inset: -12px;      /* invisible 44px-ish hit area */
}

#What they must not be used for

Anything a reader needs. Generated content is not reliably exposed to assistive technology, is invisible to search engines, and cannot be selected or copied. Text that carries meaning belongs in the HTML.

Decorative content should be explicitly ignored:

css
.icon::before { content: ''; }              /* fine */
.label::before { content: 'Step: '; }       /* questionable — put it in the markup */

#They do not work on replaced elements

<img>, <input>, <br>, <video> have no children to insert into. Wrap them in a <span> if you need the effect.

#One colon or two?

CSS3 introduced :: to distinguish pseudo-elements from pseudo-classes like :hover. Browsers accept :before for compatibility. Write two colons.