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-radiuson child images (overflow: hiddenon 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 az-indexvalue other thanautoopacityless than 1- Any
transform(includingtransform: nonebeing explicitly set) filter(any value other thannone)will-change: transformorwill-change: opacityisolation: 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:
| Layer | z-index range |
|---|---|
| Page content (default) | 0–9 |
| Dropdowns, tooltips | 10–99 |
| Fixed navigation | 100–199 |
| Modals and overlays | 200–299 |
| Toast notifications | 300–399 |
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
| Symptom | Likely cause | Fix |
|---|---|---|
| z-index has no effect | Element is position: static | Add position: relative |
| Element trapped below sibling | Parent has opacity or transform | Remove the side effect or restructure HTML |
| Modal appears behind sticky nav | Nav z-index higher than modal context | Increase modal z-index or use a portal |
| Box shadow clipped | Ancestor has overflow: hidden | Move shadow to parent or restructure |