When a dropdown appears behind another element, inspect its stacking context. When it is cut off, inspect ancestor overflow. When it opens beyond the screen, inspect placement and viewport collisions. Increasing z-index addresses only part of the first problem; it does not solve clipping or calculate a better position.
Start with the visible symptom
| Symptom | Likely area to inspect | First useful change |
|---|---|---|
| Menu behind a neighboring section | Ancestor stacking contexts | Compare the ancestors, not only the menu’s z-index |
| Menu cut off at a container boundary | overflow: hidden, auto, or clip | Remove unnecessary clipping or render outside the clipped ancestor |
| Menu extends beyond the viewport | Alignment, width, and collision handling | Clamp width and use a positioning system that flips or shifts |
| Menu moves away while scrolling | Coordinate system and scroll listeners | Use a positioning library or recompute placement |
See why z-index cannot fix clipping
Example setup: Tailwind CSS 4.1.18, compiled locally, with plain HTML and vanilla JavaScript. These small educational examples are separate from the membership library. Adapt them to your application and test your final content and browser requirements.
Complete HTML, CSS, and JavaScript
Use your compiled Tailwind stylesheet in place of /examples/tailwind.css. The example uses Tailwind CSS 4 utilities and vanilla JavaScript.
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="robots" content="noindex,follow"><title>An overflow clip cannot be fixed with z-index</title><link rel="stylesheet" href="/examples/tailwind.css"><style>body{font-family:system-ui,sans-serif;background:#faf9f6;color:#252824;padding:24px}button{cursor:pointer}button:disabled{cursor:wait;opacity:.6}:focus-visible{outline:3px solid #4f46e5;outline-offset:4px}</style></head><body><button id="fix" aria-pressed="false" class="mb-5 rounded-lg bg-stone-900 px-4 py-2 text-white">Fix the clipping</button>
<div id="boundary" class="relative h-32 overflow-hidden rounded-xl border border-stone-300 bg-white p-4">
<p class="text-sm text-stone-600">This container clips its children.</p>
<div class="absolute top-16 right-4 z-50 w-56 rounded-xl border border-orange-200 bg-orange-50 p-5 shadow-lg"><strong>My dropdown</strong><p class="mt-2">A high z-index still cannot escape overflow: hidden.</p></div>
</div>
<p id="result" role="status" class="mt-20 text-sm">The menu is clipped.</p><script>const fix = document.querySelector('#fix');
const boundary = document.querySelector('#boundary');
fix.addEventListener('click', () => {
const enabled = fix.getAttribute('aria-pressed') !== 'true';
fix.setAttribute('aria-pressed', String(enabled));
boundary.style.overflow = enabled ? 'visible' : 'hidden';
fix.textContent = enabled ? 'Show the clipping' : 'Fix the clipping';
document.querySelector('#result').textContent = enabled ? 'The menu is visible. In a real clipped scroller, use a portal instead.' : 'The menu is clipped.';
});</script></body></html>The dropdown already has a high stacking value. The parent still clips everything outside its rectangle. Changing overflow makes the issue visible immediately. In an actual scrolling panel, removing overflow may break the design; a portal can move the floating content outside that boundary instead.
Understand stacking contexts before choosing a number
A stacking context groups descendants into a separate stacking unit. A child cannot use a large local z-index to outrank an unrelated ancestor’s position in the outer context. Positioned elements with a non-auto z-index, transforms, and opacity below one are examples of conditions that can establish a context. Use browser developer tools to inspect the ancestor chain rather than escalating values to 999999.
MDN’s stacking-context reference documents the other triggers. After removing an accidental context, check the whole page: the transform or opacity may have been serving a deliberate purpose.
Keep menus inside the screen
OpenTailwind’s editor exposed another common case: a toolbar can wrap at smaller widths, leaving a menu aligned against the wrong edge. The useful controls are alignment, collision padding, available height, and maximum width. They solve a placement problem without changing the visual design of the trigger.
For a React project using Radix Popover, a portalled content panel can use these properties. This is a fragment to place inside your existing Popover root, alongside its trigger:
<Popover.Portal>
<Popover.Content
align="start"
sideOffset={8}
collisionPadding={12}
style={{
width: 288,
maxWidth: 'calc(100vw - 24px)',
maxHeight: 'var(--radix-popover-content-available-height)',
overflowY: 'auto',
}}
>
{/* Your controls */}
</Popover.Content>
</Popover.Portal>
A portal alone does not supply collision handling; the positioning implementation does. Follow the Radix Popover documentation for the package version in your project. Choose a menu primitive for a true application menu and a disclosure or navigation pattern for ordinary site links.
Test the awkward positions
- Open the panel near each viewport edge.
- Resize until the toolbar wraps to a second row.
- Try browser zoom and the longest option label.
- Scroll both the document and any nested containers.
- Use the keyboard to open, navigate, dismiss, and return focus.
For a simple site menu, our responsive navbar example provides a smaller starting point. For visual direction, explore the capsule header design.
