---
title: ActiveUix — the composition root
type: reference
audience: human + agent
authority: E1 architecture — how UIX boots, which services it owns, and how it degrades
status: current
source: migrated from src/uix/active-uix/README.md (2026-07-02, docs-book F7.1)
---
# ActiveUix
`active-uix` is UIX's composition root. Its responsibility is not to be another
behavior layer, but to hand `morfo` , `soma` , `sema` and `eidos` the minimal
services they need — without components ever knowing `ActiveApp` directly.
> **Whole-system architecture**: [`architecture/active-architecture.md`](./active-architecture.md).
> **Executable contract**: [`src/uix/contracts.ts`](../../src/uix/contracts.ts), validated by
> [`contracts.test.ts`](../../src/uix/contracts.test.ts).
## Two boot modes
`createActiveUix(options)` — **standalone** . Composes its own runtime:
- creates `logger` , `timers` , `bus` and `prefs` ;
- the `bus` uses `createSvelteEngineBus({ logger, clock: timers.clock })` , same
as `ActiveApp` , so listeners run under `untrack` and never create accidental
reactive dependencies;
- creates `langs` , `dom` (or `disabledDom` when `dom:false` ), `clipboard` ,
`format` and `events` according to the options;
- keeps `portal` as a generic portal target, so each layer adapts it to its own
API (`portalTo`) without coupling `active-uix` to that layer;
- registers the common translations and the per-component catalogs from
`src/uix/langs/components/*` .
`attachActiveUix(app, options)` — **attach** to an external `ActiveApp` :
- reuses `app.prefs` , `app.langs` , `app.dom` , `app.clipboard` and `app.format`
when they exist;
- **requires** `app.langs` and `app.dom` ; if either is missing it throws a
configuration error;
- does not re-subscribe `langs` to `prefs.language` (that connection belongs to
`defineActiveLangs` inside `ActiveApp` );
- registers the common translations and the per-component catalogs;
- does not own the `app` 's lifecycle.
## Minimum contracts per module
> Executable source: [`src/uix/contracts.ts`](../../src/uix/contracts.ts).
| Module | Minimum required | Optional | Fallback when missing | Error when missing |
| --- | --- | --- | --- | --- |
| `ActiveUix` standalone | `langs` config | `clipboard:false` , `format` , `events` , `portal` , `dom:false` | creates `prefs` , core services and, with `dom:false` , a local `disabledDom` | missing `langs` config |
| `ActiveUix` attach | `ActiveApp` core + `langs` , `dom` services | `app.clipboard` , `app.format` , event engine, `portal` | none for required services | missing `langs` or `dom` on the app; the getter of an absent optional service fails explicitly |
| `SomaRuntime` | `dom` from `ActiveUix` | event engine, `langs` , `format` | none of its own | nonexistent morfo/event/part |
| `Sema` direct | `dom` or `projector` when `visual` is active | `sound` , `haptic` , `visual:false` | none of its own for UIX services | `SemaConfigError` without `dom/projector` while visual is active |
| `Eidos` | `dom` when `applyDom` | `langs` , `format` , `prefs` , mode/density sources | `applyDom:false` allows render/serialize without DOM | missing `dom` with `applyDom` active |
| `ADom` direct | caller's target/window/document | breakpoints/window | `disabledDom` only when the caller asks for it | ADom's own errors without a real DOM |
## Ownership and degradation rules
1. **Only composition roots create shared services.** `morfo` , `soma` , `sema` ,
`eidos` and components never create `dom` , `langs` , `prefs` , `format` ,
`clipboard` or equivalents: they receive them from `ActiveUix` .
2. ** `dom:false` only degrades in standalone.** `ActiveUix` exposes a local
`disabledDom` and passes it to `EngineSemantic` too; Sema/events never fall
back to direct DOM writes. In attach there is no compensating creation: if
the app has no `dom` , `attachActiveUix(app)` fails.
3. ** `langs` is not `locale` .** `prefs.language` feeds translations;
`prefs.locale` feeds formats. They never mix.
4. ** `clipboard` is a capability service, not visual DOM.** Standalone creates
it unless `clipboard:false` ; attach consumes it from `app.clipboard` when a
layer asks for it, and fails with an explicit error if it was not declared.
5. ** `prefs.direction` is the effective direction preference**; `html[dir]` is
only its DOM projection.
6. ** `ActiveUix` does not auto-project preferences onto the DOM.** `arts/prefs`
projects the cross-modal attrs (`dir`, `data-motion` , `data-sound` ,
`data-haptic` ) via `createActivePrefsDomProjection` ; `ActiveEidos` projects
the visual ones (`data-theme`, `data-mode` , `data-density` ). The `frontend`
artifact was retired and must not reappear as a `locale` source.
7. ** `ActiveUix` neither creates nor knows `Soma` or `Eidos` .** `Soma.create(...)`
creates its own scope and `Soma.runtime(...)` ; `ActiveEidos.create(...)`
creates the visual scope when the app needs runtime CSS.
## Booting a UIX shell
A shell that uses visual components wires three pieces explicitly:
```ts
const uix = createActiveUix({ langs, prefs: { schema } });
setActiveUix(uix);
Soma.create();
const prefsProjection = createActivePrefsDomProjection({
prefs: uix.prefs,
dom: uix.dom
});
const eidos = ActiveEidos.create({
theme: 'base',
modeSource,
applyDom: true
});
```
`prefsProjection` owns `dir` , `data-motion` , `data-sound` and `data-haptic` .
`ActiveEidos` owns `data-theme` , `data-mode` and `data-density` . Light/dark
mode goes to `ActiveEidos.modeSource` , not to `prefs.theme` — `theme` is not
part of UIX's core prefs preset and writing it raises
`prefs::unknown_dimension` .