docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
title: Eidos Motion — Design and Architecture
type: reference
audience: human + agent
authority: E1/E3 — the two-moment motion model and the engine architecture
status: current
source: migrated from src/uix/eidos/eidos-motion.md (2026-07-02, docs-book F7.3)
---
# Eidos Motion — Design and Architecture
> **Status: the two-moment model — F1– F7 implemented (2026-06-04).**
>
> This document describes Eidos's animation system. The model, anchored in
> the canon (the historical seed `GUIA_IMPLEMENTACION_SEMAUIX.md` §4.3, §7,
> §11), is:
>
> **An interaction has TWO moments, and EACH may carry animation — one, the
> other, or both:**
>
> - **The `--event` moment** (`data-event-*`): the perceptual flourish during
> a sema signal's _hold_. It is the `motion` channel of the **perceptual
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
> signature** (§11), defined **per event** (`family`/`intent`/`event`).
> Transient.
> - **The `--state` moment** (`data-state`): the **transition** to/from a
> persistent condition. Defined **per component** (the `motion` prop).
> Persistent.
>
> This is what makes UIX different from **every** current framework: the
> others collapse presence animation onto a single axis (Chakra: only
> `data-state` + `Presence`). UIX animates both moments and integrates them
> with the perceptual signature — more coherent and richer.
>
> **Implemented (F1– F6):** the two moments (keyframes + `signatures` +
> `presets` + generation + the `EngineMotion` engine + validation + tests +
> the `/temas/animations` demo); the **signature migrated** from `events.css`
> into the registry (`signatures`, F2); the **recipes migrated** to presets
> with per-component duration/easing overrides (F3); the **JS drivers**
> `spring` (physics) / `waapi` / `rect` (FLIP) + `Presence.motion` + a real
> overlay (Dialog) bouncing (F4); **choreography** — stagger + the Material
> `shared-axis`/`fade-through` presets (F5); **token rigor** — the raw
> signature tokenized (long perceptual holds `slower`/`deliberate`/
> `emphatic`/`sustained`), shared-axis on `--motion-distance-xl`, the
> `emphasized` easing, the `[data-motion-set='expressive']` set (F6); the
> **extensible typegen** of preset names — the augmentable registry
> `EidosMotionPresets` (F7). **Roadmap F1– F7 complete.** `data-motion-ref`
> and the "TSC `event:*` scope" are obsolete (THEMING §13/§14, no real use).
>
> **Refactor (post-F5): the motion engine is a SERVICE.** The runtime no
> longer lives in eidos: it was relocated to **`src/arts/motion`** (an _art_
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
> — a pure runtime artifact with no UI or cross-art dependencies), exposed as
> **`uix.motion`** and consumed by **both** layers — soma (`Presence`
> receives `motion: EngineMotion` and calls `motion.run(node, phase)`) and
> eidos (delegates via `eidos.motion` + registers its `css` presets in the
> service at boot). This **dissolves the soma→eidos coupling**: the
> `DialogProps.runMotion` prop and `eidos.motionRunner` disappear — the
> _bridge_ is now `EngineMotion.run`, which reads the node's
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
> `data-animation-style`. Eidos keeps the **CSS generation**
> (`lib/render-css.ts`) + the presets/keyframes/signatures **DATA**
> (`lib/motion/presets/css.ts`); the types, the engine and the drivers
> (`spring`/`waapi`/`rect`) live in `$motion`. See
> [`src/arts/motion/README.md`](../../src/arts/motion/README.md). (Sections
> §5/§6/§9 + the file tree below already reflect the new home.)
> **USAGE extension — 3 domains (2026-06-21).** This document describes the
> ENGINE, which has **two moments** (event/state). At the USAGE level the
> `motion` prop covers **three** domains: **event** (the signature) ·
> **state** (the per-component transition) · **content** (the third: content
> entering/leaving/looping, via `motionAttrs` / `<Motion>` / the
> `spin`/`pulse`/… loops — a USAGE layer **on top of** the state-preset
> machinery, NOT a third engine moment). The dedicated state-domain
> (`data-motion-state`, `<Card>` pilot), the container-driven cascade in both
> directions and the `[data-debug-stagger]` affordance are post-F7. **For the
> task-oriented guide** (recipes · preset catalog · loops · state-domain ·
> staggered cascade · reduced-motion · debug) →
> [`motion-guide.md`](./motion-guide.md). For decisions and history (incl.
> the "coordinated" engine retired in Plan A) →
> [`MOTION_SERVICE_RFC.md`](../../src/uix/eidos/MOTION_SERVICE_RFC.md).
**TL;DR**:
- Two animatable moments: ** `--event` ** (`data-event-*`, the perceptual
signature, `signatures` ) and ** `--state` ** (`data-state`, the
per-component transition, `presets` ). One, the other, or both; they
compose in sequence (`sequence`).
- One registry (`EidosConfig.motion`) with three maps: `keyframes` ,
`signatures` (the event-moment), `presets` (the state-moment).
- `data-state` belongs to **soma** ; `data-event-*` to **sema** ; **eidos
reads both and animates** (§7: the rule is not to OVERWRITE the other
layer's attribute, not "one axis only").
- Usage: the state-moment via the prop `motion="scale-fade"` →
`data-animation-style` ; the event-moment is automatic when the event fires
(the signature).
- Drivers: `css` (the floor, Chakra parity) + JS (`waapi`/`spring`/`rect`/
`svelte` — physics/FLIP/genie/orchestration, beyond Chakra), for either
moment.
---
## Table of contents
1. [Thesis and positioning ](#1-thesis-and-positioning )
2. [The two moments ](#2-the-two-moments )
3. [Motion across the 4 layers ](#3-motion-across-the-4-layers )
4. [Architecture — the two-surface registry ](#4-architecture--the-two-surface-registry )
5. [The types ](#5-the-types )
6. [The `EngineMotion` API ](#6-the-enginemotion-api )
7. [The drivers ](#7-the-drivers )
8. [The DOM contract ](#8-the-dom-contract )
9. [Soma integration (`Presence`) ](#9-soma-integration-presence )
10. [Reduced motion ](#10-reduced-motion )
11. [Primitives and keyframes ](#11-primitives-and-keyframes )
12. [Initial content (signatures + presets) ](#12-initial-content-signatures--presets )
13. [Per-component defaults ](#13-per-component-defaults )
14. [Where the code lives ](#14-where-the-code-lives )
15. [The event-moment: from `events.css` to `signatures` ](#15-the-event-moment-from-eventscss-to-signatures )
16. [Comparison with Chakra UI v3 ](#16-comparison-with-chakra-ui-v3 )
17. [Naming decisions ](#17-naming-decisions )
18. [Implementation phases ](#18-implementation-phases )
19. [Out of scope / deferred ](#19-out-of-scope--deferred )
---
## 1. Thesis and positioning
An interaction does not have **one** animatable moment; it has **two** , and
they are of different natures:
1. The **perceptual occurrence** — "something just happened" — transient,
with a `hold` , an `intent` and a `sequence` . **Sema** writes it as
`data-event-*` .
2. The **state change** — "this is now open" — persistent, the source of
truth. **Soma** writes it as `data-state` .
The canon makes it explicit (GUIA §4.3): `emerge` = "something enters or
leaves the perceptual field" → it is an **event** , not a state. GUIA §11
defines the **per-event perceptual signature** , where `motion` is one of the
channels (alongside sound/color/presence/haptic): the exit movement of a
`commit.delete + loss` is "withdrawal/descent"; that of a
`signal.alert + threat` is "protruding entrance". Movement is defined **per
event**.
> **UIX's differentiator.** Every current framework animates presence on
> **one axis** (Chakra: `data-state` + `Presence`; Radix/Ark: same). UIX
> distinguishes the two moments and **animates both**, integrating the
> event-moment with the perceptual signature (sound/haptic included). That
> is what makes it more coherent (one model, two sharp moments) and richer
> (the animation of "what happened" is never confused with "what state we
> are in").
**What eidos owns**: the typed DATA registry (`EidosConfig.motion` =
`keyframes` + `signatures` + `presets` ), the CSS generation for both
surfaces, the `motion` prop and the `data-animation-style` attr. The
**execution engine** (`EngineMotion`) is NOT eidos's: it lives in `$motion`
and is consumed via `uix.motion` (eidos delegates + registers its `css`
presets there).
**What it does NOT own**: emitting the signal and its semantic signature →
**sema**; the mount/unmount lifecycle and `data-state` → **soma
`Presence` **; the attrs animated over (`data-state`, `data-side` ,
`data-starting/ending-style` ) → declared in **morfo** ; the `allow` /`reduce`
pref → ** `ActivePrefs` **.
---
## 2. The two moments
| | The ** `--event` ** moment | The ** `--state` ** moment |
| ------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| **Attribute** | `data-event-*` | `data-state` |
| **What it is** | the perceptual occurrence (the signal) | the transition to/from a persistent condition |
| **Its animation** | the **signature's flourish** (settle, pulse, intent-tinted withdrawal, a `present` 's entrance…) | the **presence/layout transition** (scale, slide, grow to the open height…) |
| **Granularity** | **per event** (`family`/`intent`/`event`) — generic, consistent system-wide | **per component** (the `motion` prop) |
| **Attr owner** | sema (stamps it during the `hold` ) | soma (effects) |
| **Nature** | transient (lives the `hold` ) | persistent |
| **In the registry** | `signatures` | `presets` |
| **Canon** | §11 (perceptual signature), §4.3 (emerge), §7.1 | §7.2, §5.2 (`sequence`) |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Each moment may carry animation — one, the other, or both:**
- `press` (a button): only the **event** moment (`contact.press` → squeeze).
No state transition.
- a `dialog` opening: the **event** flourish of `emerge.present` **and** the
**state** transition to `open` .
- a `collapse` : the height transition (**state**) **and/or** the `expand`
**event** flourish.
**They compose in sequence** (GUIA §5.2): `sequence: 'pre'` runs the event's
flourish **before** the state changes (e.g. animate the exit before
closing); `'post'` , after (celebrate after the real result). The
architecture chapter describes it in the causal chain
([`active-architecture.md`](../architecture/active-architecture.md) §5): the
event's animation runs during the `hold` , then the state takes over.
**`data-state` and `data-event-*` never mix** (§7.3): the rule is that
**sema does not overwrite** state attributes and vice versa (ownership). It
does NOT say only one may animate — **eidos reads both and animates both** .
What is forbidden is stepping on the other's name, not animating over both
axes.
test(motion): pin firma-vs-stagger cascade precedence (F4-D motion decision)
Workflow analysis (state map + 4 dispositions + adversarial stress) resolved the
direction dispute: the stagger/coordinated preset winning the (0,3,0) tie over
the generic commit-affirm firma is the DOCTRINE-CORRECT outcome (RFC B.2 — the
coordinated owns the visual axis, the generic firma is silenced). The earlier
"flip to firma" reading was backwards; that flip contradicts B.2 and its
channels:[] safety net is dormant; @layer sinks as a front-line fix (a semantic
ownership question answered with a global cascade tool + a reduced-motion
!important-inversion a11y risk).
No shipped component shows a defect (dropdown select feedback is sound+haptic
only; the item is fading out at select). The only real issue: the correct
outcome rides on an ACCIDENTAL source-order tiebreak (the explicit neutralization
was retired), undocumented + untested. Execute the proportionate fix:
- Regression test (motion.test.ts) pinning the (0,3,0)==(0,3,0) tie + the
signatures-before-presets emission order + the absence of a (0,4,0)
[data-event^='commit-select'] signature — a HAZARD MONITOR, so a generator
reorder or a widened signature fails loudly, not silently.
- Load-bearing comment at the render-css emission order.
- Doctrine note in motion.md: a known-fragile accident, not a contract; the
structural endpoint ("una firma por evento") + @layer are deferred/tracked.
Zero behavior change; base.css unchanged. Tests 28/28; npm run check src/ = 9
baseline, 0 new.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
> **When both write `animation` on ONE node (KNOWN-FRAGILE).** Eidos animates
> both axes, but a firma and a `--state`/stagger preset both set the single
> `animation` shorthand — only one applies. On a shared node (a `DropdownMenu`
> item that staggers on open/close AND receives a `commit-select` event) both
> selectors compute `(0,3,0)`, so **CSS source order** decides: the generator
> emits all `signatures` before all `presets`, so the later stagger wins and the
> generic firma is masked — directionally the intended outcome (the coordinated
> owns the visual axis, RFC B.2), but produced **accidentally by emission order**,
> NOT by an opt-out (the `channels:[]` / `expression:'none'` source-silence is
> dormant; the old `animation: none !important` neutralization is retired). No
> shipped component shows a defect (e.g. dropdown select feedback is sound+haptic
> only; the masked pulse is the generic fallback and the item is fading out at
> select). This is a **provisional accident, not a contract** — pinned by
> `motion.test.ts` ("pins the firma-vs-stagger cascade precedence") so a generator
> reorder or a future `(0,4,0)` `[data-event^='…']` signature fails loudly. The
> structural fix ("una firma por evento") is
> [RFC §D.4-B](../../src/uix/eidos/MOTION_SERVICE_RFC.md). `@layer` (order-
> independent precedence) is a deferred, tracked end-state — triggered only when a
> coordinated component gains a visual commit channel, `(0,4,0)` signatures ship,
> or `@layer` is adopted for other reasons.
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
> **Child coordination (stagger / cascade) — CLOSED model, RFC §D.11.**
> There was a third "coordinated" axis (`PresenceGroup` / `cascade-*` presets
> over `data-starting/ending-style`); it was **retired** on 2026-06-19. The
> final model: the cascade is NOT a separate system — it is the **`--state`
> moment** (a declared preset) + the **stagger that already existed**
> (`index × --motion-stagger-each`, parallel = 0, cascade = N) + **one
> foundation rule** that writes the index from structure
> (`[data-stagger] > *:nth-child → --motion-stagger-index` /
> `:nth-last-child → -rev`, generated in `lib/render-css.ts`). The event's
> signature (`emerge` → `present-rise`) is only the **container's** flourish
> (generic, over `data-event-*`); the children's timing is **per-component
> realization**, not a sema channel. Three orthogonal axes (sema emits ·
> motion is the engine · eidos materializes) + the full lifecycle:
> [RFC §D.11](../../src/uix/eidos/MOTION_SERVICE_RFC.md).
> **The UNIVERSAL `motion` prop — one selector, three domains (design
> framing, [RFC §D.12]).** After retiring the coordinated axis, ONE system
> remains → **a single `motion` prop** (today `<Cascade>` still uses
> `animation`; it gets unified). The goal: **any** component (not just
> overlays) can receive `motion="X"` from a type-safe registered catalog.
> What the prop means is decided by the **discriminant** "does the animation
> realize a perceptual event?": **event** → the signature rules, the prop is
> an override/violation (types/lint); **state** → it selects the
> state-preset (`data-state`); **content** (no event) → the prop is the
> **primary** path. The sound/haptic coupling applies only to the event
> domain — content ones stay in clean parity. Detail + plan (a)+(b):
> [RFC §D.12](../../src/uix/eidos/MOTION_SERVICE_RFC.md).
> **Structural constraints of the CSS model — [RFC §D.13].** The contracts
> the declarative approach demands: (1) **structure** — `[data-stagger]` ↔
> DIRECT children; `:nth-child` ignores comments/`{#if}` (hardened), but an
> intermediate wrapper element breaks the count → a cascading grouper
> becomes its own scope (`inherits:false` isolates the index);
> `display:contents` without re-scoping = a dead zone. (2) **exit** —
> unit-exit via `Presence` (retain + await; the parent's `opacity` drags the
> children) works today; per-child staggered-exit requires lifecycle JS
> (`PresenceGroup`, deferred) → the container-driven rule is ENTER-ONLY;
> **never raw `{#if}`** on an animated surface. (3) **debug** — the index is
> `inherits:false` (no ancestor can stomp it); everything lives typed in
> Computed; the opt-in `[data-debug-stagger]` mode materializes it (an
> `::after` badge per child with its index, via a counter mirroring
> `:nth-child - 1`).
---
## 3. Motion across the 4 layers
| Layer | What it contributes | Moment |
| --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| **Morfo** | Declares the attrs: `data-state` + states (`open`/`closed`), the **events** (`emerge`/`commit`/`signal` + `sequence` /`persistence`/`intent`), `data-side` /`data-align`, `data-starting/ending-style` . | both |
| **Sema** | Stamps `data-event-*` (`family`/`intent`/`direction`/`phase`/`id`) during the `hold` ; resolves the signature (sound/haptic are runtime channels; ** `motion` /`color`/`presence` are materialized by eidos** reading `data-event-*` ). | `--event` |
| **Soma** | Writes `data-state` via effects; `Presence` keeps the node during the exit and awaits the animation (`getAnimations()` + `Promise.all(finished)` ). Fires the event with its `sequence` . | `--state` (+ fires the event) |
| **Eidos** | `keyframes` + `signatures` (event-moment, over `data-event-*` ) + `presets` (state-moment, over `data-state` ) + **CSS generation** ; delegates the JS engine to ** `uix.motion` ** (the service) and registers its `css` presets there at boot. **Reads both axes and animates.** | both |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
Reduced-motion is already wired: `MotionEffective = 'allow' | 'reduce'`
feat(motion)!: una fuente de motion, y los cuatro ejes visuales SIEMPRE en prefs — eidos no lee medios, nadie en el árbol uix pregunta al SO, ningún esquema del app deja a eidos sin motor (P2 #5)
La preferencia efectiva de motion la decide prefs UNA vez (`resolveMotion`: la
intención `allow|reduce` gana al hint del SO, `system` deriva) y la proyección la
estampa en `<html data-motion>` — desde e3c0899dd antes del primer pintado. Aun
así el árbol la re-derivaba por su cuenta leyendo el SO en crudo en dos capas:
A) CSS de eidos (§61 del changelog): 65 at-rules `@media (prefers-reduced-motion:
reduce)` en 63 ficheros + 29 emitidas por el generador, conviviendo con 14 + 52
selectores `[data-motion='reduce']` («with or without the JS projection»). Dos
fuentes ⇒ un `allow` explícito no llegaba al CSS. Cada bloque pasa a
`[data-motion='reduce'] SEL` con la MISMA declaración y orden (paridad a
máquina: 63 ficheros, 65 bloques, 89 reglas, 178 pares selector-declaraciones,
0 discrepancias); los 3 ficheros con gemelo idéntico se unifican; ningún bloque
usaba `:root`. Los 4 `!important` dentro de bloques migrados QUEDAN con su rival
nombrado (navigation-menu ×2 contra el swap direccional (0,3,0); text-focus
contra un `style:transition` inline; events.css empate (0,3,0) con las firmas
generadas en otra hoja). Generador: los 4 emisores dejan de emitir el media, ink
marks gana su gemelo; `generated/base.css` 29 → 0 media, `--nombres` 8034 /
5751 únicos INTACTOS. GUARD nuevo `reduced-motion-media.test.ts` (at-rule, no
prosa; anti-vacío > 60 ficheros; mordido por mutación). Cambio de veredicto de
cascada MEDIDO en Chrome real por el adversarial: el prefijo suma (0,1,0), una
veintena de variantes más específicas que ganaban al media hoy pierden — y bajo
la media del SO la forma de HEAD NO paraba el anillo del spinner. `data-motion`
tiene TRES dueños (reduce · navigation-menu from/to · tabs fade|slide), valores
disjuntos, forma ancestro obligatoria. Prosa que afirmaba «both» barrida por la
AFIRMACIÓN (tooltip.css, card.css, recipes/base.ts, skin-media-player, events,
float-panel).
B) JS (§62): el motor de motion (`engine-motion.ts:131`), el motor de escena
(`engine-scene.ts:99`), la háptica de sema (`chans/haptic.ts:129`) y OCHO
lecturas en componentes de eidos preguntaban a `ActiveDom.prefersReducedMotion`
(el media en crudo). El puerto correcto YA EXISTÍA sin consumidores:
`MotionSource = Source<MotionEffective>` (`$libs/motion`). La fuente nace en
prefs (`createMotionSourceFromPrefs`, `src/arts/prefs/motion-source.ts`,
precedente `createLocaleSourceFromPrefs`) y la construyen UNA vez las tres
raíces: `createActiveUix`, `attachActiveUix` (fallbacks desde `app.prefs`) y las
fábricas `defineEngineMotion` / `defineEngineScene` / `defineEngineSemantic`
(`coreDependencies: ['prefs']`, forma de format y langs). MUEREN de los puertos
`MotionDom.prefersReducedMotion`, `SceneDom.prefersReducedMotion` y el interface
`HapticChannelDom` entero (un puerto que contesta política es la puerta por
donde vuelve el defecto); `MotionRunOptions.reduced` se queda como PIN por
ejecución. `ActiveEidos.reducedMotion` con dos puertas: con prefs el efectivo;
standalone sigue al SO con acta (sin primer motor no hay segundo, §60). Los
ocho sitios de componentes leen `eidos.reducedMotion`. Hallazgo del lote: DOS
motores de escena por superficie DOM (aura-indicator, pack Ambient) que se
habrían quedado ciegos a la policy `reduce` obligatoria (P-1) EN SILENCIO —
reciben la fuente vía `eidos.reducedMotion`. Deuda nombrada: escena lee la
fuente en el montaje (paridad con la lectura del media que sustituye).
`ActiveDom.prefersReducedMotion` no se toca: es un hecho del SO que alimenta
el ENTORNO de prefs y nada más.
Soma (adenda firmada «a en B»): el último lector de política que preguntaba al
SO, `soma/runtime.svelte.ts:1280` (migración a11y del morfo: `'state'` ⇒
`channels: []` por S5, `'text'` ⇒ región viva, `'focus'` ⇒ foco), lee la
MISMA fuente: `SomaRuntimeBaseSources.motion: MotionSource` OBLIGATORIO como
`dom` (un runtime que no puede responder «¿reducido?» no puede honrar S5 — lo
garantiza el tipo), llenado por `Soma.runtime()` desde `uix.prefs` (esquema sin
`motion` ⇒ permitir, como los motores). La decisión mayor (b) — que el MOTOR
aplique `'state'` en su pasada de reducción y que un `emit()` directo reciba
tratamiento a11y — queda ABIERTA como fila §3.9 de CONTINUE-sema-audit.md, a
ejecutar junto a D-full. `ActiveEidos.reducedMotion` distingue «sin prefs»
(standalone ⇒ SO, §60) de «prefs sin ranura motion» (⇒ permitir, como los
motores): las dos mitades de una UI ya no discrepan bajo un esquema sin la
dimensión. Hallazgo de la adenda: la bolsa `sources` se construye en 96
sitios (3 de producción — menu-dial, metrics, onion-menu — que reciben la
fuente vía `eidos.reducedMotion`, y 93 harnesses cuyo `as unknown as Soma`
CEGABA la comprobación de miembros: 60 tests rojos hasta declarar el `Omit`
real; el tipo hizo su trabajo en el código de producción y era ciego justo
donde se suponía que bastaba). Test real nuevo
`soma/test/reduced-motion-source.svelte.test.ts` (chromium: `Soma.create()`
lee contexto Svelte) con `createActiveUix` + `EngineSemantic` reales;
mutación (el trigger vuelve al dom) ROJA 3/3. Adversarial dirigido de la
adenda: siete defectos cerrados — `src/uix/contracts.ts` declaraba
`requires: ['dom']` para la bolsa de soma y el test no lo asertaba (un guard
que no inspecciona nada pasa) → `['dom', 'motion']` + aserto; la bolsa #97
(`bag-census.test.ts`) sin `motion` bajo un casteo; el camino `'text'` —el
ÚNICO que declaran 9 morfos de producción— sin test (añadido); dos snippets
de docs que ya no compilaban (component-guide A1, morfo.md); cifras del §62.
C) La raíz GARANTIZA los cuatro ejes visuales (cazado por el autor en el docs
site: texto invisible en oscuro). Cuatro layouts congelados componen su propio
esquema de prefs SIN `mode/theme/density/scaling`; un esquema del app
SUSTITUÍA al de la raíz entero y eidos «degradaba defensivamente» a
`mode = 'light'` CONSTANTE ignorando el SO, mientras el shell estampaba su
wrapper en oscuro: tinta de tema claro sobre superficies oscuras. Forma:
`uixVisualPrefsDimensions()` (pura, en `prefs-schema.ts`; el boot compila la
misma) y `createActiveUix` fusiona SIEMPRE `{ ...ejesVisuales, ...esquemaDelApp }`
— el app puede REDEFINIR un eje, nunca omitirlo; sin un solo cast. Eidos deja
de degradar en silencio: `createPrefsPreferenceSource(prefs, fallbacks,
onMissing)` avisa por `uix.logger.warn` por cada ranura ausente (queda solo
para attach con prefs ajenas; docs de attach: el app compone
`uixVisualPrefsDimensions()`). Tests reales (esquema sin ejes + SO oscuro ⇒
`data-mode="dark"`; app que redefine `theme` gana; prefs ajenas ⇒ 4 avisos por
el logger REAL); el viejo test «degrades to the fallbacks» estaba verde POR
COINCIDENCIA (defaults = fallbacks) y se sustituye; mutación (sin fusión) 2
rojos y restaurada. Boot regenerado (15134 B), delta cero ×5 intacto.
`web/routes/uix/+layout@.svelte` (descongelado por orden del autor, +14/−29):
muere el `modeSource` muerto y el `$state` local; el shell LEE la ranura
`mode` y el toggle escribe `uix.prefs.setIntent('mode', …)`; migración única
de `uix-docs-theme` al sobre de prefs (clave borrada). Ledger 95 → 93 (la
entrada de ese fichero desaparece). VERIFICADO EN CHROME por el coordinador:
SO oscuro sin clave ⇒ `<html data-mode="dark">`, wrapper dark, tinta
`oklch(0.95)`; clave vieja `dark` ⇒ intent `dark`; toggle mueve html, wrapper,
tinta e intent en los dos sentidos. Nombrado, no arreglado: el boot compila
siempre el esquema por defecto (un app que REDEFINE un eje resuelve distinto
que el boot; hoy nadie en web/ usa el boot) · los otros tres layouts congelados
recuperan los ejes pero su wrapper sigue en `$state` local · SSR del docs sirve
`light` y la hidratación corrige (previo).
Verificación: A — vitest eidos+value-channels 43/483, eidos:lint 0, paridad 0
discrepancias, mutación del guard 3/3 y 4/4 (adversarial), prettier solo avisos
preexistentes; B — vitest del scope 90/990 y, con la adenda, 242/2395; +26 tests
(motor 5 · escena 2 · háptica 2 · raíz real jsdom 7 · ActiveEidos 7 · fábrica 1 ·
soma 3 — cifras verificadas una a una por el adversarial), 11 dobles re-firmados,
93 harnesses de soma tipados con el `Omit` real, cuatro mutaciones ROJAS (3/2/3 +
la de soma 3/3) y restauradas por sha256, más la mutación de TIPO del adversarial
(la fuente devuelve la intención ⇒ 2 errores nuevos en src/: el puerto es gate de
tipo, no prosa); C — vitest active-uix+eidos+prefs 57/591, boot 8/8 con delta
cero, mutación 2 rojos, verificación en Chrome por el coordinador; todos —
check src/ 0 (ledger 95 → 93: MENGUA), check:gate OK, docs:check OK,
arts:check OK, packs:check OK. Suite completa 457/5336 verde (adversarial A). Adversariales Opus
independientes por lote (informes en el handoff). Constructores + adversariales
Opus 5; la sesión coordina. ⚠ Lección: la cuenta del brief de A («94 en fuentes»)
era un fallo de medida del coordinador (`grep -rh | grep -v generated` filtra
LÍNEAS con esa palabra, no el directorio); el constructor la re-midió porque el
brief lo exigía. ⚠ Dos constructores cortados por límite de sesión y reanudados
tras releer su diff entero (ley de la casa).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 weeks ago
(`libs/motion`), resolved ONCE by prefs (`resolveMotion` folds the OS hint
under the user's intent), projected as `data-motion` by
`ActivePrefsDomProjection` , and handed to every JS runtime as a `MotionSource`
(`createMotionSourceFromPrefs`). The OS hint itself is tracked by
`ReducedMotionTracker` (`arts/adom`) and exposed on
`ActiveDom.prefersReducedMotion.matches` — it feeds the prefs ENVIRONMENT, and
nothing else reads it to decide.
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
## 4. Architecture — the two-surface registry
```
EidosConfig.motion
├── keyframes: { 'fade-in': {...}, 'scale-in': {...}, ... } registered @keyframes
│
├── signatures: { ← the --event MOMENT (the signature, generic per event)
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
│ 'emerge-present': { family:'emerge', event:'emerge-present', keyframes:['fade-in'], ... },
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
│ 'commit-settle': { family:'commit', keyframes:['settle'], ... },
│ 'announce-threat':{ family:'signal', intent:'threat', keyframes:['pulse'], ... }
│ } → generates [data-event-*][data-event-phase='active'] rules
│
└── presets: { ← the --state MOMENT (per-component transition)
'scale-fade': { driver:'css', enter:{...}, exit:{...} },
'slide-fade': { driver:'css', enter:{ bySide }, exit:{ bySide } },
'genie': { driver:'rect', enter, exit } (P3, JS)
} → generates [data-animation-style][data-state] rules
│
▼
uix.motion : EngineMotion (service · arts/motion — resolves, runs JS drivers, honors reduce)
▲ eidos.motion delegates here + registers the css presets at boot
│
▼
Eidos reads data-event-* (signature) + data-state (transition) → animates
```
- **`signatures`** is the generic signature: a `commit` settles the same way
system-wide; an `emerge.present` enters the same way. Sema's packs
(`sema/components/*.ts`) fine-tune a concrete component's signature. It is
what `events.css` used to do by hand (§15).
- **`presets`** is the per-component transition, chosen with the `motion`
prop.
- Theme/app can add or override in both maps.
---
## 5. The types
`$motion` (`src/arts/motion/types.ts`) — relocated to an _art_ .
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
`duration` /`ease` are `string` (decoupled from `DurationKey` /`EaseKey`:
eidos resolves the tokens in its generation layer; the art doesn't know the
scale):
```ts
type ReducePolicy = 'instant' | 'opacity-only' | 'none';
type MotionSide = 'top' | 'right' | 'bottom' | 'left';
type KeyframeName = string; // key into motion.keyframes
type MotionPresetName = string; // the `motion` prop's value; 'none' disables
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// One CSS phase: composed keyframes + tokens + side-awareness.
interface CssPhase {
keyframes: KeyframeName | KeyframeName[]; // comma-composed: ['scale-in','fade-in']
duration?: string; // token key ('moderate'…) or raw ('600ms')
ease?: string;
transformOrigin?: string; // e.g. 'var(--_floating-transform-origin)'
bySide?: Partial< Record < MotionSide , KeyframeName | KeyframeName [ ] > >;
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
// ── The --event moment: the perceptual signature (generic per event) ──
interface EventSignature {
family?: string; // data-event-family ('emerge' | 'commit' | 'signal' | …)
intent?: string; // data-event-intent (valenced families)
event?: string | string[]; // data-event name(s)/prefix(es) (['emerge-present','emerge-open'], …)
direction?: string; // data-event-direction ('forward' | 'backward') — sense of travel,
// decided per emit. A REFINEMENT, never a matcher on its own.
keyframes: KeyframeName | KeyframeName[];
duration?: string; // token key OR raw hold ('600ms', outside the scale)
ease?: EaseKey;
fill?: 'none' | 'forwards' | 'backwards' | 'both';
reduce?: ReducePolicy;
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
// ── The --state moment: per-component preset (the `motion` prop) ──
interface CssStatePreset {
driver: 'css';
enter?: CssPhase; // [data-state='open']
exit?: CssPhase; // [data-state='closed']
reduce?: ReducePolicy;
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
interface JsStatePreset {
// implemented (waapi/spring/rect/svelte)
driver: 'waapi' | 'spring' | 'rect' | 'svelte';
enter?: MotionRun;
exit?: MotionRun;
requires?: ('sourceRect' | 'targetRect' | 'placement')[];
reduce?: ReducePolicy;
fallback?: CssStatePreset; // declared; NOT auto-applied today
// (the spring driver honors ctx.reduced itself)
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
type StatePreset = CssStatePreset | JsStatePreset;
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
// ── The registry ──
interface MotionConfig {
keyframes?: Record< KeyframeName , KeyframeStops > ;
signatures?: Record< string , EventSignature > ; // the --event moment
presets?: Record< string , StatePreset > ; // the --state moment
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
```
Structural decision: **the event-moment is generic per event**
(`signatures`, by `family` /`intent`), not packaged per preset — because the
signature (§11) is defined per event and must be consistent across
components. Per-component fine-tuning of the event-moment goes in **sema's
packs**. The `motion` prop only chooses the **state preset** .
---
## 6. The `EngineMotion` API
Lives in `$motion` (the `uix.motion` service; `eidos.motion` and
`soma.motion` expose it — the same instance). Registration + the declarative
CSS path + **JS driver execution** . The caller passes `MotionRunOptions`
(`dom`, `reduced` , `side` , `sourceRect` /`targetRect`, `duration` /`ease`) for
the drivers that need them.
```ts
interface EngineMotion {
register(name: string, preset: StatePreset): void;
resolve(name: string): StatePreset | undefined;
has(name: string): boolean;
list(): string[];
// css: declarative (settled handle); js: builds the MotionContext, runs the
// driver, normalizes the return into a handle and tracks it per element.
enter(el: HTMLElement, name: string, opts?: MotionRunOptions): MotionHandle;
exit(el: HTMLElement, name: string, opts?: MotionRunOptions): MotionHandle;
// Presence's bridge (replaces the old `runner` ): reads the node's
// `data-animation-style` and delegates to enter/exit (css → settled handle;
// js → runs the driver).
run(el: HTMLElement, phase: 'enter' | 'exit', opts?: MotionRunOptions): MotionHandle;
cancel(el: HTMLElement): void; // cancels active JS handles
pending(el: HTMLElement): Promise< void > ; // combined finished (for Presence)
dispose(): void; // cancels everything (service cleanup)
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
}
```
- A ** `css` state preset** is declarative: the wrapper sets
`data-animation-style` and the `[data-animation-style][data-state]` CSS
rule + soma's `Presence` do the rest. The runtime doesn't "run" it.
- A **JS state preset** runs the driver (`spring`/`waapi`/`rect`/`svelte`),
normalizes the return (`Animation` | `Animation[]` | `MotionHandle` ) into
one handle, tracks it per element (`cancel`/`pending`), and honors the
`ReducePolicy` .
- The **event-moment** (signatures) does not go through `enter/exit` : it is
generated CSS reacting to `data-event-*` .
### 6.1 — Cleanup policy (WAAPI / spring)
`fill: forwards` leaves `Animation` s dangling in `getAnimations()` → it
would break `Presence` 's exit wait. Discipline: rest states live in the
`[data-state]` CSS; `waapi` presets animate **without `fill: forwards`** and
the runtime `cancel()` s on finish; for rest states not expressible in CSS
(measured FLIP), `commitStyles()` + `cancel()` .
---
## 7. The drivers
They apply to **either moment** (a signature or a state preset).
| Driver | Substrate | For | Beyond CSS |
| --------------------- | ------------------------------------------------------ | ----------------------------------------------- | --------------------------------------------- |
| ** `css` ** | `@keyframes` + tokens (generated) | fade / scale / slide / collapse / the signature | — (the Chakra floor) |
| ** `waapi` ** | `el.animate()` | runtime-computed keyframes | measured distance/size, cancelable |
| ** `spring` ** | a semi-implicit Euler integrator (RAF via `ActiveDom` ) | overshoot/settle, drag, snap-back | real physics — what no cubic-bezier expresses |
| ** `rect` ** | rect measurement (via `ActiveDom` ) → WAAPI | FLIP, shared-element, genie | animating between two real positions |
| ** `svelte` ** (opt-in) | `transition:` / `animate:flip` | `{#each}` list reorders | Svelte's declarative |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Bundle**: `css` (generated) is the floor. `spring` is a **self-contained**
integrator (no dependency — one independent spring per property, stepped by
`ctx.dom.requestFrame` ); `waapi` wraps `el.animate` (a browser API, already
visible to `getAnimations()` ); `rect` (FLIP) composes measurement + WAAPI.
The helpers live in `lib/motion/presets/js.ts` (`spring()` / `waapi()` /
`rect()` ). `svelte` (opt-in) is reserved for `animate:flip` in `{#each}` . An
external adapter (`motion-one`) is opt-in.
**The `svelte` nuance**: `{#if}` transitions would fight soma's `Presence` ;
the core path is WAAPI/spring (composes with `getAnimations` ). `svelte` is
reserved for `animate:flip` in `{#each}` (Svelte owns that lifecycle).
**`rect` batching**: FLIP with N elements does its pass in two phases over a
single `requestFrame` (measure all sources → mutate → measure all targets →
animate). `ActiveDom` already provides `requestFrame` ; the batching belongs
to the driver, not to `ActiveDom` .
---
## 8. The DOM contract
**The `--event` moment** (sema writes it during the hold; eidos reacts):
```css
[data-event='emerge-present'][data-event-phase='active'] {
animation: fade-in …;
}
[data-event-family='commit'][data-event-phase='active'] {
animation: settle …;
}
[data-event-family='signal'][data-event-intent='threat'][data-event-phase='active'] {
animation: pulse …;
}
[data-event-family='shift'][data-event-direction='forward'][data-event-phase='active'] {
animation: shift-cross-forward …;
}
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
```
feat(morfo,sema,eidos): todo evento dice de que familia es, y el cruce por fin se ve
El framework llamaba a la misma cosa de dos maneras: `open` pelado en ocho
componentes y `emerge-open` en tres. No era estetica — un preset de movimiento
engancha el nombre con `^=`, asi que el dialecto pelado no casaba con ninguna
firma y simplemente no animaba, sin romper una sola prueba.
Los 40 nombres sin prefijo pasan a `{familia}-{verbo}[-{matiz}]`: 256 eventos,
256 con prefijo, 0 ambiguos. El plan decia 36 y decia `handle-drag-start`; eran
40, y el canon (c25) dice que esos verbos son `pick` y `drop` — `handle-pick` y
`handle-drop` ya existian en 10 y 4 componentes.
`validateMorfo` cierra la puerta: un `events[].name` que no empiece por su
familia ahora lanza. Visto fallar antes con un nombre pelado inyectado.
Lo que el renombrado destapo, y va aqui tambien:
- La receta del splitter enganchaba `commit-resize`, muerto desde
`bd2e40366`. No casaba desde mayo y nadie chillo. Reescrita por FAMILIA, como
slider y knob, y `eidos-lint` valida ahora el VALOR de `data-event*` contra el
catalogo de morfos — el guard que lo habria cazado en su dia.
- La familia `shift` era muda en el canal visual, contra su propia doctrina
(c27: el cruce debe percibirse; c34 tipifica el «shift invisible»). Su mapa ya
describia la firma que le faltaba y su sonido por defecto es `slide`. Ahora
tiene firma direccional: sexto atributo del sello (`data-event-direction`,
`forward`|`backward`, por emision) y deslizamiento de 320ms RTL-safe por
`:dir()`. Medido: LTR -30px/+30px, RTL los invierte.
- El sello de `shift-navigate` pasa del BOTON al `grid` en los cuatro
calendarios. Medido: el boton recibia `contact-activate` y 8,5 ms despues
—media trama— el `shift-navigate` pisaba la misma ranura y el `press-squeeze`
moria sin pintar un fotograma. Una superficie, una ranura (A-36).
- 101 contradicciones docs<->morfo adjudicadas con evidencia (git log, docs de
decision, el componente vivo). Las docs desfasadas, corregidas; los nueve
DEFECTOS de codigo obsoleto quedan abiertos y sin tocar.
- `SoundDirection` -> `SoundContour`: era un contorno de tono, no un sentido, y
habia tres cosas distintas deletreadas «direction».
check en su linea base con 0 errores nuevos por diferencia de conjuntos ·
docs:check 0/0 · eidos-lint invalid 0 · el censo y las escenas de navegador
medidas con raton real y rAF vivo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
`data-event-direction` (`forward` | `backward` ) is the sixth attr of the stamp
and the only one decided PER EMIT rather than declared in the morfo: the event
name already says `shift-enter-mode` vs `shift-exit-mode` , so the attr carries
only the sense of travel. The `shift` crossing is the first firma to read it —
its keyframes travel the inline axis via `--motion-shift-sign` (+1 `:dir(ltr)` /
− 1 `:dir(rtl)` ), never a physical `translateX` .
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**The `--state` moment** (soma writes it; eidos reacts). The wrapper sets
`data-animation-style` (the `motion` prop); the rest state comes from the
recipe over the same `data-state` :
```css
[data-animation-style='scale-fade'][data-state='open'] {
animation:
scale-in,
fade-in …;
}
[data-animation-style='scale-fade'][data-state='closed'] {
animation:
scale-out,
fade-out …;
}
[data-animation-style='slide-fade'][data-side='top'][data-state='open'] {
animation:
slide-from-bottom,
fade-in …;
}
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
```
Public prop: `motion?: MotionPresetName | 'none'` (per-component default;
`'none'` disables). Placement-aware: it reads `data-side` /`data-align`
refactor(soma)!: §15 — la geometría del posicionador es canal del ANFITRIÓN
Firma A del autor (§14 literal, cuarta aplicación de «la capa es la pluma,
no la dueña»): FloatingContentOpts gana `component` (kebab del morfo, nunca
literal — 11 hilos de creación en 9 anfitriones) y las medidas por instancia
del posicionador aterrizan como `--_{host}-floating-{transform-origin,
available-width,available-height,anchor-width,anchor-height}`. El sexto
nombre que el expediente no contaba (`--floating-native-offset`, rama
nativa) queda canal INTERNO de capa (`--_floating-native-offset`: lo escribe
la capa en su propio wrapper, lo leen solo sus reglas foundation). El lector
COMPARTIDO (preset scale-fade de motion) lee el alias neutro
`--_floating-transform-origin` que cada receta anfitriona declara desde su
canal — cableado sliding-indicator, 9 declaraciones. getFloatingContentCSSVars
(export muerto, next-features §13.ii) MUERE con la firma.
Matiz que la ejecución destapó: el content de MENUBAR compone DropdownMenu —
su canal es --_dropdown-menu-floating-* y el alias de menubar se acota al
PANEL (el canal sigue al componente que POSEE la composición flotante);
split-button y palabras leen el canal ajeno de su composición en el punto de
composición, anotado en cada sitio.
Verificado: suites 96+60+78 en verde - eidos-lint x10 con 0 invalid -
reach-floor 5/5 (ledger 0 NEW / 0 STALE) - component:audit 162 PASS / 4
NEEDS-WORK idéntico - check 0 atribuibles POR FICHERO - dinámica CDP 9/9
anfitriones: canal vivo con px por instancia, los nombres VIEJOS computan
VACÍO en todos, positivo exacto (tooltip max-block-size 525.033px == canal
525.0326px; combobox ídem; panel de menubar anchor-width 886px = la barra;
sidebar popout ENGANCHADO 720px/35px), negativo fijando los viejos en :root
= nada se mueve. Trampa reconfirmada: el popout SIN ANCLAR da undefinedpx —
comprobar que la superficie estaba abierta.
Viaja con atribución declarada (acuerdo por canal con la sesión P0
vicen-42, precedente 1cb07c1e1): los tres hunks de fase C en
docs/architecture/soma-architecture.md (runtime.part sin syncAttrs,
partProps = render bag, el párrafo del imperativo) son suyos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
(morfo) + the `--_floating-transform-origin` alias each floating host's recipe
declares from its own `--_{c}-floating-transform-origin` channel (§15).
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**NOT `data-motion` **: that name already has two owners (the
`allow` /`reduce` pref + NavMenu's direction). The engine's attr is
`data-animation-style` (mirroring Chakra's `animationStyle` ). See §17.
---
## 9. Soma integration (`Presence`)
`soma/layers/presence.svelte.ts` already does the lifecycle: on close it
keeps the node, awaits `node.getAnimations()` + `Promise.all(finished)`
(`runId` guard, `enabled` flag), and unmounts. CSS animations (both moments)
and `el.animate()` ones (JS drivers) appear in `getAnimations()` → **they
are awaited with no new plumbing**.
**`Presence.motion`** (F4 · the service refactor) — the hook for drivers NOT
in `getAnimations()` (the `spring` , pure RAF): `Presence` receives
`motion: EngineMotion` and calls ** `motion.run(node, phase)` ** on
enter/exit; `run` starts the JS animation and returns a `MotionHandle` ,
whose `finished` `Presence` awaits ALONGSIDE `getAnimations()` . For a `css`
preset (or a node without `data-animation-style` ), `run` returns an
already-settled handle → the declarative path is unchanged. Soma **does NOT
import eidos**: it consumes the service via `soma.motion` (= `uix.motion` ),
the same instance as `eidos.motion` . Wired in Dialog's provider
(`contentPresence`/`overlayPresence` receive `motion: this.soma.motion` );
the same pattern fits any overlay — just pass `motion` to its `Presence` ,
**no prop and no coupling** (the old `DialogProps.runMotion` /
`eidos.motionRunner` disappeared in the refactor).
**Child orchestration** (pending, being redesigned): it hangs off the
event's signature (sema) materialized by eidos, not off a parallel engine —
see Appendix D of
[`MOTION_SERVICE_RFC.md` ](../../src/uix/eidos/MOTION_SERVICE_RFC.md ).
### 9.1 — SSR / hydration
Overlays mount on open, client-side; motion fires on transitions, not on
first mount (a mounted-open element suppresses the enter). No in-flight
SSR→JS handoff; the CSS `fallback` covers no-JS.
### 9.2 — Exit + a11y: `inert` (soma's)
During the exit the node stays mounted: it must be `inert` /`aria-hidden` and
unfocusable. The morfo's `focus.return` already returns focus; `inert`
during the exit window is a `Presence` /`Dismissal` improvement in soma (the
pattern is already used in `color-field` ).
---
## 10. Reduced motion
feat(motion)!: una fuente de motion, y los cuatro ejes visuales SIEMPRE en prefs — eidos no lee medios, nadie en el árbol uix pregunta al SO, ningún esquema del app deja a eidos sin motor (P2 #5)
La preferencia efectiva de motion la decide prefs UNA vez (`resolveMotion`: la
intención `allow|reduce` gana al hint del SO, `system` deriva) y la proyección la
estampa en `<html data-motion>` — desde e3c0899dd antes del primer pintado. Aun
así el árbol la re-derivaba por su cuenta leyendo el SO en crudo en dos capas:
A) CSS de eidos (§61 del changelog): 65 at-rules `@media (prefers-reduced-motion:
reduce)` en 63 ficheros + 29 emitidas por el generador, conviviendo con 14 + 52
selectores `[data-motion='reduce']` («with or without the JS projection»). Dos
fuentes ⇒ un `allow` explícito no llegaba al CSS. Cada bloque pasa a
`[data-motion='reduce'] SEL` con la MISMA declaración y orden (paridad a
máquina: 63 ficheros, 65 bloques, 89 reglas, 178 pares selector-declaraciones,
0 discrepancias); los 3 ficheros con gemelo idéntico se unifican; ningún bloque
usaba `:root`. Los 4 `!important` dentro de bloques migrados QUEDAN con su rival
nombrado (navigation-menu ×2 contra el swap direccional (0,3,0); text-focus
contra un `style:transition` inline; events.css empate (0,3,0) con las firmas
generadas en otra hoja). Generador: los 4 emisores dejan de emitir el media, ink
marks gana su gemelo; `generated/base.css` 29 → 0 media, `--nombres` 8034 /
5751 únicos INTACTOS. GUARD nuevo `reduced-motion-media.test.ts` (at-rule, no
prosa; anti-vacío > 60 ficheros; mordido por mutación). Cambio de veredicto de
cascada MEDIDO en Chrome real por el adversarial: el prefijo suma (0,1,0), una
veintena de variantes más específicas que ganaban al media hoy pierden — y bajo
la media del SO la forma de HEAD NO paraba el anillo del spinner. `data-motion`
tiene TRES dueños (reduce · navigation-menu from/to · tabs fade|slide), valores
disjuntos, forma ancestro obligatoria. Prosa que afirmaba «both» barrida por la
AFIRMACIÓN (tooltip.css, card.css, recipes/base.ts, skin-media-player, events,
float-panel).
B) JS (§62): el motor de motion (`engine-motion.ts:131`), el motor de escena
(`engine-scene.ts:99`), la háptica de sema (`chans/haptic.ts:129`) y OCHO
lecturas en componentes de eidos preguntaban a `ActiveDom.prefersReducedMotion`
(el media en crudo). El puerto correcto YA EXISTÍA sin consumidores:
`MotionSource = Source<MotionEffective>` (`$libs/motion`). La fuente nace en
prefs (`createMotionSourceFromPrefs`, `src/arts/prefs/motion-source.ts`,
precedente `createLocaleSourceFromPrefs`) y la construyen UNA vez las tres
raíces: `createActiveUix`, `attachActiveUix` (fallbacks desde `app.prefs`) y las
fábricas `defineEngineMotion` / `defineEngineScene` / `defineEngineSemantic`
(`coreDependencies: ['prefs']`, forma de format y langs). MUEREN de los puertos
`MotionDom.prefersReducedMotion`, `SceneDom.prefersReducedMotion` y el interface
`HapticChannelDom` entero (un puerto que contesta política es la puerta por
donde vuelve el defecto); `MotionRunOptions.reduced` se queda como PIN por
ejecución. `ActiveEidos.reducedMotion` con dos puertas: con prefs el efectivo;
standalone sigue al SO con acta (sin primer motor no hay segundo, §60). Los
ocho sitios de componentes leen `eidos.reducedMotion`. Hallazgo del lote: DOS
motores de escena por superficie DOM (aura-indicator, pack Ambient) que se
habrían quedado ciegos a la policy `reduce` obligatoria (P-1) EN SILENCIO —
reciben la fuente vía `eidos.reducedMotion`. Deuda nombrada: escena lee la
fuente en el montaje (paridad con la lectura del media que sustituye).
`ActiveDom.prefersReducedMotion` no se toca: es un hecho del SO que alimenta
el ENTORNO de prefs y nada más.
Soma (adenda firmada «a en B»): el último lector de política que preguntaba al
SO, `soma/runtime.svelte.ts:1280` (migración a11y del morfo: `'state'` ⇒
`channels: []` por S5, `'text'` ⇒ región viva, `'focus'` ⇒ foco), lee la
MISMA fuente: `SomaRuntimeBaseSources.motion: MotionSource` OBLIGATORIO como
`dom` (un runtime que no puede responder «¿reducido?» no puede honrar S5 — lo
garantiza el tipo), llenado por `Soma.runtime()` desde `uix.prefs` (esquema sin
`motion` ⇒ permitir, como los motores). La decisión mayor (b) — que el MOTOR
aplique `'state'` en su pasada de reducción y que un `emit()` directo reciba
tratamiento a11y — queda ABIERTA como fila §3.9 de CONTINUE-sema-audit.md, a
ejecutar junto a D-full. `ActiveEidos.reducedMotion` distingue «sin prefs»
(standalone ⇒ SO, §60) de «prefs sin ranura motion» (⇒ permitir, como los
motores): las dos mitades de una UI ya no discrepan bajo un esquema sin la
dimensión. Hallazgo de la adenda: la bolsa `sources` se construye en 96
sitios (3 de producción — menu-dial, metrics, onion-menu — que reciben la
fuente vía `eidos.reducedMotion`, y 93 harnesses cuyo `as unknown as Soma`
CEGABA la comprobación de miembros: 60 tests rojos hasta declarar el `Omit`
real; el tipo hizo su trabajo en el código de producción y era ciego justo
donde se suponía que bastaba). Test real nuevo
`soma/test/reduced-motion-source.svelte.test.ts` (chromium: `Soma.create()`
lee contexto Svelte) con `createActiveUix` + `EngineSemantic` reales;
mutación (el trigger vuelve al dom) ROJA 3/3. Adversarial dirigido de la
adenda: siete defectos cerrados — `src/uix/contracts.ts` declaraba
`requires: ['dom']` para la bolsa de soma y el test no lo asertaba (un guard
que no inspecciona nada pasa) → `['dom', 'motion']` + aserto; la bolsa #97
(`bag-census.test.ts`) sin `motion` bajo un casteo; el camino `'text'` —el
ÚNICO que declaran 9 morfos de producción— sin test (añadido); dos snippets
de docs que ya no compilaban (component-guide A1, morfo.md); cifras del §62.
C) La raíz GARANTIZA los cuatro ejes visuales (cazado por el autor en el docs
site: texto invisible en oscuro). Cuatro layouts congelados componen su propio
esquema de prefs SIN `mode/theme/density/scaling`; un esquema del app
SUSTITUÍA al de la raíz entero y eidos «degradaba defensivamente» a
`mode = 'light'` CONSTANTE ignorando el SO, mientras el shell estampaba su
wrapper en oscuro: tinta de tema claro sobre superficies oscuras. Forma:
`uixVisualPrefsDimensions()` (pura, en `prefs-schema.ts`; el boot compila la
misma) y `createActiveUix` fusiona SIEMPRE `{ ...ejesVisuales, ...esquemaDelApp }`
— el app puede REDEFINIR un eje, nunca omitirlo; sin un solo cast. Eidos deja
de degradar en silencio: `createPrefsPreferenceSource(prefs, fallbacks,
onMissing)` avisa por `uix.logger.warn` por cada ranura ausente (queda solo
para attach con prefs ajenas; docs de attach: el app compone
`uixVisualPrefsDimensions()`). Tests reales (esquema sin ejes + SO oscuro ⇒
`data-mode="dark"`; app que redefine `theme` gana; prefs ajenas ⇒ 4 avisos por
el logger REAL); el viejo test «degrades to the fallbacks» estaba verde POR
COINCIDENCIA (defaults = fallbacks) y se sustituye; mutación (sin fusión) 2
rojos y restaurada. Boot regenerado (15134 B), delta cero ×5 intacto.
`web/routes/uix/+layout@.svelte` (descongelado por orden del autor, +14/−29):
muere el `modeSource` muerto y el `$state` local; el shell LEE la ranura
`mode` y el toggle escribe `uix.prefs.setIntent('mode', …)`; migración única
de `uix-docs-theme` al sobre de prefs (clave borrada). Ledger 95 → 93 (la
entrada de ese fichero desaparece). VERIFICADO EN CHROME por el coordinador:
SO oscuro sin clave ⇒ `<html data-mode="dark">`, wrapper dark, tinta
`oklch(0.95)`; clave vieja `dark` ⇒ intent `dark`; toggle mueve html, wrapper,
tinta e intent en los dos sentidos. Nombrado, no arreglado: el boot compila
siempre el esquema por defecto (un app que REDEFINE un eje resuelve distinto
que el boot; hoy nadie en web/ usa el boot) · los otros tres layouts congelados
recuperan los ejes pero su wrapper sigue en `$state` local · SSR del docs sirve
`light` y la hidratación corrige (previo).
Verificación: A — vitest eidos+value-channels 43/483, eidos:lint 0, paridad 0
discrepancias, mutación del guard 3/3 y 4/4 (adversarial), prettier solo avisos
preexistentes; B — vitest del scope 90/990 y, con la adenda, 242/2395; +26 tests
(motor 5 · escena 2 · háptica 2 · raíz real jsdom 7 · ActiveEidos 7 · fábrica 1 ·
soma 3 — cifras verificadas una a una por el adversarial), 11 dobles re-firmados,
93 harnesses de soma tipados con el `Omit` real, cuatro mutaciones ROJAS (3/2/3 +
la de soma 3/3) y restauradas por sha256, más la mutación de TIPO del adversarial
(la fuente devuelve la intención ⇒ 2 errores nuevos en src/: el puerto es gate de
tipo, no prosa); C — vitest active-uix+eidos+prefs 57/591, boot 8/8 con delta
cero, mutación 2 rojos, verificación en Chrome por el coordinador; todos —
check src/ 0 (ledger 95 → 93: MENGUA), check:gate OK, docs:check OK,
arts:check OK, packs:check OK. Suite completa 457/5336 verde (adversarial A). Adversariales Opus
independientes por lote (informes en el handoff). Constructores + adversariales
Opus 5; la sesión coordina. ⚠ Lección: la cuenta del brief de A («94 en fuentes»)
era un fallo de medida del coordinador (`grep -rh | grep -v generated` filtra
LÍNEAS con esa palabra, no el directorio); el constructor la re-midió porque el
brief lo exigía. ⚠ Dos constructores cortados por límite de sesión y reanudados
tras releer su diff entero (ley de la casa).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 weeks ago
- **CSS** (both moments): `[data-motion='reduce'] …` rules per policy
(`instant` → `animation: none` ; `opacity-only` → fade only; `none` →
untouched) — and **only** those. Eidos does not read media: the effective
preference is `prefs` ' (`resolveMotion` folds the OS hint, and an explicit
`allow` overrules it) and arrives stamped on `<html>` . A `@media
(prefers-reduced-motion)` in a recipe would be a second source, still
feat(motion)!: una fuente de motion, y los cuatro ejes visuales SIEMPRE en prefs — eidos no lee medios, nadie en el árbol uix pregunta al SO, ningún esquema del app deja a eidos sin motor (P2 #5)
La preferencia efectiva de motion la decide prefs UNA vez (`resolveMotion`: la
intención `allow|reduce` gana al hint del SO, `system` deriva) y la proyección la
estampa en `<html data-motion>` — desde e3c0899dd antes del primer pintado. Aun
así el árbol la re-derivaba por su cuenta leyendo el SO en crudo en dos capas:
A) CSS de eidos (§61 del changelog): 65 at-rules `@media (prefers-reduced-motion:
reduce)` en 63 ficheros + 29 emitidas por el generador, conviviendo con 14 + 52
selectores `[data-motion='reduce']` («with or without the JS projection»). Dos
fuentes ⇒ un `allow` explícito no llegaba al CSS. Cada bloque pasa a
`[data-motion='reduce'] SEL` con la MISMA declaración y orden (paridad a
máquina: 63 ficheros, 65 bloques, 89 reglas, 178 pares selector-declaraciones,
0 discrepancias); los 3 ficheros con gemelo idéntico se unifican; ningún bloque
usaba `:root`. Los 4 `!important` dentro de bloques migrados QUEDAN con su rival
nombrado (navigation-menu ×2 contra el swap direccional (0,3,0); text-focus
contra un `style:transition` inline; events.css empate (0,3,0) con las firmas
generadas en otra hoja). Generador: los 4 emisores dejan de emitir el media, ink
marks gana su gemelo; `generated/base.css` 29 → 0 media, `--nombres` 8034 /
5751 únicos INTACTOS. GUARD nuevo `reduced-motion-media.test.ts` (at-rule, no
prosa; anti-vacío > 60 ficheros; mordido por mutación). Cambio de veredicto de
cascada MEDIDO en Chrome real por el adversarial: el prefijo suma (0,1,0), una
veintena de variantes más específicas que ganaban al media hoy pierden — y bajo
la media del SO la forma de HEAD NO paraba el anillo del spinner. `data-motion`
tiene TRES dueños (reduce · navigation-menu from/to · tabs fade|slide), valores
disjuntos, forma ancestro obligatoria. Prosa que afirmaba «both» barrida por la
AFIRMACIÓN (tooltip.css, card.css, recipes/base.ts, skin-media-player, events,
float-panel).
B) JS (§62): el motor de motion (`engine-motion.ts:131`), el motor de escena
(`engine-scene.ts:99`), la háptica de sema (`chans/haptic.ts:129`) y OCHO
lecturas en componentes de eidos preguntaban a `ActiveDom.prefersReducedMotion`
(el media en crudo). El puerto correcto YA EXISTÍA sin consumidores:
`MotionSource = Source<MotionEffective>` (`$libs/motion`). La fuente nace en
prefs (`createMotionSourceFromPrefs`, `src/arts/prefs/motion-source.ts`,
precedente `createLocaleSourceFromPrefs`) y la construyen UNA vez las tres
raíces: `createActiveUix`, `attachActiveUix` (fallbacks desde `app.prefs`) y las
fábricas `defineEngineMotion` / `defineEngineScene` / `defineEngineSemantic`
(`coreDependencies: ['prefs']`, forma de format y langs). MUEREN de los puertos
`MotionDom.prefersReducedMotion`, `SceneDom.prefersReducedMotion` y el interface
`HapticChannelDom` entero (un puerto que contesta política es la puerta por
donde vuelve el defecto); `MotionRunOptions.reduced` se queda como PIN por
ejecución. `ActiveEidos.reducedMotion` con dos puertas: con prefs el efectivo;
standalone sigue al SO con acta (sin primer motor no hay segundo, §60). Los
ocho sitios de componentes leen `eidos.reducedMotion`. Hallazgo del lote: DOS
motores de escena por superficie DOM (aura-indicator, pack Ambient) que se
habrían quedado ciegos a la policy `reduce` obligatoria (P-1) EN SILENCIO —
reciben la fuente vía `eidos.reducedMotion`. Deuda nombrada: escena lee la
fuente en el montaje (paridad con la lectura del media que sustituye).
`ActiveDom.prefersReducedMotion` no se toca: es un hecho del SO que alimenta
el ENTORNO de prefs y nada más.
Soma (adenda firmada «a en B»): el último lector de política que preguntaba al
SO, `soma/runtime.svelte.ts:1280` (migración a11y del morfo: `'state'` ⇒
`channels: []` por S5, `'text'` ⇒ región viva, `'focus'` ⇒ foco), lee la
MISMA fuente: `SomaRuntimeBaseSources.motion: MotionSource` OBLIGATORIO como
`dom` (un runtime que no puede responder «¿reducido?» no puede honrar S5 — lo
garantiza el tipo), llenado por `Soma.runtime()` desde `uix.prefs` (esquema sin
`motion` ⇒ permitir, como los motores). La decisión mayor (b) — que el MOTOR
aplique `'state'` en su pasada de reducción y que un `emit()` directo reciba
tratamiento a11y — queda ABIERTA como fila §3.9 de CONTINUE-sema-audit.md, a
ejecutar junto a D-full. `ActiveEidos.reducedMotion` distingue «sin prefs»
(standalone ⇒ SO, §60) de «prefs sin ranura motion» (⇒ permitir, como los
motores): las dos mitades de una UI ya no discrepan bajo un esquema sin la
dimensión. Hallazgo de la adenda: la bolsa `sources` se construye en 96
sitios (3 de producción — menu-dial, metrics, onion-menu — que reciben la
fuente vía `eidos.reducedMotion`, y 93 harnesses cuyo `as unknown as Soma`
CEGABA la comprobación de miembros: 60 tests rojos hasta declarar el `Omit`
real; el tipo hizo su trabajo en el código de producción y era ciego justo
donde se suponía que bastaba). Test real nuevo
`soma/test/reduced-motion-source.svelte.test.ts` (chromium: `Soma.create()`
lee contexto Svelte) con `createActiveUix` + `EngineSemantic` reales;
mutación (el trigger vuelve al dom) ROJA 3/3. Adversarial dirigido de la
adenda: siete defectos cerrados — `src/uix/contracts.ts` declaraba
`requires: ['dom']` para la bolsa de soma y el test no lo asertaba (un guard
que no inspecciona nada pasa) → `['dom', 'motion']` + aserto; la bolsa #97
(`bag-census.test.ts`) sin `motion` bajo un casteo; el camino `'text'` —el
ÚNICO que declaran 9 morfos de producción— sin test (añadido); dos snippets
de docs que ya no compilaban (component-guide A1, morfo.md); cifras del §62.
C) La raíz GARANTIZA los cuatro ejes visuales (cazado por el autor en el docs
site: texto invisible en oscuro). Cuatro layouts congelados componen su propio
esquema de prefs SIN `mode/theme/density/scaling`; un esquema del app
SUSTITUÍA al de la raíz entero y eidos «degradaba defensivamente» a
`mode = 'light'` CONSTANTE ignorando el SO, mientras el shell estampaba su
wrapper en oscuro: tinta de tema claro sobre superficies oscuras. Forma:
`uixVisualPrefsDimensions()` (pura, en `prefs-schema.ts`; el boot compila la
misma) y `createActiveUix` fusiona SIEMPRE `{ ...ejesVisuales, ...esquemaDelApp }`
— el app puede REDEFINIR un eje, nunca omitirlo; sin un solo cast. Eidos deja
de degradar en silencio: `createPrefsPreferenceSource(prefs, fallbacks,
onMissing)` avisa por `uix.logger.warn` por cada ranura ausente (queda solo
para attach con prefs ajenas; docs de attach: el app compone
`uixVisualPrefsDimensions()`). Tests reales (esquema sin ejes + SO oscuro ⇒
`data-mode="dark"`; app que redefine `theme` gana; prefs ajenas ⇒ 4 avisos por
el logger REAL); el viejo test «degrades to the fallbacks» estaba verde POR
COINCIDENCIA (defaults = fallbacks) y se sustituye; mutación (sin fusión) 2
rojos y restaurada. Boot regenerado (15134 B), delta cero ×5 intacto.
`web/routes/uix/+layout@.svelte` (descongelado por orden del autor, +14/−29):
muere el `modeSource` muerto y el `$state` local; el shell LEE la ranura
`mode` y el toggle escribe `uix.prefs.setIntent('mode', …)`; migración única
de `uix-docs-theme` al sobre de prefs (clave borrada). Ledger 95 → 93 (la
entrada de ese fichero desaparece). VERIFICADO EN CHROME por el coordinador:
SO oscuro sin clave ⇒ `<html data-mode="dark">`, wrapper dark, tinta
`oklch(0.95)`; clave vieja `dark` ⇒ intent `dark`; toggle mueve html, wrapper,
tinta e intent en los dos sentidos. Nombrado, no arreglado: el boot compila
siempre el esquema por defecto (un app que REDEFINE un eje resuelve distinto
que el boot; hoy nadie en web/ usa el boot) · los otros tres layouts congelados
recuperan los ejes pero su wrapper sigue en `$state` local · SSR del docs sirve
`light` y la hidratación corrige (previo).
Verificación: A — vitest eidos+value-channels 43/483, eidos:lint 0, paridad 0
discrepancias, mutación del guard 3/3 y 4/4 (adversarial), prettier solo avisos
preexistentes; B — vitest del scope 90/990 y, con la adenda, 242/2395; +26 tests
(motor 5 · escena 2 · háptica 2 · raíz real jsdom 7 · ActiveEidos 7 · fábrica 1 ·
soma 3 — cifras verificadas una a una por el adversarial), 11 dobles re-firmados,
93 harnesses de soma tipados con el `Omit` real, cuatro mutaciones ROJAS (3/2/3 +
la de soma 3/3) y restauradas por sha256, más la mutación de TIPO del adversarial
(la fuente devuelve la intención ⇒ 2 errores nuevos en src/: el puerto es gate de
tipo, no prosa); C — vitest active-uix+eidos+prefs 57/591, boot 8/8 con delta
cero, mutación 2 rojos, verificación en Chrome por el coordinador; todos —
check src/ 0 (ledger 95 → 93: MENGUA), check:gate OK, docs:check OK,
arts:check OK, packs:check OK. Suite completa 457/5336 verde (adversarial A). Adversariales Opus
independientes por lote (informes en el handoff). Constructores + adversariales
Opus 5; la sesión coordina. ⚠ Lección: la cuenta del brief de A («94 en fuentes»)
era un fallo de medida del coordinador (`grep -rh | grep -v generated` filtra
LÍNEAS con esa palabra, no el directorio); el constructor la re-midió porque el
brief lo exigía. ⚠ Dos constructores cortados por límite de sesión y reanudados
tras releer su diff entero (ley de la casa).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 weeks ago
suppressing the motion of a user who asked to keep it. Guarded by
`src/uix/eidos/reduced-motion-media.test.ts` .
- **Runtime (JS drivers)**: `MotionContext.reduced` from the EFFECTIVE
preference — a `MotionSource` over `prefs.motion`
(`createMotionSourceFromPrefs`), injected into `EngineMotion` / `EngineScene`
/ sema's haptic channel by the composition root. The same ONE source as the
CSS above: the engines no longer read `ActiveDom.prefersReducedMotion` , which
is the OS hint, not the answer. `MotionRunOptions.reduced` stays as a per-run
PIN and wins over the source. The `ReducePolicy` applies before running.
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
- Morfo declares `a11ySemantic.reducedMotionFallback` per event; soma
silences the signal when it applies. The engine is coherent with that
decision.
---
## 11. Primitives and keyframes
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
Existing tokens (`STATIC_MOTION`, generated, read by css and js):
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| Token | Values |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--duration-{k}` | `instant 0` · `fast 120` · `normal 180` · `moderate 240` · `slow 320` ms |
| `--ease-{k}` | `default` · `out` · `in` · `spring` · `alert` · `symmetric` |
| `--motion-distance-{k}` | `xs 2` · `sm 4` · `md 8` · `lg 16` px |
| `--motion-scale-{k}` | `enter 0.985` · `press 0.97` · `through 0.92` (fade-through) · `lift 1.02` (drag pickup — the only `>1` , see §Draggable surfaces in THEMING) |
| `--motion-stagger` | `20ms` — the stagger STEP a menu ripples at (`dropdown-menu`, `context-menu` set `--motion-stagger-each` from it) |
feat(eidos): el ritmo de revelado sube a config — primitives.motion.staggerViewport, y su regla emigra a la foundation
Firma opción 1 del autor (cableado EidosConfig.motion → CSS, la última
entrada de PENDING_PRIVATE_RENAME). Ejecutada con el esquema completo:
expediente + dos constructores en serie + adversarial, todos Opus en ultra,
coordinación y supervisión de la sesión.
El gancho --motion-stagger-each-default era FICCIÓN: nadie lo declaraba y su
único consumo (motion.css:42) resolvía siempre al literal 70ms. Ahora:
primitives.motion.staggerViewport: '70ms' (tipo + default + validación +
contrato + emisión --motion-stagger-viewport en :root, molde floating-gap
verbatim) y la regla [data-stagger] > [data-animation-trigger='viewport']
EMIGRA a renderMotionBlocks — donde vive la máquina entera del stagger. Con
ello motion.css queda en sus dos gates de opacity (su veredicto structural,
más verdadero), la entrada del registro cayó STALE POR MECÁNICA y el
registro, a cero, se DESMONTA con acta (un guard que inspecciona el vacío es
un falso verde); la historia completa cierra en canon/recipe-contract.md.
E14 (70→20ms) queda APLAZADA con nombre: firma de diseño propia, mueve 8
blocks.
Adversarial 9/9 CONFIRMADO: neutralidad término a término contra la línea
base histórica en Chrome real con el IntersectionObserver disparando
(0/0.07/0.14/0.21/0.28/0.35s en /blocks/team/preview) - override 999ms
reescribe la cascada - nombre muerto inerte (5s en :root, cero efecto) -
<Cascade> intacto con control positivo quirúrgico (solo el hijo estampado
toma el token) - cero empates de hoja posibles (tres escritores en .css,
especificidad decide) - el guard del espacio cerrado muerde por mutación -
126 tests, censo 73%/66/1152-0-0, docs 0/0. Sus reservas, con acta en
changelog §57: el contrato gana DOS filas (--motion-stagger-each entra como
(derived) al emitirse — clase preexistente de index/index-rev) - gates
divergentes latentes - path del contrato sin pin. Correcciones del
adversarial incluidas: dos citas muertas del ledger de blocks anotadas, el
presente falso de component-audit.ts, y el absoluto de canon matizado.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 month ago
| `--motion-stagger-viewport` | `70ms` — the reveal rhythm of a viewport animator inside `[data-stagger]` ; the foundation writes it into `--motion-stagger-each` on that selector alone (changelog §57) |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**Override vars (runtime, NOT theme tokens)** — a component sets them on its
element; the preset reads them with a token fallback:
| Var | For | Notes |
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--motion-duration-{enter,exit}` | per-component timing (F3) | `@property inherits:false` |
| `--motion-ease-{enter,exit}` | per-component curve (F3) | `@property inherits:false` |
| `--motion-slide-leave` | `slide-full` 's extra travel for inset panels (drawer) | default `0px` |
| `--height` / `--collapsed-height` | the `collapse` 's measured height (soma writes it) | — |
| `--motion-stagger-each` | the stagger rhythm (on the container, inherits) | default `0ms` → **parallel** ; N → cascade. One exception, and it is foundation, not a component: `[data-stagger] > [data-animation-trigger='viewport']` gets `var(--motion-stagger-viewport)` (the theme token above), so a section reveals on rhythm with no per-item delay. `<Cascade>` keeps its `0` |
| `--motion-stagger-index` / `-index-rev` | the per-item index **from structure** — `[data-stagger] > *:nth-child` (fwd, enter) / `:nth-last-child` (rev, exit); generated in `render-css.ts` , **nobody writes it** | `@property inherits:false` , `<integer>` |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
Keyframes (`EidosConfig.motion.keyframes`, parity with Chakra's
`theme.keyframes` ): individual `translate` /`scale` (they compose without
stomping each other); parameterized by CSS var for dynamic sizes/distances
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
(`var(--height)`, `var(--collapsed-height,0)` ). Built-in: `fade-in/out` ,
`scale-in/out` , `slide-from/to-{side}[-full]` , `expand/collapse-height` ; the
**signature** (`announce-pulse-*`, `commit-settle` , `press-squeeze` ,
`dismiss-fade` , `present-rise` ); **Material**
(`slide-axis-{x,y}-{in,out}`, `scale-from-92` ).
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
## 12. Initial content (signatures + presets)
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**`presets` (the `--state` moment)** — built-in (`css`):
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| Name | Notes |
| ---------------------------------- | -------------------------------------------------------------------------- |
| `fade` · `scale-fade` | enter+exit; scale-fade with the `--_floating-transform-origin` alias (§15) |
| `slide-fade` | side-aware (`data-side`): slide + scale 0.985 + fade |
| `slide-full` | edge drawer, inset-aware (`--motion-slide-leave`) |
| `collapse` | measured height (`var(--height)`) |
| `shared-axis-x/y` · `fade-through` | Material 3 (F5) |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
The **JS drivers** (`spring`/`waapi`/`rect`, `presets/js.ts` ) are registered
per app/demo. The first **named built-in JS** preset is `spring-pop`
(`driver:'spring'`, `presets/js.ts` → `BUILTIN_JS_PRESETS` ), registered
directly on `ActiveEidos` — not via the serializable
`EidosConfig.motion.presets` , which cannot carry its `MotionRun` functions
(`structuredClone` fails). Demos additionally register `panel-spring` ,
`dialog-spring` , `flip` .
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
**`signatures` (the `--event` moment)** — built-in, migrated from
`events.css` (F2, §15): `present` /`dismiss`, `commit` (+ the
`fulfill` /`affirm`/`threat` intents), `press` (`contact`), `announce`
(+ 5 intents). Generic per `family` /`intent`/`event`.
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
## 13. Per-component defaults
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
The per-component **state-moment** (the `motion` prop, the wrapper's
default):
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
| Component | Part | Default `motion` |
| ------------------------------------------------------------- | ----------------- | --------------------- |
| Dialog | overlay / content | `fade` / `scale-fade` |
| Popover · Tooltip · Dropdown · Select · Menubar · ContextMenu | content | `slide-fade` |
| Drawer | content | `slide-full` |
| Accordion · Collapsible | content | `collapse` |
| Toast | item | `slide-fade` |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
The **event-moment** is automatic: when the event fires (e.g. `commit` ,
`emerge.present` ), the corresponding `signatures` entry reacts — no prop.
---
## 14. Where the code lives
```
arts/motion/ ← THE SERVICE (a pure art: no UI or cross-art deps)
types.ts ✓ MotionDom (structural port), MotionConfig, EventSignature, StatePreset, MotionHandle…
engine-motion.ts ✓ createEngineMotion → EngineMotion (register/resolve/enter/exit/run/cancel/pending/dispose)
drivers.ts ✓ JS drivers: spring() (physics) / waapi() / rect() (FLIP)
index.ts ✓ the `$motion` barrel (named re-exports) + README.md
arts/active-app/service-factories/motion.ts ✓ defineEngineMotion (factory; coreDep 'dom')
eidos/lib/motion/presets/css.ts ✓ DATA: keyframes + state presets + signatures (BUILTIN_*) + Material (F5)
eidos/lib/config-types.ts ✓ EidosConfig.motion ($motion's MotionConfig type)
eidos/lib/render-css.ts ✓ GENERATES CSS: presets ([data-state]) + signatures ([data-event-*]) + overrides + stagger
eidos/lib/config.ts ✓ validates presets (css + js) + signatures
eidos/lib/themes/base.ts ✓ built-in DATA (keyframes + signatures + presets)
eidos/active-eidos.svelte.ts ✓ eidos.motion → delegates to uix.motion + registers the css presets at boot
eidos/components/dialog/ ✓ wrapper: motion prop → data-animation-style (no runMotion)
active-uix/active-uix.svelte.ts ✓ uix.motion : EngineMotion (createActiveUix creates it; attach reads it from the app)
soma/core/soma.svelte.ts ✓ soma.motion → uix.motion
soma/layers/presence.svelte.ts ✓ `motion: EngineMotion` opt → motion.run(node, phase) (JS gating) + tests
```
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
(`✓` done. Relocated to `arts/motion` in the service refactor. Pending:
passing `motion` to more overlays (drawer/popover/…); named built-in JS
drivers; F6/F7.)
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
## 15. The event-moment: from `events.css` to `signatures`
`events.css` WAS "the hand-implemented `--event` surface". In **F2 it
migrated to `signatures` **: the 9 keyframes + 12 reactions (announce pulse
per intent, commit settle, dismiss fade, press squeeze, present rise) now
live in `EidosConfig.motion.{keyframes,signatures}` (`presets/css.ts` →
`BUILTIN_SIGNATURES` ) and are generated into `generated/base.css` —
theme-extensible. `events.css` was reduced to two global concerns: the
compositor hint (`will-change`) and the reduced-motion cap (the signature's
`motion` is silenced under reduce, but sound + haptic keep communicating —
the cross-modal advantage). `archetypes.css` (4 baseline hover/focus
transitions) stays as the transversal micro-interaction.
---
## 16. Comparison with Chakra UI v3
| Capability | Eidos | Chakra v3 |
| --------------------------------------------------- | --------------------------------------------- | ---------------------------------- |
| The `--state` moment (`data-state` + named presets) | ✅ `presets` + the `motion` prop | ✅ `animationStyle` + `data-state` |
| `Presence` (hold-through-exit) | ✅ soma (`getAnimations`, covers transitions) | ✅ (`animationend`) |
| keyframes registry + tokens | ✅ | ✅ |
| placement-aware | ✅ `data-side` | ✅ `data-placement` |
| **The `--event` moment (perceptual signature)** | ✅ `signatures` + sema (intent/hold/sequence) | ❌ **doesn't exist** |
| **Both moments integrated** | ✅ | ❌ (only `--state` ) |
| physics / FLIP / orchestration (JS drivers) | ✅ `spring` / `rect` / `waapi` | ❌ |
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
The ** `--event` moment integrated with the perceptual signature** (and with
sound/haptic) is what no current framework has. Chakra animates the state;
UIX animates the state **and** the occurrence, keeping them distinct.
---
## 17. Naming decisions
- The state-moment attr: ** `data-animation-style` ** (NOT `data-motion` ,
which is reserved for the reduce-motion pref `allow` /`reduce`).
- The event-moment attrs: the `data-event-*` sema already stamps.
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
- **Cleanup (F7)** ✓: NavMenu's directional `data-motion` (a planned rename)
**does not exist** in the code — moot. `data-motion-ref` and the "TSC
`event:*` scope" are discarded (no real use).
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
---
## 18. Implementation phases
The "beat every framework" roadmap. **F1– F6 implemented:**
- **F1 — The two-moment foundation** ✓: the types (`MotionConfig`
{keyframes, signatures, presets}, `EventSignature` , `StatePreset` ) +
generation (both surfaces) + the `EngineMotion` engine (the `uix.motion`
service, relocated to `$motion` in the refactor) + validation + tests.
- **F2 — The cross-modal signature** ✓ (beats SwiftUI): `events.css`
migrated to `signatures` (§15); the signature's `motion` + sound + haptic
come from the SAME event (a coupling SwiftUI's `.sensoryFeedback` leaves
loose).
- **F3 — Polished presence** ✓ (matches Base UI): Dialog/Popover/Drawer/
Accordion migrated to presets; **per-component duration + easing**
overrides (`--motion-duration/ease-{enter,exit}` + `@property
inherits:false`), inset-aware slide (`--motion-slide-leave`), the
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
measured-height bridge (`--height`). Zero timing regression.
- **F4 — The mechanical JS engine** ✓ (matches Framer): the `spring` (real
physics) / `waapi` / `rect` (FLIP) drivers; `Presence.motion` →
`motion.run(node, phase)` (the engine as the `uix.motion` service,
consumed by soma and eidos **with no coupling** ); a real Dialog bouncing.
- **F5 — Choreography** ✓ (matches Material 3): declarative stagger
(`--motion-stagger-{index,each}`), the `shared-axis-x/y` + `fade-through`
presets; container-transform via the `rect` driver.
- **F6 — Token rigor** ✓ (matches Carbon): the raw signature **tokenized** —
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
the duration scale gains the long stretch (`slower` 400 · `deliberate` 600 ·
`emphatic` 800 · `sustained` 1000ms). _(Corrected 2026-07-06: F6 originally
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
set the neutral/affirm/fulfill announces to `deliberate` claiming alignment
"with the book's holds-by-intent" — that aligned with the DRIFTED runtime
table, not the book. Per the book's regions (TABLA 32.2): neutral/affirm =
`moderate` ("breve"), fulfill = `slower` ("breve-media"), and the long end
(`emphatic`/`sustained`) belongs to risk/threat. The hold never truncates a
signature — the visual channel awaits the expression, capped by
`MAX_EXPRESSION_WAIT_MS` ; see decisions/book-deviations.md D.12.)_
feat(audit): component re-audit + fixes P0–P5 (P4 complete, P5 in progress)
Re-audit of the whole component catalog at pilot depth (91 fichas + the
checkpoint verdicts in docs/audit/components/) and the executed fix packages.
- P0–P3: doctrine (sema participation, CANON §3 intent, component-guide §5 +
API naming, §32 outline), guards (morfo-vocabulary-check S10/S11d,
component-audit A-3.1/A-1.4/S9, eidos-lint classHooks), morfo contracts
(role=application removed ×4, aria-selected off the Day, drp translationRef,
field data-state prune, pin-input commit-set, media-player renames), 13 new
sema packs + 12 morfos family-default → pack.
- P4 (N1–N10) COMPLETE: selectionMode (select/combobox/toggle-group/calendar);
onValueCommit terminal-callback norm (pin-input/search/password/textarea +
date/time/color-field add); typed validation reason + onInvalid (tags-input,
css-field); index/onIndexChange (carousel); deselectable; openDelay/
groupSkipDelay; allowCustomValue; defaultValue prune.
- P5 (in progress): S1 class-hooks codemod (22 hooks → data-attrs across
cropper/button/image-picker/toggle/image-adjustments); S5 outline remnant in
field.css; depth pass (data-depth=overlay on tooltip/toast/float-panel);
touch-rows (--touch-target token, decoupled ::before hit-slop, area-not-visual,
44 AAA). S6 (Field composition) + S8 (calendar-surface) pending.
Verification: errors-outside-(alpha|words|palabras|chronos) == 59 (baseline);
morfo-vocabulary-check exit 0; eidos-lint class-hooks/invalid 0 on touched
components; per-component vitest suites green.
Handoff + remaining plan: docs/process/continue-audit-fixes-2026-07.md
Excluded (broken by the N1 rename, left broken per user decision, not staged):
words/**, palabras/**, chronos, web/routes/alpha/**.
Reconciliation pending: the touch-rows ::before for checkbox/switch reverses
changelog §37's earlier pseudo-element rejection (WCAG-overlap) that had routed
markers to a labeled-row/Field task — flagged for the user in the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
Shared-axis travels the canonical
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
`--motion-distance-xl` (30px); fade-through uses `--motion-scale-through`
(0.92); `press` /`commit` snap to the existing scale (sub-perceptible). The
** `emphasized` ** easing (M3 emphasized-decelerate). **Productive/
expressive sets** (Carbon): `primitives.motion.expressive` emits a
`[data-motion-set='expressive']` scope remapping the easing — productive
is the default. Distance-scaled duration stays a **pairing convention**
(distance token ↔ duration token), not a runtime formula (no CSS consumer
today — the `rect` driver would do it by measurement if a consumer asks).
- **F7 — Extensibility + typegen** ✓ (beyond everyone): the `--state`
moment's preset names are **type-safe + app-extensible** via an
augmentable registry — `EidosMotionPresets` (mirroring
`SemaChannelSignatures` ), with `MotionPresetName = keyof
EidosMotionPresets | 'none' | (string & {})`. An app adds type-safe
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
presets with `declare module '$uix/eidos' { interface EidosMotionPresets
{ … } }`: the `motion` prop autocompletes them and a typo is a compile
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
error. The engine (`$motion`/`uix.motion`) stays `string` (open at
runtime) — the registry is compile-time ergonomics over the props; a test
revert: deshacer la auditoría entera — se hizo sin leer la doctrina
Revert de los 7 commits de la sesión del 2026-07-29/30:
352ca8bbe style(soma): formato Prettier en el test de gradient-picker
17a1f167b test(soma): chat-list
e70397fca test(soma): field-langs y gradient-picker
c9b83d025 docs(morfo): qué hace pública una parte + hallazgos retirados
feb4a8424 test(soma): los primeros providers que no tenían red
f8e35b8fd fix(morfo): el eje intent/color
c39170abb fix(uix): la auditoría del sistema
`4e785d32b` (stats-band, otra sesión) queda intacto — el revert lo salta.
## Por qué se revierte todo y no una parte
La instrucción de partida era «audita el sistema, **para ello previamente lee
toda la documentación**». No se leyó. Se auditó primero y se justificó después,
y eso contaminó el conjunto, no unos commits concretos:
- Tres hallazgos del informe eran FALSOS, todos de la misma forma —
heurísticas de una sola vía dadas por hechas sin abrir el código:
D-3 `AgentTimersPort` (el puerto sí está satisfecho por el adaptador de
`defineActiveAgent`); «5 derivas de scope» (`avatar-group` / `path-trace` /
`rotate-align` sí están implementados, co-locados en el directorio del
padre); «29 morfos con `kind:'public'` irreal» (medía si existe
`<Componente.Parte>` e ignoraba que un primitivo de API plana compone por
props y snippets).
- `c9b83d025` existe SÓLO para retirar esos hallazgos del commit anterior.
- Los 6 tests de provider se montan sobre un `installSomaHarness` que FABRICA
cuatro servicios compartidos (`dom`, `langs`, `timers`, `prefs`), violando la
primera regla de propiedad de `architecture/active-uix.md`: «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`.» El arranque real son tres líneas
(`createActiveUix` → `setActiveUix` → `Soma.create()`) que estaban escritas.
- Y se le pidieron al usuario decisiones cuya respuesta estaba en el corpus
(`testing-and-tooling.md` dice que los tests de provider son convención, no
guard; la regla 1 zanja el arnés).
Triar qué conservar exigía justo el juicio que falló. Se revierte todo, se lee
el corpus, y vuelve sólo lo que se pueda sostener citando un documento — o lo
que sea defecto MEDIBLE y por tanto no dependa de criterio (el colapso de
uniones de props, que hacía compilar `<Avatar color="nonsense">`; que
`translations:check` crashease en cada ejecución de su historia). Nada se
pierde: los commits siguen en la historia y se recuperan con `cherry-pick`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 months ago
pins the built-in set against it. NavMenu's directional `data-motion` no
longer exists (the rename is moot). `transition` with named groups stays
docs(book): F7.3 (8/9) — eidos-motion translated to docs/theming/motion.md
src/uix/eidos/eidos-motion.md (698 L, Spanish) translated to English, same
s1-s19 numbering: the two-moment thesis, the closed cascade model
(D.11-D.13), motion across the 4 layers, the two-surface registry, types,
the EngineMotion API + cleanup policy, the 5 drivers, the DOM contract
(data-animation-style), Presence integration, reduced motion, primitives/
keyframes, built-in content, per-component defaults, code map, s15 (the
events.css -> signatures migration — cited from events.css), Chakra
comparison, naming, phases F1-F7, deferred. Stub with the full s-map at
the old path; corpus links swept (comparison, eidos chapter, changelog,
motion-guide). docs:check 0 errors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
**deferred** (no consumer; `getAnimations({ subtree: true })` would cover
it).
**Roadmap F1– F7 complete.** Every phase kept **zero regression** for anyone
not using `motion` or firing new events.
---
## 19. Out of scope / deferred
- A `motion-one` adapter or another external JS engine (opt-in, never a base
dependency).
- Exotic drivers (complex timelines) — only with a real consumer.
- Widening durations to 7 steps (Chakra-style) — only if 5 falls short.
---
**Revision notes**: 2026-06-21 — closure of the `motion` prop's domain
(§D.12): the **state domain** (the dedicated `data-motion-state` attr + the
`select-pop` emphasis preset, `<Card>` pilot), **content loops**
(`spin`/`pulse`/`ping`/`bounce`, un-gated infinite rules) + the `<Motion>`
exit fix (`tick` no-op), the `[data-debug-stagger]` debug affordance, and
the first **named built-in JS** preset `spring-pop` (the `spring` driver's
debut, registered on `ActiveEidos` ; demonstrated on
`Popover.Content motion="spring-pop"` ). Loops+lifecycle demo at
`web/routes/demos/motion` . Earlier (2026-06-04): F1– F7 implemented + the
engine relocated to `arts/motion` (the `uix.motion` service).
If the code diverges, the code wins; open an issue. References: the
historical seed `GUIA_IMPLEMENTACION_SEMAUIX.md` (§4.3, §7, §11 — the
event/state/signature model),
[`active-architecture.md` ](../architecture/active-architecture.md ) §5/§6
(the causal chain, the attrs), THEMING (tokens),
[`soma-architecture.md` ](../architecture/soma-architecture.md ) (Presence),
[`sema.md` ](../architecture/sema.md ) (signature/signals),
`arts/adom/README.md` (reduced-motion).