Skip to content

Build1 publisher3 min readPublished

A modal body's 70vh scroll container clips options a headless dropdown already rendered

A headless dropdown panel is an ordinary painted element, so a modal body set to overflow-y: auto decides how much of it the user ever sees. The escape has to change which element does the clipping.

The Engineer · Build desk

Illustration accompanying A modal body's 70vh scroll container clips options a headless dropdown already rendered

What happened

  • A dev.to walkthrough swapped a native select inside a three-step modal wizard for a headless Listbox whose options panel is absolutely positioned at z-index 50 with its own max height and overflow-auto.
  • Opened near the bottom of the modal, the panel showed its first option in full, cut the second one in half, and did not draw the third at all.
  • Raising the panel's z-index to 9999 left the browser painting the same clipped panel, even though the DOM still listed all three options in their expected positions.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost Leaving the native control gets the styling the design system wants and brings back the ancestor clipping chain that the browser's own dropdown layer was not subject to, so the team that owns the modal now sets the paint boundary of every dropdown inside it.
  • constraint One shared modal shell capped at 70vh with a scrollbar sets a painting boundary for every component placed inside it, and no amount of stacking order inside the component moves that boundary.
  • decision The choice is between a modal body that keeps its scrollbar and a panel that keeps painting inside it; only one of the two can hold, so the panel has to leave that subtree or the height cap has to go.
  • precedent While the customizable select stays an experimental feature, teams that want a styled dropdown keep building their own and keep inheriting this clipping problem with it.

The browser paints a native select's dropdown as its own layer, not as part of your page boxes, so no ancestor's overflow can clip it [1]. The same arrangement keeps most of the parts a developer wants to style outside CSS's reach [2], and MDN says as much when it calls the element hard to style consistently across browsers [3]. According to MDN's customizable-select docs, the practice for years was appearance: none plus styling whatever CSS could still touch, and the customizable version is still landing as an experimental feature, Chrome, 2025 [4].

A headless Listbox trades that layer away. In the dev.to example the options panel is an ordinary element with position absolute, z-index 50, a 15rem max height and its own overflow-auto [5]. Ordinary elements get painted under every ancestor's clip.

MDN's wording for the auto value is short: "Overflow content is clipped at the element's padding box. When overflowing, the element box is a scroll container displaying scroll bars." [6] Two jobs in one sentence. The first draws a clip edge at the padding box, and anything painted past that edge is not drawn at all; the second adds a scrollbar when the element's in-flow content is taller than the box [7]. hidden and scroll clip as well, the newer clip value clips and forbids all scrolling including programmatic, and visible is the only value that does not clip [8].

In-flow is the word that decides this case. An absolutely positioned element is out of flow [9]. The panel never adds to the in-flow height that would produce a scrollbar, and the clip edge applies to it anyway, so there is nothing to scroll and nothing painted past the edge [10].

This is why the z-index experiment failed. Raising it to 9999 produced the same paint [12], because z-index orders painting within a stacking context and does not move the clip edge [15]. The inspector still listed all three options [13].

The clip edge belongs to .modal-body, which sets max-height: 70vh and overflow-y: auto [14]. Setting that overflow to visible restores the paint and removes the scrollbar the height cap exists to produce [16]. The alternative is to keep the scroll and stop painting the panel inside that box.

For the failure to reproduce in your own layout, two things have to hold: the panel resolves its position inside a scroll container, and an ancestor's overflow is set to something other than visible [18]. Both are ordinary in a modal wrapped around a long form. The walkthrough introduces three companions holding one piece of the answer each, and the published text breaks off in the middle of the second, before it names a fix [17].

What to watch

  • Customizable select moving out of experimental status would remove much of the reason to hand-roll the control at all.
  • The remaining sections of the walkthrough, which name the fix the published excerpt breaks off before reaching.
  • Whether headless component libraries change their default panel placement so the panel is not painted inside the nearest scroll container.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories