/** * Svelte action that re-parents a node to document.body (or any selector). * * Use this for popups / dropdowns / tooltips that rely on * `position: fixed` positioning. Any ancestor with `backdrop-filter`, * `transform`, `filter`, `perspective`, `contain: paint`, or * `will-change: transform` becomes the containing block for fixed * descendants — which silently breaks viewport-relative positioning. * * Portalling sidesteps that by detaching the node from the component * tree and appending it to a target outside any such ancestor. * * Usage: *
...
// → document.body *
...
// → custom selector */ export type PortalTarget = string | HTMLElement; export function portal(node: HTMLElement, target: PortalTarget = 'body') { function attach(t: PortalTarget) { const el = typeof t === 'string' ? document.querySelector(t) : t; if (el instanceof HTMLElement) el.appendChild(node); } attach(target); return { update(newTarget: PortalTarget) { attach(newTarget); }, destroy() { node.parentNode?.removeChild(node); }, }; }