# Dropdown Portal / Teleport Pattern When a dropdown is clipped by a parent with `overflow: hidden` or trapped inside a stacking context, CSS z-index alone cannot fix it. The solution is to move the dropdown to a portal root at the end of `` and position it with `position: fixed` + `getBoundingClientRect()`. ## Setup Every page needs `` right before ``. This is a portal target — a direct child of `` with no clipping ancestors. ## Vanilla JS Implementation ```javascript function showDropdown(dropdown, anchorInput) { // 1. Move to portal const root = document.getElementById('dropdown-root'); if (root && dropdown.parentElement !== root) { root.appendChild(dropdown); } // 2. Position relative to anchor const rect = anchorInput.getBoundingClientRect(); dropdown.style.position = 'fixed'; dropdown.style.top = (rect.bottom + 4) + 'px'; dropdown.style.left = rect.left + 'px'; dropdown.style.width = rect.width + 'px'; dropdown.style.zIndex = '99999'; dropdown.classList.remove('hidden'); } // 3. Reposition on scroll/resize window.addEventListener('scroll', () => { if (!dropdown.classList.contains('hidden')) { const rect = anchorInput.getBoundingClientRect(); dropdown.style.top = (rect.bottom + 4) + 'px'; dropdown.style.left = rect.left + 'px'; dropdown.style.width = rect.width + 'px'; } }, { passive: true }); ``` ## React Implementation ```tsx import { createPortal } from 'react-dom'; function Dropdown({ open, anchorRect, children }) { if (!open) return null; return createPortal(
{children}
, document.getElementById('dropdown-root')! ); } ``` ## Why z-index alone fails A parent with `overflow: hidden`, `transform`, `will-change`, or `position: relative` creates a new stacking context. The dropdown's z-index is scoped within that parent — it can never escape to be above a sibling element that comes later in the DOM. ## Diagnosis If `z-index: 99999 !important` on the dropdown doesn't fix clipping, and the dropdown is inside any ancestor with `overflow: hidden` or `position: relative`, this is the fix.