DOM, CSS, and sandbox
Distinguish DOM ownership, style isolation, and JavaScript realms without confusing them with a security boundary.
Three distinct decisions
App behavior depends on where JavaScript runs, who owns the DOM boundary, and which styles it receives. Configuring one dimension does not automatically configure the others.
| JavaScript mode | Execution | Usage and limits |
|---|---|---|
module |
import() in the main window |
Trusted modern modules; does not isolate host globals |
strict |
Actual import inside a hidden iframe | Separate realm and cleanup; requires the module to be loadable there |
eval |
Classic HTML/script path inside an iframe | UMD/IIFE compatibility; does not support ESM modules as strict does |
eval mode runs scripts in the iframe through its HTML compatibility path; for ESM modules, use module or strict according to the realm you need.
Strict and its fallback
await wu.init({
sandbox: 'strict',
strictFallback: false,
apps: [{ name: 'reports', url: '/apps/reports' }],
});
await wu.mount('reports', '#reports-slot');
console.table(wu.getSandboxInfo('reports'));
An import failure inside the iframe produces an error by default. strictFallback: true explicitly authorizes a fallback to eval mode; it does not fix an ESM bundle incompatible with that mode. The per-app option takes precedence over the global option.
getSandboxInfo exposes requestedMode, actualMode, isolationLevel, mounted, nativeShadow, and domOwnership. Compare requestedMode and actualMode when diagnosing a fallback: configuration expresses the preference, and state describes the mode running that app.
Who owns the DOM
domOwnership |
Result |
|---|---|
wu |
WU creates the outer ShadowRoot and gives the renderer an inner root |
app |
The renderer manages its own boundary, useful for LitElement |
light |
Mounting without WU’s outer ShadowRoot |
An explicit value wins. Otherwise, nativeShadow: true selects app, and false selects wu; without either, framework: 'lit' selects app, and all others select wu.
The name app expresses a responsibility, not a guarantee that the component actually creates Shadow DOM. If the renderer does not create its own boundary, its content remains in the DOM tree it received.
Style distribution
Configure the application manifest:
{
"name": "reports",
"entry": "assets/reports.js",
"styleMode": "own-only"
}
| Value | Meaning |
|---|---|
shared |
The bridge allows external style distribution into the ShadowRoot |
isolated / none |
Does not inject additional external styles |
fully-isolated / own-only |
Injects styles identified as belonging to that app |
The bridge observes changes and attributes styles to applications. For a stylesheet created by host code, wu.tagStyleAsApp(element, appName) is available. Do not assume a global shell CSS rule will cross a ShadowRoot. Inherited custom properties and inheritable CSS properties can still affect content appearance.
The sandbox receives styleMode from the manifest. Put the style decision there; the same-named field in top-level host configuration does not replace that value during mounting.
The actual trust boundary
The iframe itself documents that it is same-origin and redirects node creation to the host document. A node can expose ownerDocument.defaultView, through which a script can reach the host. Hiding parent or top does not close that path.
Use these modes to coordinate trusted code, globals, and cleanup. Running untrusted code requires another boundary, such as an isolated origin and a restricted protocol; the runtime’s same-origin iframes retain potential host access.
Containers and layout
A slot must be the renderer’s exclusive territory. In React/Vue, place the loading message beside the mount node: if the shell reconciles its children inside the same node, it can erase the tree WU inserted. The repository’s specific Vue test reproduces this silent failure.
fullBleed in host configuration decides whether the microfrontend fills the slot height and takes responsibility for its scrolling. If content should flow with the document, keep the default behavior. Slot height and layout remain the shell’s responsibility.
Implementation references
Modes: src/core/wu-core.js, src/core/wu-core.js, src/core/wu-core.js, src/core/wu-core.js; ownership: src/core/wu-sandbox.js; same-origin limit: src/core/wu-iframe-sandbox.js. Tests: tests/unit/sandbox-honesty.test.js, tests/unit/native-shadow-sandbox.test.js, tests/unit/sandbox-container-ownership.test.js, tests/unit/style-own-only.test.js, tests/unit/style-cross-app.test.js, tests/unit/vue-slot-mount-target.test.js.
In strict, parent.postMessage honors targetOrigin and delivers the sandbox window as event.source. The internal __wuHostPostMessage access was removed. This identity fix does not turn the same-origin iframe into isolation against hostile code.