You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/docs/rfcs/rfc-depth.md

228 lines
16 KiB

---
title: RFC — Eidos depth engine
type: rfc
audience: human + agent
status: implemented — phases 1-5 ✅
source: migrated from src/uix/eidos/DEPTH_ENGINE_RFC.md (2026-07-02, docs-book F7.4)
---
# RFC — Eidos depth engine
> Sibling of [`rfc-color-engine.md`](./rfc-color-engine.md) and
> [`rfc-typography.md`](./rfc-typography.md). Takes the **depth** channel
> (`depth/presence` from the book _Diseñando lo que ocurre_) to reference-grade
> by **breaking** the references' model, not copying it — with the **open cage**.
## 0. Thesis
> **Depth is not something an element _has_; it is something that _happens_.**
An overlay does not "have elevation 4": it **states an emergence** — it ascends
to a front plane on appearing, recedes on leaving — governed by the **same sema
event** that already drives its motion/sound/haptic. Depth becomes a
**first-class expression channel** (one of the book's 8), **unified** (a single
notion coheres surface + shadow + z + atmosphere), **eventful** (fired by the
sema engine) and with an **open cage** (strong default, doors at every layer).
## 1. The survey — how the references do it and where they stop
| Framework | Model | Structural limit |
| ---------------------------- | ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| **Material 3** | elevation = tonal tint + shadow, `dp` scale 0–5 | `dp` = physical height with 1 light source; elevation is a component **property**, not the expression of a happening; fixed scale |
| **Tailwind** | flat `shadow-sm…2xl` presets | no mode (the shadow _lies_ in dark), no surface, no semantics |
| **Apple / SwiftUI** | materials (blur/vibrancy) + subtle shadow | platform-bound (backdrop), not a portable token system |
| **Polaris / Carbon / Radix** | shadow scale by role or number | static; fixed role, detached from events |
**Common limit** (even M3, the finest): depth = **a static property you assign**
- **a skeuomorphic light model** + **disconnected** from what happens and from
its own signals. Nobody treats depth as the **expression of a happening**. And
everyone picks a side: Tailwind = flexible **without opinion**; Material =
**strong opinion but closed cage**.
## 2. Where Eidos stands today (already above average, but not novel)
- **z-index** in semantic bands: `base · raised · sticky · dropdown · popover · tooltip · modal · toast`. ✓
- **Tonal, mode-aware surface ladder**: `surface-default→raised→overlay` =
`neutral-1→2→4` (in dark, "higher" = lighter — M3's tonal overlay, already present). ✓
- **Role-named shadows**: `shadow-subtle/raised/overlay` (→ `shadow-1/3/5`),
per-theme set (`THEME_BASE_DARK_SHADOW`). ✓ (above Tailwind)
- **BUT** they are **3 separate systems** (you pick shadow + surface + z by
hand), depth is **static** (a prop) and the model is still the skeuomorphic
shadow one. You are ~on par with M3.
## 3. The novel model — 4 pieces
1. **Unified semantic plane** — `flush · raised · overlay · modal · recessed`.
One level coheres surface + shadow + z + (blur) at once. Named by
**role in the attention hierarchy**, not by millimeters.
2. **Mode-adapted, _computed_ multi-signal mix** — light leans on shadow; dark
on surface tint + halo (the shadow lies in dark). Shadows derived from the
**surface color in OKLCH** (not flat black-alpha), reusing the color engine.
Solves the shadow-in-dark problem at the root.
3. **_Eventful_ depth (the genuinely new part)** — a channel the **sema** engine
fires, like sound/haptic: `emerge`→rises, `contact`→sinks, `signal/threat`→
pushes to the front; coordinated with motion's **two-moments** model.
4. **Atmosphere / presence** — backdrop-blur (frost) on the plane; the modal
_veil_ is component-owned (`--{component}-overlay-*` / `--color-overlay`), not
a plane cue: what is in front blurs/dims what recedes (overlap with the
_presence_ channel).
## 3.bis The two moments of depth
Depth respects the framework's **two-moments model** (motion F1) — it does not
reinvent it. Eidos already reads both axes; depth adds its layer to each:
| Moment | Attribute (axis) | For depth | Phase |
| --------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
| **state** | `data-state` (persistent · soma/morfo) | the **resting plane** — `data-depth='{plane}'`: where the element _is_. Eidos applies it as a state _preset_. | 1 ✅ |
| **event** | `data-event-*` (transient · sema, during the `hold`) | the **ascent / recession** — a _depth signature_ on `emerge`/`contact`: what _happens_. Ordered by `sequence`, next to the motion signatures. | 3 |
That is why an overlay **has** a plane (state) **and ascends** to it (event) —
the two moments, never collapsed into one. The depth channel is a new channel
**expressed through the same model** motion already uses: the book's rigor
(event ≠ state) carried to depth.
## 4. Doctrine — _strong default, open cage_
> The semantic layer is **additive** over primitives that **never disappear**;
> the set, the mix and the channel are **extensible/overridable** via config +
> sema's open registry.
| Piece | Strong default | Open door |
| -------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **planes** | canonical set (5) | the **set is config-driven** (`EidosConfig.depth.planes`); add `sheet`/`peek`, rename — not a closed enum |
| **mix** | computed mix per plane | **retune the mix per plane** in config; **per-component override** of any signal; the **primitives stay** (`--shadow-N`, `z-index`, raw `box-shadow` one step away) |
| **eventful channel** | `emerge↑ / contact↓` | **opt-in/opt-out** (like sound+haptic); rules in **sema's open registry** (declaration merging + appendable cascade → the app adds/overrides the event→depth map); **degrades with `prefers-reduced-motion`**; static depth **works without the channel** (eventful is additive, never a wall) |
| **atmosphere** | frost (blur) on the plane, opt-in via `data-frost` | amount in config; per-overlay **opt-out**. The modal _veil_ is component-owned (`--{component}-overlay-*` / `--color-overlay`), not a plane cue — scrim cue pruned 2026-07-12 |
| **whole system** | canonical theme | runtime **`applyDepth(seed)`** (sibling of `applyColorScheme`/`applyTypeScale`) |
None of this is **new to the framework**: it is exactly how sema (open
registry), eidos (config + `setCssVariables` + recipe overrides) and
color/typography (retintable canon + runtime builders) already operate. Depth
inherits the same openness contract.
## 5. Token contract (additions, frozen)
Bare-prefixed by system (`--depth-…`). The plane is a **named bundle** that
**composes the existing primitives** — it does not replace them:
```
--depth-{plane}-surface → var(--color-surface-{…}) (tonal tint, mode-aware)
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
--depth-{plane}-border → var(--color-border-{…}) (bordered elevation · A1/Decisión 8)
--depth-{plane}-shadow → var(--shadow-{…}) (drop, mode-aware via theme)
--depth-{plane}-halo → oklab rim-light (dark-mode lift · Phase 2)
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
--depth-{plane}-z → var(--z-index-{…}) (band — EXPOSED, never painted)
--depth-{plane}-blur → frost backdrop-blur (Phase 4 ✅ · opt-in via data-frost)
```
> **Amendment 2026-07-12** — the `--depth-{plane}-scrim` cue was **pruned** from
> this contract (removed from `DepthPlane` / `DEPTH_CUES` / validation / emitter).
> It was declared but never seeded, emitted or read — a write-surface with no
> reader (`applyDepth({p:{scrim}})` silently no-op'd). The modal veil lives, by
> the standing decision, in the component overlay layer (`--{component}-overlay-*`
> tinted by `--color-overlay`) — matching MD3 (`md.sys.color.scrim`), Radix
> `Dialog.Overlay`, Vaul: none put the veil on an elevation cue. Unlike the `z`
> cue (kept as an exposed-but-unpainted open cage — it varies per plane and is
> unpaintable-forever), scrim had no per-plane variance and its only "cage" would
> duplicate the existing `--color-overlay` role (a token alias). Re-add whole only
> alongside a real backdrop rule.
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
Consumption (canonized 2026-07-06 — the A1/"Decisión 8" wave, 06-19→22,
originally recorded in the archetype-coherence audit and never absorbed here):
`[data-depth='{plane}']` paints the **appearance bundle** — `background`
(when the plane declares a surface), `border` (`var(--border-width) solid
var(--depth-{plane}-border)` — the **bordered-elevation** model, Radix/shadcn
side of the field, vs M3's tonal-only: in light the subtle shadows need the
border to separate planes; in dark, where the shadow lies, border + halo carry
the boundary), `box-shadow` (drop + halo), and the **on-surface typographic
baseline** (`font-family: var(--style-label-font-family)`;
`line-height: var(--leading-ui)`)
so portaled surfaces never fall back to the browser serif — the functional
equivalent of Radix re-wrapping portals in its Theme class, with the plane
attribute as the surface marker. It is a BASELINE, not content styling: an
adopter keeps setting its own radius / font / color on top and wins by cascade
order (recipes load after the foundation) — no double border. Note the box
cost: unlike Radix (border embedded in the shadow stack), the plane border is
a real `border` and adds 1px to the adopter's box.
> **Doctrine — the plane paints, the positioner positions.** `z-index` is
> NEVER painted by `[data-depth]`: stacking is a positioning concern owned by
> whoever positions the element (soma's floating positioner mirrors the
> content's computed z; portaled overlays live on the flat
> `--z-index-overlay-*` band precisely because the plane ladder cannot order
> them — dropdown 300 < modal 700 would hide a menu opened inside a dialog).
> The plane exposes `--depth-{plane}-z` as introspection / escape hatch (0
> consumers today — open cage). Guarded:
> `active-eidos-config.test.ts` asserts the generated CSS never paints
> `z-index: var(--depth-…-z)`.
Nothing existing is renamed → **zero component breakage**.
## 6. Phases (the flexibility is baked into each)
1. ✅ **Unified plane** — `EidosConfig.depth` + canonical planes +
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
`--depth-{plane}-*` emission (composing surface/border/shadow/z) +
`[data-depth]` paint rule (surface/border/shadow+halo — z is exposed,
never painted; see §5's doctrine) + validation + test + regen. Doors:
config-driven set, per-plane mix, primitives intact, escape to raw.
2. ✅ **Computed mode-adaptive mix** — the drop shadow keeps the themed scale
(subtle slate in light / more opaque black in dark — already a tint, not flat
black); Phase 2 adds the **`halo`** cue: a top-edge rim-light **computed in
oklab** (`color-mix(in oklab, white N%, transparent)`, scaled per plane:
5/7/8% on raised/overlay/modal). It is invisible over light surfaces (the
drop rules) and becomes the elevation signal over dark surfaces (where the
drop barely reads) — solves "the shadow lies in dark" **without touching the
global shadows**: it lives only in the depth channel, composed into
`[data-depth]`'s `box-shadow` (`shadow, halo`). The surface-derived halo +
atmospheric frost stay for Phase 4.
3. ✅ **Eventful `depth` channel (event moment)** — the ELEVATION dimension
lives in the _signature_: `present-rise` (emerge) grows the shadow from flat
→ the element's resting one (rises); `press-squeeze` (contact) flattens it to
the surface (recedes). Generic (flush = no-op), coordinated with
position/scale and with sound+haptic from **one single event**, degrading
with reduced-motion (global cap). Overridable: a theme rewrites the
keyframes/signatures. Mounts on the existing `signatures` system, **not** a
parallel one.
4. ✅ **Atmosphere (frost)** — per-plane `blur` cue (overlay/modal) + **opt-in**
rule `[data-depth='{plane}'][data-frost]` (translucent `color-mix` 80%
surface + `backdrop-filter: blur`), gated so an opaque overlay does not turn
translucent by default. + runtime builder **`applyDepth(planes)`** /
`clearDepth()` (+ pure `buildDepth`) retuning any plane cue live — sibling of
`applyColorScheme` / `applyTypeScale`. (The `scrim` cue was **pruned
2026-07-12** — modal backdrop dimming is component-managed via
`--{component}-overlay-*` / `--color-overlay`, so a plane-level scrim cue was
duplicative surface with no consumer; see the token-contract amendment above.)
5. ✅ **Showcase + docs** — `/temas/profundidad` at reference depth: reacts ·
states (dynamic elevation) · ascends (signature) · plane ladder · resting
catalog · light vs shadow (the halo) · open cage · a11y. Exceeds the breadth
of Material's _elevation_ reference by adding the two axes it lacks
(eventful + open cage). + theming reference §29.
## 7. Composition with the existing
- **Surface ladder** (color) → the plane's `surface` signal (mode-aware for free).
- **OKLCH color engine** → computed shadows (Phase 2) and halos.
- **Motion's two moments** → the ascent/recession transition (Phase 3) is a
movement, not a jump.
- **Sema event bus** → the eventful trigger (Phase 3), like sound/haptic.
- **z-index bands** → the plane's `z` signal.
Nothing is reinvented: the dispersed pieces are **unified + made eventful**,
under the book's doctrine.
## 8. Doctrine (parallel to color / typography)
- **Planes + channel = eidos canon** (like roles/variants and named styles):
themeable values, but the _set_ is canon.
- **Eventful depth = an engine capability**, opt-in with degradation (like
sound/haptic).
- **Theme = retint/retune the perceptually fixed**: change HOW MUCH shadow
`overlay` gets, not WHAT `overlay` means.
- **Open cage**: a strong opinion that never traps — primitives always reachable.
## 9. Out of scope
- Real 3D / perspective / gyroscope parallax (depth is perceptual, not a 3D engine).
- Geometry-based shadow ray-tracing (the tint/halo is computed, not physical occlusion).

Powered by TurnKey Linux.