# Overlay

> Every floating component in NGWR (dialog, drawer, dropdown, select, tooltip, popover, popconfirm, toast) is built on Angular CDK's overlay primitives. `provideWrOverlay()` gives them an overlay container and an `Overlay` instance of their own, so they never share a DOM root — or a z-index stack — with another CDK consumer.

Source: https://ngwr.dev/guides/overlay  
Kind: Core

## Why this exists

CDK creates a single overlay container at the document root, and every CDK consumer (NGWR, Angular Material, NG-ZORRO) attaches its overlays to that one element. Styling collisions are the usual symptom — z-index wars, shared `.cdk-overlay-pane` rules. `provideWrOverlay()` replaces two tokens NGWR components inject instead of CDK's own: `WR_OVERLAY_CONTAINER` with a `WrOverlayContainer` subclass that tags its element `.wr-overlay-container`, and `WR_OVERLAY` with a fresh `Overlay` built in a child injector bound to that container. It also installs `WrVisualViewport`, which publishes `--wr-keyboard-inset` so a bottom-sheet can lift clear of the on-screen keyboard.

```angular-ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideWrOverlay } from 'ngwr/overlay';

bootstrapApplication(AppComponent, {
  providers: [
    provideWrOverlay(),
  ],
});
```

## Scope styles to NGWR overlays

After provideWrOverlay() is registered, the overlay root element has both the standard `.cdk-overlay-container` class and `.wr-overlay-container`. Use the second to scope your overrides:

```scss
// Scope custom overlay styles to NGWR overlays so other libs aren't affected.
.wr-overlay-container .cdk-overlay-pane {
  z-index: 1100;
}
```

## Caveats

CDK's own root `Overlay` and `OverlayContainer` are left untouched, so other libraries keep the container they already had — the isolation is NGWR moving out, not NGWR taking over. What the call does not do is isolate two OTHER libraries from each other. And it is not load-bearing: both tokens fall back to CDK's root instances, so overlays still open without it — they just land in the shared container, and the keyboard inset is never published.

## Focus, on the way in

A modal overlay — `WrDialog`, `<wr-drawer>` and `WrDrawerManager` — records `document.activeElement` before it attaches, installs a CDK focus trap once the content is in the DOM, and moves focus inside. What it moves focus TO is decided in this order.

1. The element carrying `cdkFocusInitial`, if the content has one. It is a plain attribute — no import, no directive — because the trap resolves it with `querySelector`. This is the answer to "focus has to land on Cancel, not on Delete"; nothing needs to call `focus()` after `open()`.
2. Otherwise the first tabbable element inside the overlay, in DOM order.
3. Otherwise nothing moves, and the trap still holds.

Which means the two drawer forms differ, and the difference is DOM order rather than policy: `<wr-drawer>` renders its ✕ in its own template, above the projected content, so the ✕ is the first tabbable and takes focus; `WrDialog` and `WrDrawerManager` append the ✕ AFTER the content portal — so it paints above the panel — and focus lands on the content's first control. Pin it with `cdkFocusInitial` rather than relying on either.

```angular-html
<!-- Inside the dialog's own component template. `cdkFocusInitial` is a plain
     attribute the CDK focus trap looks up — nothing to import. -->
<h2 wrDialogTitle>Delete project</h2>
<p>This cannot be undone.</p>

<footer>
  <button wr-btn cdkFocusInitial [wrDialogClose]="false">Cancel</button>
  <button wr-btn color="danger" [wrDialogClose]="true">Delete</button>
</footer>
```

## Focus, on the way out

Every dismissal path funnels through one `close()`, and that one method destroys the trap, disposes the overlay and then restores focus to the element recorded on open. There is no path that skips it, and no option that turns it off.

| Closed by | Focus goes to | Note |
| --- | --- | --- |
| Escape | The opener | Off with `closeOnEscape: false`. Focus need not be inside the overlay — the CDK keeps one document listener and routes the key to the topmost overlay. |
| The built-in ✕ | The opener | Off with `closable: false`; renamed with `closeLabel`. |
| `[wrDialogClose]` / `ref.close(result)` | The opener | The same method the ✕ calls, so the result is the only difference. |
| Backdrop click | The opener | Off with `closeOnBackdropClick: false`. |

A fifth path is not a dismissal: with `closeOnNavigation` left on, a route change disposes the overlay so a modal cannot outlive the page it was opened over. And **a dismissal is not distinguishable from**`close(undefined)` — both resolve `awaitClose()` with `undefined`, so a dialog that needs to tell "cancelled" from "saved nothing" has to close with a value that says which.

## Components that use the overlay

```angular-ts
// Overlay-based building blocks shipped by the lib:
import { WrDialog }      from 'ngwr/dialog';
import { WrToast }       from 'ngwr/toast';
import { WrDrawer }    from 'ngwr/drawer';
import { WrPopover }   from 'ngwr/popover';  // also covers tooltip via mode="tooltip"
import { WrPopconfirm } from 'ngwr/popconfirm';
import { WrDropdown }  from 'ngwr/dropdown';
import { WrSelect }    from 'ngwr/select';
```
