# DOM, CSS, and sandbox

Distinguish DOM ownership, style isolation, and JavaScript realms without confusing them with a security boundary.

<span id="tres-decisiones-distintas"></span>

<span class="section-anchor" id="section-1"></span>

## 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.

<span id="strict-y-su-fallback"></span>

<span class="section-anchor" id="section-2"></span>

## Strict and its fallback

```js
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.

<span id="quién-posee-el-dom"></span>

<span class="section-anchor" id="section-3"></span>

## 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.

<span id="distribución-de-estilos"></span>

<span class="section-anchor" id="section-4"></span>

## Style distribution

Configure the application manifest:

```json
{
  "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.

<span id="la-frontera-de-confianza-real"></span>

<span class="section-anchor" id="section-5"></span>

## 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.

<span id="contenedores-y-layout"></span>

<span class="section-anchor" id="section-6"></span>

## 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.

<span id="referencia-de-implementación"></span>

<span class="section-anchor" id="section-7"></span>

## 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.