In CSS, what actually happens when you set overflow-x: hidden and leave overflow-y as visible on the same element?
answer
- the two axes are not independent
- one value quietly rewrites the other
- the computed value is not what you typed
- a value that never scrolls escapes the rule
- check the other axis first
basics
~20 sYou do not get "clipped horizontally, spilling vertically". Because one axis uses a scrolling value, the visible on the other axis computes to auto, so the element becomes a scroll container in both directions and vertical overflow is clipped and scrollable too.
solid answer
~50 sCSS does not allow a box to be scrollable on one axis and overflowing on the other, so it silently rewrites your declaration: when one of `overflow-x`/`overflow-y` is `visible` and the other is a value that clips and scrolls, the `visible` one computes to `auto`. Writing `overflow-x: hidden` therefore gives you `overflow-y: auto` for free, and the element becomes a scroll container in both axes. The visible symptom is that things which used to escape the box vertically — a dropdown menu, a tooltip, a decorative overhang — are now clipped, and an unexpected vertical scrollbar can appear. The fix is `overflow-x: clip`: because `clip` is not a scrolling value, the paired `overflow-y: visible` survives as specified. `clip` has been supported in every major engine since Safari 16 in 2022. Otherwise, move the horizontal clipping to a wrapper element.
code
css · 8 lines.row-clipped-wrongly {
overflow-x: hidden; /* overflow-y computes to auto */
}
.row-clipped-correctly {
overflow-x: clip;
overflow-y: visible; /* stays visible */
}go deeper
Know that setting only overflow-x does not leave the vertical axis alone: the other axis becomes auto, so the box can clip and scroll vertically too.
State the computed-value rule precisely — visible becomes auto when the other axis clips and scrolls — and name overflow-x: clip as the pairing that legally preserves overflow-y: visible.
Diagnose it from the symptom: a clipped popover or a phantom scrollbar traced back to a utility class several rules away, and explain why paint-order changes cannot rescue a clipped descendant.
Decide how the codebase avoids the trap at scale — whether escaping content lives outside clipping subtrees by construction, and what the house rule is for utility classes that set a single overflow axis.
## The rule `overflow` is a shorthand for two longhands, `overflow-x` and `overflow-y`, and it is tempting to treat them as fully independent. They are not. A box cannot be a scroll container in one axis while letting content spill freely out of the other — the spilling content would have to be painted outside a box whose contents are supposed to be windowed by a scrollport, and there is no coherent way to render that. So the specification resolves the conflict at computed-value time: > If one of `overflow-x` / `overflow-y` is `visible` and the other is neither `visible` nor `clip`, then `visible` computes to `auto`. There is a matching rule for `clip`: if one is `clip` and the other is neither `visible` nor `clip`, the `clip` computes to `hidden`. The practical translation: | You write | You get | |---|---| | `overflow-x: hidden` (y untouched) | `overflow-x: hidden; overflow-y: auto` | | `overflow-x: auto; overflow-y: visible` | `overflow-x: auto; overflow-y: auto` | | `overflow-x: clip; overflow-y: visible` | unchanged — both as specified | | `overflow-x: clip; overflow-y: auto` | `overflow-x: hidden; overflow-y: auto` | ## Why this bites The classic case is a horizontal-scroll strip, a table wrapper, or a row that must not push the page sideways: ```css .toolbar { overflow-x: hidden; /* stop the sideways spill */ } ``` The author's mental model is "clip left/right, leave top/bottom alone". What they actually built is a scroll container. Three things follow: 1. **Vertical escapes are clipped.** A dropdown menu, tooltip, popover or decorative badge that used to hang below the toolbar is now cut off at the padding edge. The element is still positioned exactly where it was — it simply cannot paint outside its scroll container's scrollport any more. 2. **A vertical scrollbar can appear.** Because `overflow-y` computed to `auto`, any content taller than the box now produces a scrollbar you never asked for, which also narrows the scrollport and re-lays-out its contents. 3. **Descendants get a new scroll parent.** Anything that resolves against "the nearest scrolling ancestor" now points at this box rather than the page, so behaviour tied to that relationship changes. Because none of these symptoms mention overflow, the bug is usually diagnosed as "the dropdown is broken" rather than "the parent became a scroll container", and people start reaching for larger z-index values, which cannot help — clipping happens regardless of paint order. ## The fix ```css .toolbar { overflow-x: clip; overflow-y: visible; /* survives, because clip is not a scrolling value */ } ``` `clip` clips without creating a scroll container, and it is explicitly exempt from the rewrite rule, so the pairing you wanted is legal. This is the single most useful thing `clip` added to the language. Do write the `overflow-y: visible` explicitly: it documents the intent, and it protects you from a later shorthand elsewhere in the cascade. If you must support environments without `clip`, the structural fallback is a wrapper: put the horizontal clipping on an outer element, and let the element that needs vertical escape sit outside that clipping context — for example by taking the escaping content out of the clipped subtree entirely. ## Checking your work Because the rewrite happens at computed-value time, inspecting the computed styles is what proves it: you will see `auto` on an axis where your stylesheet says `visible`. If you are ever surprised that a box clips vertically, look at the *other* axis first — the cause is often several rules away, in a utility class that set `overflow-x` for a completely different reason. ## Related gotcha at the root The same rule applies when the values are propagated from the root element to the viewport, which is why `html { overflow-x: hidden }` — a very common attempt to kill horizontal page scroll — turns the viewport into a scroll container in both axes. That is usually harmless because the page scrolls vertically anyway, but it can interfere with layout that assumed the viewport itself was not a clipping context.
- Why does raising the z-index of a clipped dropdown never fix this?Clipping and paint order are different mechanisms. Once an ancestor is a scroll container, its scrollport clips descendants no matter how high they paint within it; `z-index` only reorders painting among siblings in the same stacking context. The dropdown must either stop being inside the clipping box, or the ancestor must stop clipping — `overflow-x: clip` with `overflow-y: visible` is usually the smallest change.
- What happens if you specify overflow-x: clip together with overflow-y: auto?The mirror-image rule applies: when one axis is `clip` and the other is neither `visible` nor `clip`, the `clip` computes to `hidden`. So you get `overflow-x: hidden; overflow-y: auto`, a scroll container in both axes. The `clip`-with-`visible` exemption only holds when the other axis is genuinely non-scrolling.
- How do you confirm this rewrite is what is happening in a real page?Compare what the stylesheet says with the element's computed values: the axis you left as `visible` reads `auto`. The tell in the rendering is a vertical scrollbar or clipped descendant on a box whose CSS only mentions `overflow-x`. Tracking down which rule set that axis — often a shared utility class — is usually the real work.
saying these in an interview costs you the question
- Believes overflow-x and overflow-y are fully independent
- Blames z-index when a dropdown is clipped
- Thinks overflow-x: hidden only affects horizontal content
- Adds a fixed height to 'fix' the surprise scrollbar
- Assumes the computed value always matches the declaration