Vize

Dismissable Layer

Headless Dismissable Layer; covers Radix DismissableLayer, React Aria overlay dismissal, Reka UI DismissableLayer.

Package @vizejs/ui/dismissable-layer
Maturity stable
Own the source vize lib pull dismissable-layer
Requires —
Aliases dismissible layer, outside interaction, escape routing, overlay stack
Covers Radix DismissableLayer, React Aria overlay dismissal, Reka UI DismissableLayer

API

createDismissableLayer

Create an SSR-safe, document-scoped dismissal layer for overlays and popups.

function createDismissableLayer( options: DismissableLayerOptions, ): DismissableLayerController

useDismissableLayer

Create, mount-activate, and scope-dispose a dismissable overlay layer.

function useDismissableLayer(options: DismissableLayerOptions): DismissableLayerController

eventElement

Resolve an event target without assuming it comes from the current realm.

function eventElement(value: EventTarget | null): Element | null

readBoolean

function readBoolean(source: unknown, name: string, fallback: boolean): boolean

readRoot

function readRoot(source: unknown): Element | null

readBranches

function readBranches(source: unknown, document: Document | null): readonly Element[]

validateBranches

function validateBranches(source: readonly unknown[], document: Document | null): Element[]

validateOptions

function validateOptions(options: DismissableLayerOptions): void

createPointerDownOutsideEvent

Build an immutable outside pointer event that is stable after native dispatch mutates.

function createPointerDownOutsideEvent( originalEvent: PointerEvent | MouseEvent | TouchEvent, target: Element, ): DismissableLayerPointerDownOutsideEvent

createFocusOutsideEvent

Build an immutable outside focus event that preserves related-target evidence.

function createFocusOutsideEvent( originalEvent: FocusEvent, target: Element, ): DismissableLayerFocusOutsideEvent

createEscapeKeyDownEvent

Build an immutable Escape event before dismissal is committed.

function createEscapeKeyDownEvent( originalEvent: KeyboardEvent, target: Element | null, ): DismissableLayerEscapeKeyDownEvent

createDismissEvent

Build the final immutable dismissal notification.

function createDismissEvent( reason: DismissableLayerDismissReason, originalEvent: Event, target: Element | null, ): DismissableLayerDismissEvent

recomputeDismissableLayers

function recomputeDismissableLayers(document: Document): void

attachDismissableLayer

function attachDismissableLayer(token: DismissableLayerToken, root: Element): void

detachDismissableLayer

function detachDismissableLayer(token: DismissableLayerToken): void

refreshDismissableLayer

function refreshDismissableLayer(token: DismissableLayerToken): void

Behavior

The dismissable layer primitive is a document-scoped overlay foundation. It does not render a component shell or visual styling; consumers keep ownership of source, markup, Teleport placement, inerting, focus guards, and scroll locks while sharing a deterministic dismissal stack.

Scenario Required behavior
Activation activate() joins the root element's Document stack when root resolves to a connected Element; deactivate() and dispose() release ownership exactly once.
Top-layer routing Only the last activated, connected, enabled layer in a document is topmost and eligible for outside pointer, outside focus, and Escape dismissal.
Branches Reactive branches and imperative registerBranch() roots are treated as inside the layer, including portalled content and composed Shadow DOM paths.
Outside pointer A pointer down, mouse down fallback, or touch start outside the top layer emits immutable pointer-down-outside evidence, then onInteractOutside, then onDismiss unless prevented or a callback throws.
Outside focus A focusin outside the top layer emits immutable focus-outside evidence with relatedTarget, then onInteractOutside, then onDismiss unless prevented or a callback throws.
Escape routing An unprevented, non-composing Escape keydown in the document emits immutable escape-key evidence and then onDismiss unless prevented or a callback throws.
Prevention Calling preventDefault() on pointer, focus, or Escape evidence prevents onDismiss for that native event without mutating the original event.
Reactive enablement enabled, outsidePointerDown, outsideFocus, and escapeKey are re-read for every event and during synchronous option refreshes.
Root migration Moving a root within the same document preserves stack order; moving across documents releases old listeners before attaching to the new document.
Disconnected roots Disconnecting a top layer removes its topmost eligibility on the next mutation turn and restores the next eligible parent layer.
SSR and hydration useDismissableLayer() renders deterministic inactive props on the server, touches no global document during render, and activates after client mount without replacing hydrated nodes.
Runtime diagnostics Invalid roots, branches, booleans, callbacks, out-of-scope usage, and post-disposal calls throw stable VIZE_UI_DISMISSABLE_LAYER_* diagnostics.