Skip to content

refactor(overlay): extract generic overlay coordination; drop dom-dialog - #29

Merged
ivanbanov merged 2 commits into
mainfrom
feat/overlay
Jul 19, 2026
Merged

refactor(overlay): extract generic overlay coordination; drop dom-dialog#29
ivanbanov merged 2 commits into
mainfrom
feat/overlay

Conversation

@ivanbanov

Copy link
Copy Markdown
Member

Summary

The overlay family — dialog, drawer, alert-dialog, popover, menu, combobox — shares one coordination problem: when overlays stack, which layer is topmost? The topmost owns Escape, the focus trap, and (when modal) assistive-tech containment. That behavior lived inside @dunky.dev/dom-dialog, named as if it were dialog-specific — but nothing about it is. This extracts it so every upcoming overlay primitive builds on one implementation instead of growing its own.

Two new packages, split along the one seam that actually matters — agnostic decision vs. host effect:

  • @dunky.dev/overlay (core/utils/overlay) — the agnostic half: a stack of open layers and the rule for which is topmost (deepest nesting, open order breaking ties). No DOM, no framework. A future native substrate reuses it unchanged; only the containment/effects get rewritten per host.
  • @dunky.dev/dom-overlay — the DOM realization on top of the kernel: the layer stack wired to aria-hidden + inert containment, the exit window (hideExitingLayer / watchExitAnimation), and initial focus (getInitialFocus).

@dunky.dev/dom-dialog is removed — it was never released (no CHANGELOG, still 0.0.0), so this is a clean deletion, not a breaking change to consumers.

Supersedes #16, which extracted along different lines against a since-reworked dialog and had gone stale (conflicting, built on src/utils/stack.ts that no longer exists).

Changes

  • New @dunky.dev/overlaycreateLayerStack + topmost / isTopmost, generic over the layer payload.
  • New @dunky.dev/dom-overlayregisterLayer / isTopmostLayer (containment sync on stack change), getInitialFocus, watchExitAnimation, hideExitingLayer.
  • @dunky.dev/react-dialog consumes @dunky.dev/dom-overlay (registerDialogregisterLayer, isTopmostDialogisTopmostLayer). Public API and behavior unchanged.
  • Deleted @dunky.dev/dom-dialog and its never-released changeset; repointed the exit-animation changeset to @dunky.dev/dom-overlay.
  • Workspace wiring (tsconfig.json, tsdown.config.ts, lockfile) and a changeset.

Kept deliberately simple: depth-based topmost as today (no speculative path/interact-outside), containment behavior byte-for-byte preserved. dom-focus-trap is untouched.

Test plan

  • pnpm test:ci — 135 tests, 11 files, all passing
  • pnpm typecheck
  • pnpm lint
  • pnpm format:check
  • pnpm build — publint clean for all packages, including the two new ones

🤖 Generated with Claude Code

The layer stack, assistive-tech containment, exit window, and initial focus
were never dialog-specific — only their names were. Extract them so the whole
overlay family (drawer, alert-dialog, popover, menu, combobox) builds on one
implementation:

- @dunky.dev/overlay (core/utils) — the agnostic half: a stack of open layers
  and the topmost rule (deepest nesting, open order breaking ties). No DOM, no
  framework, so a future native substrate reuses it.
- @dunky.dev/dom-overlay — the DOM realization on top: the stack wired to
  aria-hidden/inert containment, hideExitingLayer/watchExitAnimation, and
  getInitialFocus.

@dunky.dev/dom-dialog is removed (never released); @dunky.dev/react-dialog now
consumes @dunky.dev/dom-overlay (registerDialog/isTopmostDialog become
registerLayer/isTopmostLayer). Public API and behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The prose bullets leaked function names and DOM mechanism literals that drift
from the code. Keep the names in the Usage example; describe behavior above it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ivanbanov
ivanbanov merged commit d247991 into main Jul 19, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant