Skip to content

feat(component-library): layer overlays with z-index properties and an inert modal layer - #1397

Closed
Arnold Stoba (arnoldstoba) wants to merge 3 commits into
mainfrom
feat/overlay-layering
Closed

Arnold Stoba (arnoldstoba) wants to merge 3 commits into
mainfrom
feat/overlay-layering

Conversation

@arnoldstoba

@arnoldstoba Arnold Stoba (arnoldstoba) commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What?

  • mt-modal makes the page behind it inert instead of trapping the focus with focus-trap, and returns the focus to the element that opened it.
  • Overlays Meteor renders into <body> (select lists, menus, tooltips, date pickers, snackbar) stay usable while a modal is open.
  • An open select list or tooltip takes Escape before the modal.
  • Overlay z-indexes come from --z-index-* custom properties, with the old values as fallback.

Why?

focus-trap kept the focus inside the modal element, but Meteor renders menus, select lists and date pickers into <body>, outside of it. In a modal like this, the trap and the menu's own focus handling pulled the focus back and forth, so the menu couldn't be used with the keyboard:

<mt-modal-root>
  <mt-modal title="Edit product">
    <template #default>
      <mt-dropdown-menu-root>
        <mt-dropdown-menu-trigger as-child>
          <mt-button>More actions</mt-button>
        </mt-dropdown-menu-trigger>
        <mt-dropdown-menu-portal>
          <mt-action-menu>…</mt-action-menu>
        </mt-dropdown-menu-portal>
      </mt-dropdown-menu-root>
    </template>
  </mt-modal>
</mt-modal-root>

Making the page inert, like a native modal dialog does, blocks the background without fighting the overlays.

How?

The new useModalLayer composable moves the focus into the panel, wraps Tab, closes the topmost layer on Escape and keeps a reference count of what it made inert. When mt-modal opens, it makes every direct child of <body> inert except itself, its backdrop, elements marked data-mt-overlay and elements that are empty at that moment, like a closed date picker's menu container. Overlays opened later from inside the modal are added to <body> after that and stay usable.

Testing?

  • useModalLayer.spec.ts: focus in and out, Tab wrapping, Escape for the topmost layer, inert reference counting.
  • mt-modal.spec.ts: Escape, the inert page and focus return, and a date picker inside the modal that stays usable.

Anything Else?

Please check modals in the Administration. Overlays that other code renders into <body> before a modal opens become inert unless they carry data-mt-overlay. #1395 changes the same lines of mt-snackbar.vue, so whichever merges second needs a small conflict resolution. #1389 is based on this PR and needs a rebase after it is merged.

@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
meteor-docs Ready Ready Preview Oct 6, 2026 12:49pm UTC
meteor-storybook Ready Ready Preview Oct 6, 2026 12:49pm UTC

Request Review

This branch was successfully deployed

2 active deployments
Preview – meteor-docs — bbaf1b63 Deployed Oct 6, 2026 by vercel[bot]
Preview – meteor-storybook — bbaf1b63 Deployed Oct 6, 2026 by vercel[bot]
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