Skip to content

Fix Modal backdrop #91

Description

@malberts

Issue #84 describes .modal-backdrop rendering above the modal dialog on Vector 2022, Vector legacy, MonoBook, and Timeless on MediaWiki 1.43+. PR #85 (5.2.4) and its master cross-port #89 ship a workaround restoring modal.vector-fix.css, whose .modal-backdrop { display: none } rule suppresses the backdrop entirely. That's not a root-cause fix; the file has long flagged this as a known issue.

Root cause

Bootstrap's modal stack is calibrated against the root stacking context: .modal at z-index: 1050 (BS4) / 1055 (BS5), .modal-backdrop at 1040 / 1050. Both versions' docs are explicit:

"Modals use position: fixed, which can sometimes be a bit particular about its rendering. Whenever possible, place your modal HTML in a top-level position to avoid potential interference from other elements. You'll likely run into issues when nesting a .modal within another fixed element." — BS 5.3 modal docs (identical wording in BS 4.6)

ModalBuilder::parse() emits the modal container inline in the parser output (trigger HTML and modal container HTML returned concatenated, per PR #75). On skins whose article wrapper establishes a stacking context — Vector 2022 pins z-index: 0; position: relative on .mw-page-container and #bodyContent.vector-body, for example — the modal's z-index: 1050 is sandboxed inside that subtree at root level z=0. The backdrop, appended to <body> at modal-open time, escapes the trap and stacks above the modal.

Chameleon and Medik don't establish such trapping ancestors, so the standard Bootstrap stack works correctly there.

Why display: none is a workaround, not a fix

  • The modal is still trapped in the low-z subtree; the backdrop is just removed instead of correctly stacked underneath.
  • Suppressing the backdrop also suppresses Bootstrap's intended dim-the-page effect on skins where stacking does work correctly (Chameleon, Medik). Since 5.2.0 those skins have been losing a working backdrop unnecessarily.
  • modal.vector-fix.css also includes .modal { top: 60px } to push the dialog clear of sticky skin chrome — another positional hack predicated on hiding the backdrop.

Approaches for the root-cause fix

Bootstrap itself does not teleport modals; framework wrappers (react-bootstrap, ng-bootstrap, BootstrapVue, HeadlessUI…) all add this behaviour because raw Bootstrap relies on the page author honouring the top-level placement rule.

  1. JS-side teleport on init. A small client-side module appends BootstrapComponents modal containers to <body> before Bootstrap shows them (document.body.appendChild(modalEl)). Restores the top-level placement that PR Fix modal trigger not opening modals on MediaWiki 1.43+ #75's inline emission gave up. Roughly 10 lines of JS, Bootstrap-ecosystem-canonical pattern.
  2. Modal z-index bump above plausible skin chrome. Replaces the .modal { top: 60px } hack. Lets the modal heading paint above sticky headers without positional shift.
  3. Server-side re-emission at OutputPage level. Re-introduces a hook-side teleport similar to the pre-PR-Fix modal trigger not opening modals on MediaWiki 1.43+ #75 deferred-content pipeline, but via a path that survives MediaWiki 1.43+'s ParserOutputAccess lifecycle. More invasive than (1).

With (1) + (2) in place, modal.vector-fix.css has nothing left to do — .modal-backdrop { display: none } becomes unnecessary on every skin, and Bootstrap's standard backdrop renders correctly everywhere, restoring the dim-the-page effect on Chameleon/Medik.

Scope

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions