Skip to content

Fix Modal backdrop #91

@malberts

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

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