4.6 KiB
CSS Z-Index & Overflow Clipping Debugging
Symptom
A dropdown, tooltip, or popup renders but is cut off / hidden behind elements below it.
Root Cause (almost always one of these)
- Parent
overflow: hidden— the dropdown's parent container clips it via CSS overflow - Missing or broken z-index — the dropdown has no z-index, or its z-index CSS variable is undefined
- Stacking context — a parent creates a new stacking context (via
position: relative + z-index,opacity < 1,transform,filter,will-change) which limits how high the child can stack - Parent position / overflow conflicts —
position: relativeon a grandparent withoverflow: hiddenblocks absolute children even with high z-index - Sibling DOM ordering — the header wrapper has no z-index, so its sibling (main content) paints on top by default
Diagnosis Checklist
1. Check parent overflow
# Search for overflow:hidden on or near the dropdown's container
search_files("overflow-hidden", file_glob="*.html", path="src/")
search_files("overflow-hidden", file_glob="*.css", path="src/")
search_files("overflow.*hidden", file_glob="*.html", path="src/")
2. Check CSS variable definitions
# If the dropdown uses a CSS variable for z-index, verify it's defined
# e.g., `z-index: var(--z-critical) !important;` without `--z-critical: N;` in :root = broken
search_files("--z-", file_glob="*.css", path="src/")
search_files("var\(--z-", file_glob="*.html", path="src/")
search_files("var\(--z-", file_glob="*.js", path="src/")
A CSS variable referenced but never defined evaluates to z-index: invalid — effectively no z-index at all.
3. Check the element's actual z-index
In browser dev tools: inspect the dropdown → computed styles → z-index. If it says invalid or isn't listed, the variable isn't resolving.
4. Check the outer wrapper's position and z-index
Even when the dropdown and its immediate parent have high z-index, the outermost header wrapper may lack position: relative, allowing the next DOM sibling (main content) to paint on top.
Check: Does the <div> that wraps the entire header have position: relative and z-index?
Fixes
Fix 1: Ensure parent overflow is visible
On the dropdown's immediate positioned parent:
overflow: visible !important;
Or remove overflow-hidden from the Tailwind class list if overflow-visible-important is already applied.
Fix 2: Define undefined CSS variables
:root {
--z-critical: 999999;
}
Fix 3: Set z-index on the header card
.header-card {
z-index: 100;
position: relative;
}
This keeps the header above page content, so the dropdown (which is a child) can stack above content below the header.
Fix 4: Remove conflicting Tailwind classes
If a div has both overflow-hidden AND a class that sets overflow: visible !important, remove overflow-hidden — the !important should win in theory, but mobile Safari and some browsers handle this inconsistently.
Fix 5: Set z-index on the outer header wrapper (non-obvious)
Even with Fix 1-4, the dropdown can be covered by page content. This happens when the outermost header div doesn't create a stacking context:
<div class="header-wrapper"> ← NEEDS position:relative + z-index
<div class="header-card z-100"> ← Fix 3 applied
<div id="user-dropdown" z-10001> ← high z-index
</div>
</div>
<div class="main-content"> ← SIBLING of header-wrapper
... ← paints ON TOP without Fix 5
</div>
Fix: Add position: relative; z-index: 1000; to the outermost header wrapper div (not just the header-card inside it). A value of 1000 is enough to beat any content z-index below.
Multi-Layer Verification
After fixing, check ALL pages that share the same component:
- Search for the same CSS classes/IDs across all HTML files
- Check if each page has its own
<style>block that overrides the shared stylesheet - Verify that shared stylesheet changes (e.g.,
style.css) apply to all pages
Common Pattern: Header Dropdown Clipping
<div class="header-card overflow-hidden"> ← PROBLEM: clips children
<div class="user-menu-container relative">
<div id="user-dropdown">...</div> ← gets clipped
</div>
</div>
Fix: Remove overflow-hidden from header-card, add z-index to it, and ensure the dropdown has a defined high z-index. Add .user-menu-container { overflow: visible !important; } for safety. Then add position: relative; z-index: 1000 to the outer header wrapper to beat sibling DOM ordering.