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

16 KiB

title type audience status source
RFC — Eidos depth engine rfc human + agent implemented — phases 1-5 ✅ 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 and 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.