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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
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)
--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)
--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.
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 +
`--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.