A stack keeps every open view alive — and by default UI Toolkit keeps rendering all of them, even the ones fully covered by the screen on top. Occlusion is the per-destination policy that decides what happens to the views beneath a destination while it's on top of its stack. It's one dropdown on the NavNode.
| Mode | Beneath, during the transition | Beneath, once settled | Use for |
|---|---|---|---|
| Overlay (default) | visible | visible — keeps rendering | modals, toasts, anything see-through |
| Replace | hidden | hidden — popped-style hide | screen-replaces-screen flows |
| Overlay Opaque | visible | display:none — stops rendering, stays open | full-screen takeovers with a solid background |
Occlusion is scoped to the destination's own stack and is ignored for tabs — switching governs a tab's visibility, not occlusion.
The default. The view below stays visible and keeps rendering for as long as the new view sits on top. Correct for anything the user can see through — dim-backdrop modals, toasts, partial overlays — and the right default for everything else, because it can't be visually wrong. Its only cost is performance: a fully covered screen still pays render cost every frame.
The view beneath is hidden (same visual path as a pop) as the new view pushes in, and shown again when the top view leaves. Use it when the incoming screen conceptually replaces the one below and you want the old screen to fade away during the transition — the two views crossfade, so the swap reads as a scene change, not a cover.
Because the view below runs a real hide, its fade events fire (OnFadeOutStart / OnFadeOut) and its input is released like any hidden view.
A render optimization for destinations whose solid background fully covers the screen — a fullscreen inventory over a hub, a settings screen over a pause menu. It behaves like Overlay during transitions and like "stop drawing everything under me" at rest:
display:none. With UI Toolkit that means zero render, zero layout, zero style-evaluation cost for the covered screens.Close, ReplaceTop, a cascade, a channel switch) — the covered views are restored before the outgoing animation runs, so the user always sees live UI behind the departing screen.The covered views stay logically open the whole time: they keep their stack position, their element state (scroll positions, text fields, animators), and — because display:none doesn't stop UI Toolkit bindings — their data bindings keep updating. No lifecycle events fire on them; the reveal is instant and data-fresh. Expect a one-frame layout recalculation on the frame they come back.
Push ──► transition (beneath visible) ──► settled: beneath display:none
│
Pop / Close / ReplaceTop / channel switch ──► hide starts: beneath restored
──► transition plays over live UIAnything beneath visibly vanishes the instant the top view settles — so this mode is only correct when the user genuinely cannot see behind the view:
When in doubt, leave the default. Overlay is never visually wrong; Overlay Opaque saves cost only when the cover is total.
Set Occlusion = Overlay Opaque on the NavNode. The canvas badges it opaque (purple), next to replace / backdrop / seed / multi.
The samples use it where it's valid, if you want live references: in Showcase, the six fullscreen screens over the hub (inventory, mailbox, journeypass, profile, chat, hero.detail); in GameFull, the settings screen. The pause menu and the GameFull inventory deliberately stay Overlay — both use a dim backdrop over the game.
There's also a dedicated stress demo: Showcase/Components/Occlusion/OcclusionDemo.unity stacks three Overlay Opaque screens on a base screen, each with a live timer plus text / toggle / slider / scroll state. Push to the deepest layer, then pop back down — every layer reveals with its timer current and its state untouched, which is the multi-depth state-preservation guarantee in action.
Open the Frame Debugger (or the Profiler's UI Toolkit markers), settle a fullscreen Overlay Opaque, and watch the draw calls for the covered screens disappear. The canvas runtime overlay still shows the covered frames as stacked — occlusion is render-only and doesn't change nav state.
Navigation drives occlusion through UIView.SetRenderOccluded(bool) — a pure display toggle that leaves visibility state, input blocking, and fade state untouched (UIView.IsRenderOccluded reads it back). It's public if you need the same render-only hiding outside the nav stack, but inside a graph, prefer the node policy: the stack handles every reveal path (pop, cascade, replace, channel switches, teardown) for you.
NavNode field referenceOnFadeIn, OnFadeOutStart) occlusion keys offReading along without the package? Occlusion ships with the nav system in Luna — get it on the Asset Store.
Settings
Theme
Light
Contrast
Material
Dark
Dim
Material Dark
System
Sidebar(Light & Contrast only)
Font Family
DM Sans
Wix
Inclusive Sans
AR One Sans
Direction