web/src/components/design-system/OverlayLayers.mdx
import { Meta } from "@storybook/addon-docs/blocks";
<Meta title="Design System/Overlay Layers" parameters={{ layout: "fullscreen" }} />
How every overlay in the app stacks: by structure (DOM order), not by z-index.
Overlays such as dialogs, dropdowns, tooltips, toasts, and the in-app
assistant used to portal to <body> and compete for “who is on top” with
hand-picked z-index values. Those values kept escalating from z-50 to z-51
to z-60 to z-[9999], and whoever picked the bigger one won until the next
overlay reset the race. Real bugs followed: toasts hidden behind the trace
peek, the search-bar error tooltip clipped, and the nav dropdown colliding with
the assistant window.
The whole app renders inside #__next, which is its own isolated stacking
context via isolation: isolate. That caps every z-index used inside the app,
so nothing in-app can paint over an overlay. The overlay layer containers are
declared once in _document.tsx as <body> siblings after #__next, so
they paint on top purely by DOM order, where later means on top, and each is
itself an isolated stacking context.
Ordering is the layer's job. Overlays carry no z-index.
LAYER_ORDER in components/ui/layer.tsx is the source of truth;
_document.tsx maps it to the containers.
The layer containers are pointer-events: none, so the empty space around a
non-modal overlay, like the table peek, stays click-through to the app behind
it. Each portaled overlay opts itself back in with one global rule,
[data-overlay-root] > [data-layer] > * { pointer-events: auto; }, so every
overlay is interactive by construction, modal or not.
*.Portal into a layer with
useLayerContainer(name). The ui/* wrappers such as Dialog,
DropdownMenu, and Select already do this, so most code gets it for free.<Layer name="…">.*.Portal fall back to <body>.The @repo/no-overlay-zindex ESLint rule fails the build if a new overlay
reaches for a z-index escape, so the category of bug cannot return. Legit
in-app chrome such as sticky page headers, fixed top banners, and the bulk
action bar keeps its z-index because it lives inside #__next and never
competes with overlays.
Part of the design system. Storybook is, step by step, becoming the home for these design-system decisions.