# Sub-Compositions
A sub-composition is a separate HTML file embedded in a host composition. HyperFrames loads it, seeks it independently, and composites the result into the host at `data-start`.
## Host Wiring
In the host composition, the sub-composition appears as a clip with `data-composition-src`:
```html
```
- `data-composition-id` on the host must match the internal `data-composition-id` of the file at `data-composition-src`.
- The host clip needs its own `data-start`, `data-duration`, `data-track-index`, `data-width`, `data-height`.
## Sub-Composition File Structure
### Mental model — what the runtime actually does
When a host loads a sub-composition via `data-composition-src`, the runtime:
1. `fetch`es the HTML file.
2. Parses it with `DOMParser`.
3. **Finds the `` element and clones ONLY its contents into the host slot.**
4. Everything **outside** the `` (including the entire ``) is **discarded**.
So `` is not just a wrapper — it is the **transport container**. If a node needs to exist in the live render, it must be inside ``. Full stop.
### File shape
```html
```
Contrast with **standalone** compositions, which put the root directly in `` with no `` wrapper.
## Common pitfalls that pass static checks but break at render
Static file checks cannot prove the **cross-file mount contract**. These failures appear only when the runtime mounts the sub-composition. Watch for them at author time and verify with the pre-render snapshot checklist below.
### Pitfall 1 — `
...
...
```
**Why this happens:** standard HTML conventions tell you to put `
```
**Why this happens:** when sub-compositions are inlined into one composited render, the compiler **scopes each file's CSS to its own `data-composition-id`** so scenes can't leak styles into each other: every rule `S` becomes `[data-composition-id=""] S` (a _descendant_ selector). A rule whose leftmost selector is the **root's own class** (`.frame`) therefore becomes `[data-composition-id=""] .frame`, which cannot match the scoped element itself. `#root` is special-cased by the scoper and keeps matching the root; plain descendant selectors (`.title`) match normally. The per-scene class namespace is also just redundant, since the `data-composition-id` scope already isolates each scene's styles.
**What actually happens today:** the render no longer drops these rules. Since #1886 the producer preserves the authored root as a `data-hf-inner-root` wrapper _inside_ the scoped element, so a class on the root still matches as a descendant and preview and render agree. The regression is pinned by `packages/producer/tests/sub-comp-class-selector/`.
**So why still use `#root`?** Because `lint` rejects the class form outright (`subcomposition_root_styled_by_class`, **error**), and it is the pattern the registry blocks model (e.g. `apple-money-count`). Treat this as a lint constraint you must satisfy, not as a render bug you are avoiding. If you inherited a composition that styles its root by class, it renders correctly; you still have to convert it to `#root` to get a green `lint`.
### Verification checklist before render
```bash
# For every sub-composition file in compositions/:
# 1)