5.8 KiB
Packs
A pack is an encapsulated, opt-in collection that sits ABOVE the UIX
layers and OUTSIDE the component canon. It consumes the framework; the
framework never references it. The live inventory is the src/packs/ tree —
never a list in a doc.
Packs exist so that decorative surface area does not dilute the canon: the catalog's value is contract density (events, ARIA, keyboard, tokens other layers consume); a pack's value is parameterized leaf decoration. Mixing the two would put fashion-velocity artifacts inside the doctrine that must age slowly.
The admission rule (canon vs pack)
Canon = anything with contract surface that others consume — semantic events, ARIA/keyboard behavior, state machines, tokens targeted by other recipes. It enters through the 9-phase route and the acceptance matrix.
Pack = a decorative leaf with parameters — nothing else in the system selects against it, listens to it, or inherits from it. It enters through the P contract below, and stays out of
component:audit.
If a pack piece grows contract surface (a real event, an a11y obligation, a token another recipe wants), that is the signal to promote it to the canon through the full route — never to grow the pack's privileges.
Hard boundaries
- Dependency direction is one-way:
src/packs/*may import$uix/*,$scene,$adomand the other arts; nothing undersrc/{arts,libs,uix}may import fromsrc/packs/. Deleting a pack directory must leavenpm run checkgreen — that is the encapsulation proof. - No morfo, no matrix: pack pieces declare no morfo and take no row in
the acceptance matrix. Their DOM is decorative (
aria-hiddenwhere it paints,pointer-events: noneunless an effect opts into pointer input). - Packs are removable by construction — an app that never imports a pack pays zero bytes for it (per-module registration, tree-shaken).
The P contract (the pack quality floor)
A pack is exempt from the acceptance matrix, NOT from discipline. Every pack
ships with these six obligations, guarded mechanically (packs-check):
| P | Obligation |
|---|---|
| P-1 | Every animated piece declares a reduce policy ('static-frame' | 'hide') — no silent default; reduced motion is honored via the injected DOM port, never a raw matchMedia. |
| P-2 | Full teardown: every mount returns a handle whose dispose() releases frames, observers, GL resources and listeners. |
| P-3 | DOM only through the injected port (dom.requestFrame / observeResize / observeIntersection / listen) — raw requestAnimationFrame / ResizeObserver / IntersectionObserver / addEventListener in src/packs/ is an error. |
| P-4 | Color parameters are token-aware: they accept an eidos custom-property name (resolved via eidos.resolveToken, re-resolved on mode change) or a raw CSS color as fallback; hardcoded palettes without a token path are an error for theme-facing params. |
| P-5 | Decorative DOM is aria-hidden="true"; pointer interactivity is opt-in per piece and never traps focus. |
| P-6 | Off-screen and hidden-tab work is paused; a scene budget guards against unbounded concurrent GL contexts (warn via logger). |
The promotion path (the agentic direction)
The first pack (ambient, animated backgrounds) exists ahead of a canonical
consumer: the agent-presence component (reserved name: Aura) that will
materialize the delegate + sustain families — the visual channel is the
ONLY channel delegate activates (see decisions/book-deviations.md D.8),
and today nothing in the catalog expresses it.
To make that promotion cheap, the effects are shared resources, not pack
internals: they live with the runtime ($scene/effects), the pack mounts
them decoratively, and Aura will mount the same resources semantically
(states from the agent lifecycle; intent modulating speed / amplitude / hue —
the visual analog of sound's pitch / gain / contour). Orientative state →
effect mapping recorded in the plan: idle → mesh/fog/grainient · listening →
orb/lumen · thinking → aurora/silk · acting/streaming → threads/waves ·
searching → radar · error (event, not state) → ray. When Aura lands, the
scene runtime is promoted to a uix.scene service; until then it stays a
standalone factory (createEngineScene).
Provenance note
The seed collection (web/routes/demos/animations, 45 effects) is kept
intact as comparative reference material — it is NOT the pack. The pack
implementations are built clean against the P contract; the originals stay to
compare against and are excluded from the framework's guards.