On this page
Polyfills
Native HTML is the default. When a browser is missing an API this site actually uses, a matching module is loaded — nothing more.
How loading works
-
BaseLayoutimportssrc/polyfills/index.jsonce. That file is the only top-levelawaitentry (Safari mis-handles several modules importing the same awaiting file at once). -
Each feature is detected on the prototype or with
CSS.supports— not onwindow. -
A module is fetched only if the feature is missing and
the document contains markup that needs it
(
[commandfor],dialog[closedby],[data-polyfill-scrollspy], or[interestfor]). - Browsers with full support download the loader, run four cheap checks, and stop. No extra chunks, no listeners.
A browser that already has the APIs downloads only the loader, 0.3 KB gzip. The other modules stay on the server.
| Module | When it loads | JavaScript |
|---|---|---|
index.js | Always. A browser that already has the APIs stops here. | 0.3 KB gzip |
invoker-commands.js | commandForElement is missing and the page has [commandfor]. | 0.8 KB gzip |
dialog-closedby.js | closedBy is missing and the page has dialog[closedby]. | 0.5 KB gzip |
scroll-target-group.js | scroll-target-group is missing and the page has [data-polyfill-scrollspy]. | 0.8 KB gzip |
interestfor.js | interestForElement is missing and the page has [interestfor]. | 2.8 KB gzip |
/**
* Site-wide polyfill loader.
*
* Feature-detect each API and dynamic-import only the missing modules, and
* only when the current document actually uses that API. Browsers with full
* support download this file and make a few cheap checks — no extra chunks,
* no listeners. Import this module once from the document (Safari: do not
* import a top-level-await module from multiple sibling scripts at once).
*/
const polyfills = [];
if (
!('commandForElement' in HTMLButtonElement.prototype) &&
document.querySelector('[commandfor]')
) {
polyfills.push(import('./invoker-commands.js'));
}
if (
!('closedBy' in HTMLDialogElement.prototype) &&
document.querySelector('dialog[closedby]')
) {
polyfills.push(import('./dialog-closedby.js'));
}
if (
(typeof CSS === 'undefined' || !CSS.supports('scroll-target-group: auto')) &&
document.querySelector('[data-polyfill-scrollspy]')
) {
polyfills.push(import('./scroll-target-group.js'));
}
if (
!Object.prototype.hasOwnProperty.call(
HTMLButtonElement.prototype,
'interestForElement',
) &&
document.querySelector('[interestfor]')
) {
polyfills.push(import('./interestfor.js'));
}
await Promise.all(polyfills);
This browser
Status is read with a classic script before the deferred loader runs, so “Native” means the engine — not a polyfill that already patched the prototype.
| Feature | Detect | This browser | Native in |
|---|---|---|---|
command / commandfor | 'commandForElement' in HTMLButtonElement.prototype | Needs JavaScript | Chrome 135+, Firefox 144+, Safari 26.2+ |
closedby | 'closedBy' in HTMLDialogElement.prototype | Needs JavaScript | Chrome 134+, Firefox 141+; Safari still preview |
scroll-target-group | CSS.supports('scroll-target-group: auto') | Needs JavaScript | Chrome/Edge 140+ |
interestfor | HTMLButtonElement.prototype.hasOwnProperty('interestForElement') | Needs JavaScript | Chrome/Edge 142+ |
Invoker commands
File: src/polyfills/invoker-commands.js.
MDN:
Invoker Commands API
.
Used by
Modal,
Offcanvas,
Dropdown,
Popover,
and the Scrollspy dropdown.
-
Loads when
commandForElementis missing and the document has[commandfor]. -
Built-in commands:
show-modal,close,request-close,show-picker,toggle-popover,show-popover,hide-popover(plusopen-popover/close-popoveraliases). Custom--*commands dispatchcommandand do nothing else. -
The native
commandevent does not bubble — listen on the target. -
If a button also has
popovertargetand the Popover API exists, the polyfill skips popover commands so the control is not toggled twice.
Try command / commandfor
<button type="button" class="btn btn-primary" command="show-modal" commandfor="polyfillInvokerModal">
Open with command / commandfor
</button>
<dialog class="modal" id="polyfillInvokerModal" closedby="any" aria-labelledby="polyfillInvokerLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="polyfillInvokerLabel">Invoker commands</h1>
<button type="button" class="btn-close" command="close" commandfor="polyfillInvokerModal" aria-label="Close"></button>
</div>
<div class="modal-body">
This dialog opened through <code>command="show-modal"</code>. Close it with the controls or Escape.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" command="close" commandfor="polyfillInvokerModal">Close</button>
</div>
</div>
</div>
</dialog> Dialog closedby
File: src/polyfills/dialog-closedby.js.
MDN:
HTMLDialogElement.closedBy
.
Used by Modal and Offcanvas static backdrops
(closedby="none").
-
Loads when
closedByis missing and the document hasdialog[closedby]. -
any: backdrop click requests close (cancel, then close). -
none: Escape / close-request is cancelled. -
closerequestand a missing attribute already match native modal Escape, so the polyfill does not extra-handle them.
Try closedby
Open each dialog and try Escape or a click on the backdrop.
<div class="d-flex flex-wrap gap-2">
<button type="button" class="btn btn-primary" command="show-modal" commandfor="polyfillClosedbyAny">
Light-dismiss (closedby="any")
</button>
<button type="button" class="btn btn-primary" command="show-modal" commandfor="polyfillClosedbyNone">
Static (closedby="none")
</button>
</div>
<dialog class="modal" id="polyfillClosedbyAny" closedby="any" aria-labelledby="polyfillClosedbyAnyLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="polyfillClosedbyAnyLabel">closedby="any"</h1>
<button type="button" class="btn-close" command="close" commandfor="polyfillClosedbyAny" aria-label="Close"></button>
</div>
<div class="modal-body">
Backdrop click or Escape requests close.
</div>
</div>
</div>
</dialog>
<dialog class="modal" id="polyfillClosedbyNone" closedby="none" aria-labelledby="polyfillClosedbyNoneLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="polyfillClosedbyNoneLabel">closedby="none"</h1>
<button type="button" class="btn-close" command="close" commandfor="polyfillClosedbyNone" aria-label="Close"></button>
</div>
<div class="modal-body">
Backdrop click and Escape do nothing. Use Close.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" command="close" commandfor="polyfillClosedbyNone">Close</button>
</div>
</div>
</div>
</dialog> scroll-target-group
File: src/polyfills/scroll-target-group.js.
MDN:
scroll-target-group
.
Used by
Scrollspy.
-
Loads when
CSS.supports('scroll-target-group: auto')is false and the document has[data-polyfill-scrollspy]— any element, not only Bootstrap navs. Put the attribute on the fragment-link group. An optional value is a scroller selector (data-polyfill-scrollspy="#pane"); an empty attribute uses the overflow ancestor of the targets. -
An
IntersectionObserverwatches the overflow box that contains the targets and toggles a class named:target-currenton the matching fragment link. -
Styles use
:is(:target-current, .\:target-current)so browsers that do not understand the pseudo-class still match the polyfill class. -
The polyfill does not set
aria-current. Native:target-currentis visual only as well.
Try scroll-target-group
Scroll the pane. The list item that matches the heading in view is highlighted.
One
Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.
Two
Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.
Three
Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.
<div class="row">
<div class="col-4">
<div id="polyfillScrollspyNav" class="list-group" data-polyfill-scrollspy>
<a class="list-group-item list-group-item-action" href="#polyfill-scroll-1">One</a>
<a class="list-group-item list-group-item-action" href="#polyfill-scroll-2">Two</a>
<a class="list-group-item list-group-item-action" href="#polyfill-scroll-3">Three</a>
</div>
</div>
<div class="col-8">
<div class="scrollspy-example" tabindex="0">
<h4 id="polyfill-scroll-1">One</h4>
<p>Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.</p>
<h4 id="polyfill-scroll-2">Two</h4>
<p>Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.</p>
<h4 id="polyfill-scroll-3">Three</h4>
<p>Placeholder copy so the scroll area is taller than the viewport of the example. Keep scrolling to move the current marker.</p>
</div>
</div>
</div> Interest invokers
File: src/polyfills/interestfor.js.
Adapted from Mason Freed’s
interestfor
(BSD-3-Clause).
MDN:
Using interest invokers
.
Used by
Tooltip
hint popovers.
-
Loads when
interestForElementis missing onHTMLButtonElement.prototypeand the document has[interestfor]. -
Hover, keyboard focus, or a long-press on a
<button>shows the target. Long-press on links is not polyfilled (the browser context menu stays). -
Opens a popover with
showPopover({ source })so CSS Anchor Positioning can tether. If the trigger has noanchor-name, the polyfill assigns one. -
Delay is read from
--interest-delay-start/--interest-delay-end. Hint tooltips also set the nativeinterest-delay-*properties to0sunless the author overrides--bs-tooltip-delay-*. -
Does not polyfill
popover="hint". Firefox 153+ has hint natively; Safari does not. Use the tooltiparia-descriptionfallback there. -
A plain hint gets
aria-describedby. Nativeinterestfordoes that association itself — do not add those attributes on the native path.
Try interestfor
Hover or focus the button. In Chrome the engine opens the hint; elsewhere this page loads the polyfill.
<span class="d-inline-block">
<button type="button" class="btn btn-secondary" interestfor="polyfillTip">
Hint tooltip
</button>
<div class="tooltip bs-tooltip-top" popover="hint" id="polyfillTip">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">Opened with interestfor</div>
</div>
</span>