Overflow and z-index

Overflow and z-index solve two different problems that both involve elements going where they should not. Overflow controls what happens when content is larger than its container. Z-index controls the vertical stacking order when elements overlap. Both have well-known gotchas — this article addresses them directly.

Overflow

The overflow property controls what happens when the content of an element extends beyond its boundaries.

overflow: visible

The default. Content that extends past the element’s bounds is visible — it spills out into adjacent areas. This is why text can appear to overlap a sibling element when a container is too small.

overflow: hidden

Content that exceeds the container is clipped — it simply disappears at the edge. This has several uses:

  • Hiding content that exceeds a fixed-height container (like a card with clamped body text)
  • Containing border-radius on child images (overflow: hidden on a rounded card clips the image corners)
  • Establishing a block formatting context, which contains floated children and prevents margin collapsing through the element

overflow: scroll

Always shows scrollbars, even when content fits. Usually not what you want — prefer auto.

overflow: auto

Shows a scrollbar only when the content actually overflows. This is the cleanest choice for scrollable containers: no scrollbar appears unless it is needed.

overflow-x and overflow-y

You can set overflow separately for each axis. overflow-x: hidden is commonly used to prevent horizontal scroll on pages where an animation or element briefly extends off screen. overflow-y: auto is the typical choice for scrollable side panels.

z-index

z-index controls the stacking order of overlapping elements. A higher z-index renders in front of a lower one.

z-index only works on positioned elements

This is the most common source of confusion. z-index has no effect on elements with position: static (the default). To use z-index, the element must have position: relative, absolute, fixed, or sticky — or be a flex/grid item.

Stacking contexts

A stacking context is an isolated z-index universe. Within a stacking context, z-index values only compete against siblings inside the same context. A child inside a stacking context can never visually escape it — no matter how high you set its z-index, it cannot appear in front of elements outside its parent context.

Properties that create a new stacking context:

  • position (not static) with a z-index value other than auto
  • opacity less than 1
  • Any transform (including transform: none being explicitly set)
  • filter (any value other than none)
  • will-change: transform or will-change: opacity
  • isolation: isolate (explicitly creates a context without other side effects)

z-index values and conventions

There are no fixed rules for z-index numbers, but consistency matters. A common convention:

Layerz-index range
Page content (default)09
Dropdowns, tooltips1099
Fixed navigation100199
Modals and overlays200299
Toast notifications300399

Leaving gaps between layers gives you room to insert values later without having to renumber everything. Avoid arbitrary large numbers like z-index: 9999 — they indicate lost control of the stacking order, not a solution.

Common z-index failures and fixes

SymptomLikely causeFix
z-index has no effectElement is position: staticAdd position: relative
Element trapped below siblingParent has opacity or transformRemove the side effect or restructure HTML
Modal appears behind sticky navNav z-index higher than modal contextIncrease modal z-index or use a portal
Box shadow clippedAncestor has overflow: hiddenMove shadow to parent or restructure