From a52f80ceab1b276f9c699955c90a8615fdefaab6 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 12 Jul 2026 21:06:29 +0200 Subject: [PATCH] refactor(eidos): prune the depth `scrim` cue (F4-D scrim decision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `--depth-{plane}-scrim` was declared (DepthPlane field, DEPTH_CUES, validation, applyDepth) but never seeded, emitted or read — a write-surface with no reader (applyDepth({p:{scrim}}) silently no-op'd; never present in generated base.css). Analyzed via workflow (state map + 4 dispositions + adversarial stress). The four collapse to prune vs promote-to-z-parity: wire-and-unify SINKS (contradicts the test-locked z open-cage precedent + the MD3/Radix/Vaul consensus that the veil is component-owned, not an elevation cue), relocate flips its verb to retain, and complete-to-z-parity would mint a third alias of the --color-overlay backdrop role. Unlike z (kept as an exposed open cage — it varies per plane and is unpaintable-forever), scrim has no per-plane variance and no value to seed. Prune: remove scrim from DepthPlane / DEPTH_CUES / validation / emitter + applyDepth doc. Amend rfc-depth.md (token contract + Phase-4 note + atmosphere rows), reference.md cue list, changelog, pendiente. The modal veil stays where MD3/Radix/Vaul + the standing decision put it: --{component}-overlay-* tinted by --color-overlay. Zero observable change (never emitted). Tests 71/71; npm run check src/ = 9 baseline, 0 new; base.css unchanged. Co-Authored-By: Claude Opus 4.8 --- .../continue-cleanroom-fixes-2026-07.md | 2 +- docs/process/pendiente.md | 6 ---- docs/rfcs/rfc-depth.md | 30 ++++++++++++++----- docs/theming/changelog.md | 5 ++-- docs/theming/reference.md | 2 +- src/uix/eidos/active-eidos.svelte.ts | 4 +-- src/uix/eidos/lib/build-depth.ts | 4 +-- src/uix/eidos/lib/config-types.ts | 2 -- src/uix/eidos/lib/config.ts | 2 +- src/uix/eidos/lib/render-css.ts | 7 +++-- 10 files changed, 36 insertions(+), 28 deletions(-) diff --git a/docs/process/continue-cleanroom-fixes-2026-07.md b/docs/process/continue-cleanroom-fixes-2026-07.md index bb29617a3..d3b3c2c84 100644 --- a/docs/process/continue-cleanroom-fixes-2026-07.md +++ b/docs/process/continue-cleanroom-fixes-2026-07.md @@ -804,7 +804,7 @@ next/font · Fontsource) + pasada adversarial. Constante: **casi cero deuda real | **layer-4** (`--{c}-{role}-{slot}`) | **KEEP** | Tier deliberado del modelo de 7 capas (`reference.md:387-425`), config-backed. La premisa del audit («sin consumidores», §9:424) es **STALE**: lo consumen banner (06-01), toast (10 tokens `color-mix` sin equivalente en L3), avatar-ring, editable, words + el fallback THM-2. MD3 `comp.*`/SLDS/DTCG mantienen el tier 1:1. El ataque «slim» sobre-generalizó (lo confundió con la purga de font-size de accordion/Field). | | **jaula depth-z** | **KEEP** (reserved) | Proyección de `--z-index-*` sobre planos; excluido del bundle a propósito (stacking va por `--{c}-content-z`). Deuda real = 1 comentario falso en `render-css.ts:1170` → ✅ **corregido** este pase. | | **jaula container-queries** | **KEEP** (open cage) | `[data-container]{container-type}` + emisor `@container` por-recipe, ya en `tsc.md` como eje reservado (Tailwind v4 envía CQ en core). Opcional: dogfoodear 1 recipe → «tested end-to-end». | -| **jaula scrim** | ⏳ **decisión de diseño** | Único medio-cableado (write sin reader). El fix «lector en `[data-depth]`» es **incorrecto** (dobla-atenúa el backdrop del componente). (a) completar-consistente — sembrar valor en planos overlay/modal + doc «expuesto, lo consume el backdrop, nunca `[data-depth]`» + test espejo (como depth-z); (b) deprecar-out. Requiere saber si el atmosphere-scrim Fase-4 es un eje real. | +| **jaula scrim** | ✅ **PODADO** (07-12) | Analizado con workflow (mapa + 4 disposiciones + estrés): las 4 colapsan en podar vs promover-a-paridad-z; C (cablear) **se hunde** (contradice el precedente z test-lockeado + consenso MD3/Radix/Vaul), D-reubicar invierte el verbo a retener. **Decisión: podar** — scrim NO gana el open-cage de z (z varía por-plano + es impintable-para-siempre; scrim ni varía ni tiene valor que sembrar, su jaula solo duplicaría `--color-overlay` → alias). Quitado de `DepthPlane`/`DEPTH_CUES`/validación/emisor + `applyDepth` doc; RFC enmendado (contrato + Phase-4 + atmósfera). El veil queda en `--{component}-overlay-*` / `--color-overlay` (= referencia). Cambio observable: cero (nunca se emitió). | | **cascada-vs-firmas** (motion) | Documentar + fijar por orden | La cascada stagger sombrea la firma genérica en ítems de menú, solo por **orden de emisión** (tie 0,3,0), sin doc ni test. Fijar por orden (NO `!important`, que sabotea una futura `@layer`) + test; `@layer` completo = iniciativa L futura. ⏳ **Decisión de diseño pendiente**: ¿la firma de interacción gana a la entrada residual (Framer) o al revés? — los dos análisis discreparon. **No ejecutado** (para no fijar la dirección equivocada). | **Límites deliberados registrados** (gap-log §7 — sin código, todos confirmados NO-bug): diff --git a/docs/process/pendiente.md b/docs/process/pendiente.md index 086dff734..7a0804e5c 100644 --- a/docs/process/pendiente.md +++ b/docs/process/pendiente.md @@ -48,12 +48,6 @@ canon: **progressive enhancement, NO polyfill**. Un Houdini Paint Worklet no exi las máscaras SVG por elemento recortan la sombra (mismo problema que `clip-path`). Revisar el soporte de Safari/Firefox periódicamente y subir el listón cuando lleguen. -## Depth — cue `scrim` - -`--depth-{plane}-scrim` existe como token pero sin regla cableada; el backdrop dim de los modales -lo gestiona hoy cada componente. Cablear una regla `[data-depth][data-scrim]` si emerge un -consumidor que lo necesite fuera de dialog/drawer. - ## Tema completo — más allá de los canales visuales (diferido) `eidos.applyTheme(seed)` (capstone, hecho) compone **solo los 5 canales visuales** (color · diff --git a/docs/rfcs/rfc-depth.md b/docs/rfcs/rfc-depth.md index 6cb53ee53..715ba69e1 100644 --- a/docs/rfcs/rfc-depth.md +++ b/docs/rfcs/rfc-depth.md @@ -53,7 +53,7 @@ everyone picks a side: Tailwind = flexible **without opinion**; Material = ## 3. The novel model — 4 pieces 1. **Unified semantic plane** — `flush · raised · overlay · modal · recessed`. - One level coheres surface + shadow + z + (blur/scrim) at once. Named by + 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 @@ -62,8 +62,10 @@ everyone picks a side: Tailwind = flexible **without opinion**; Material = 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) + scrim: what is in front - blurs/dims what recedes (overlap with the *presence* channel). +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 @@ -91,7 +93,7 @@ the two moments, never collapsed into one. The depth channel is a new channel | **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+scrim on overlays | amount/opacity in config; per-overlay **opt-out** (cost/preference) | +| **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 @@ -111,9 +113,20 @@ Bare-prefixed by system (`--depth-…`). The plane is a **named bundle** that --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) ---depth-{plane}-scrim → atmosphere (token available; no rule — backdrop stays per-component) ``` +> **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` @@ -175,9 +188,10 @@ Nothing existing is renamed → **zero component breakage**. 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 stays available as a - token; modal backdrop dimming remains component-managed, so it was not wired - to a rule.) + `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 diff --git a/docs/theming/changelog.md b/docs/theming/changelog.md index 7366823cd..c39b12b3f 100644 --- a/docs/theming/changelog.md +++ b/docs/theming/changelog.md @@ -948,8 +948,9 @@ es la corrección óptica intrínseca de cada paso. > posterior, junto a los ~7 `letter-spacing` crudos que quedan en CSS de > componentes. -**Pendiente** (menor): el cue `scrim` está disponible como token (`--depth-{plane}-scrim`) pero -sin regla cableada — el backdrop dim de los modales lo gestiona hoy cada componente. +**Resuelto 2026-07-12**: el cue `scrim` fue **podado** (era un token declarado sin sembrar/emitir/ +leer — write-surface sin reader). El backdrop dim de los modales vive en `--{component}-overlay-*` / +`--color-overlay` (= MD3/Radix/Vaul); ninguna referencia pone el veil en un cue de elevación. ## 30. Forma (shape) — continuidad + familias + anidado + eventful (2026-06-05) diff --git a/docs/theming/reference.md b/docs/theming/reference.md index 38d314f11..df83c956b 100644 --- a/docs/theming/reference.md +++ b/docs/theming/reference.md @@ -803,7 +803,7 @@ Where `{prefix}` is one of: | `--floating-gap` / `--floating-gap-{menu·panel}` | Trigger→panel gap canon (config `primitives.floating` — §36 + changelog §40) | `--floating-gap-menu` | | `--radius-factor` / `--radius-default` | The roundness multiplier + the default step (config — changelog §40) | `--radius-factor` | | `--shape-{key}` | Shape family (squircle smoothing, nest gap) — §30 | `--shape-smoothing` | -| `--depth-{plane}-{cue}` | Depth cues per plane (`surface·border·shadow·halo·z·blur·translucency·scrim`) — §29 | `--depth-modal-shadow` | +| `--depth-{plane}-{cue}` | Depth cues per plane (`surface·border·shadow·halo·z·blur·translucency`) — §29 | `--depth-modal-shadow` | | `--focus-ring` / `--focus-ring-{color,width,offset,inner-width,error}` | The two-ring focus family — §32 | `--focus-ring-color` | | `--measure-{key}` | Line-length (measure) scale — §35 | `--measure-narrow` | | `--font-feature-{key}` | `font-feature-settings` presets — §35 | `--font-feature-tabular` | diff --git a/src/uix/eidos/active-eidos.svelte.ts b/src/uix/eidos/active-eidos.svelte.ts index e8e6d3fdb..caafc0aa1 100644 --- a/src/uix/eidos/active-eidos.svelte.ts +++ b/src/uix/eidos/active-eidos.svelte.ts @@ -711,8 +711,8 @@ export class ActiveEidos { /** * Retune depth planes at runtime — the depth analogue of {@link applyColorScheme} / - * {@link applyTypeScale}. Given per-plane cue overrides (surface / shadow / halo / blur / - * scrim / z) it writes a managed block of `--depth-{plane}-{cue}` overrides that wins over + * {@link applyTypeScale}. Given per-plane cue overrides (surface / shadow / halo / blur / z) + * it writes a managed block of `--depth-{plane}-{cue}` overrides that wins over * the static foundation, so every component on a retuned plane follows. The jaula-abierta * runtime of the depth channel. */ diff --git a/src/uix/eidos/lib/build-depth.ts b/src/uix/eidos/lib/build-depth.ts index 030e0a44c..ccc1d756a 100644 --- a/src/uix/eidos/lib/build-depth.ts +++ b/src/uix/eidos/lib/build-depth.ts @@ -2,7 +2,7 @@ * Runtime depth retune — the depth analogue of `build-scheme.ts` (color) and * `build-type-scale.ts` (typography). Pure + isomorphic, DOM-free. Given per-plane cue * overrides it emits the `--depth-{plane}-{cue}` declarations for a managed `:root` block, - * so an app can retune any plane's surface / shadow / halo / blur / scrim / z at runtime — + * so an app can retune any plane's surface / shadow / halo / blur / z at runtime — * the jaula-abierta runtime of the depth channel. * * Unlike the color / type builders there is no seed math here: depth shadows reference the @@ -16,7 +16,7 @@ import type { DepthPlane } from './config-types'; /** Cue keys a plane can override, mirroring the foundation emission order. */ -const DEPTH_CUES = ['surface', 'border', 'shadow', 'halo', 'z', 'blur', 'translucency', 'scrim'] as const; +const DEPTH_CUES = ['surface', 'border', 'shadow', 'halo', 'z', 'blur', 'translucency'] as const; /** Per-plane cue overrides — `{ overlay: { shadow, halo }, modal: { blur } }`. */ export type DepthOverrides = Record>; diff --git a/src/uix/eidos/lib/config-types.ts b/src/uix/eidos/lib/config-types.ts index 2d96c205c..cf969e5d4 100644 --- a/src/uix/eidos/lib/config-types.ts +++ b/src/uix/eidos/lib/config-types.ts @@ -900,8 +900,6 @@ export interface DepthPlane { * rule; `80%` is the fallback when a frosted plane declares none. (atmosphere — Fase 4) */ readonly translucency?: string; - /** Backdrop scrim (atmosphere — Fase 4). */ - readonly scrim?: string; } /** diff --git a/src/uix/eidos/lib/config.ts b/src/uix/eidos/lib/config.ts index 1a5225a7f..35799afcd 100644 --- a/src/uix/eidos/lib/config.ts +++ b/src/uix/eidos/lib/config.ts @@ -957,7 +957,7 @@ function validateDepthPrimitives(options: EidosConfig, issues: EidosValidationIs issues.push({ path, message: 'depth plane must be a plain object' }) continue } - for (const cue of ['surface', 'shadow', 'halo', 'z', 'blur', 'scrim'] as const) { + for (const cue of ['surface', 'shadow', 'halo', 'z', 'blur'] as const) { const value = (cues as Record)[cue] if (value !== undefined) validateNonEmptyCssValue(`${path}.${cue}`, value, issues) } diff --git a/src/uix/eidos/lib/render-css.ts b/src/uix/eidos/lib/render-css.ts index 9a098b813..fd6db37fd 100644 --- a/src/uix/eidos/lib/render-css.ts +++ b/src/uix/eidos/lib/render-css.ts @@ -1151,7 +1151,7 @@ function appendTypographyDeclarations( /** Emit `--depth-{plane}-{cue}` tokens — each plane's cue values composed into `:root`. */ function appendDepthDeclarations(declarations: string[], depth: DepthPrimitiveSet): void { for (const [plane, cues] of Object.entries(depth.planes)) { - for (const cue of ['surface', 'border', 'shadow', 'halo', 'z', 'blur', 'translucency', 'scrim'] as const) { + for (const cue of ['surface', 'border', 'shadow', 'halo', 'z', 'blur', 'translucency'] as const) { const value = cues[cue] if (value !== undefined) declarations.push(cssVar(`depth-${plane}-${cue}`, value)) } @@ -1172,8 +1172,9 @@ function appendDepthDeclarations(declarations: string[], depth: DepthPrimitiveSe * z on the surface would force a redundant stacking context on elements already stacked that way. * The `--depth-{plane}-z` tokens are the plane's projection of that scale, kept as a documented but * UNWIRED reserved read-surface — no rule or wrapper consumes them today (clean elevation-vs-actual- - * stacking separation; F4-D adjudication 2026-07-12). `blur`/`scrim` (glass) stay opt-in via - * `data-frost`. (Decisión 8) + * stacking separation; F4-D adjudication 2026-07-12). `blur` (glass) stays opt-in via `data-frost`; + * the depth `scrim` cue was pruned (2026-07-12) — the modal veil lives in the component overlay + * layer (`--{component}-overlay-*` tinted by `--color-overlay`), never on the plane. (Decisión 8) */ function renderDepthBlocks(depth: DepthPrimitiveSet): string[] { const blocks: string[] = []