You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
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.
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
This is the root cause of Stack order Modal component #84, which the 5.2.4 workaround relieved symptomatically; the 6.0 release should retire the workaround in favour of the correct fix.
Cross-cuts with Revisit per-skin module loading mechanism #90 (per-skin module loading): once the modal is correctly placed, the question of which skins should receive modal.vector-fix.css becomes "none" rather than "some" or "all".
Issue #84 describes
.modal-backdroprendering 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 restoringmodal.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:
.modalatz-index: 1050(BS4) /1055(BS5),.modal-backdropat1040/1050. Both versions' docs are explicit: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 pinsz-index: 0; position: relativeon.mw-page-containerand#bodyContent.vector-body, for example — the modal'sz-index: 1050is 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: noneis a workaround, not a fixmodal.vector-fix.cssalso 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.
<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.z-indexbump above plausible skin chrome. Replaces the.modal { top: 60px }hack. Lets the modal heading paint above sticky headers without positional shift.ParserOutputAccesslifecycle. More invasive than (1).With (1) + (2) in place,
modal.vector-fix.csshas 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
modal.vector-fix.cssbecomes "none" rather than "some" or "all".