refactor(eidos): prune the depth `scrim` cue (F4-D scrim decision)

`--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 <noreply@anthropic.com>
menubar-v4-safe
dev 3 months ago
parent 2742e4e6de
commit a52f80ceab

@ -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):

@ -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 ·

@ -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

@ -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)

@ -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` |

@ -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.
*/

@ -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<string, Partial<DepthPlane>>;

@ -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;
}
/**

@ -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<string, unknown>)[cue]
if (value !== undefined) validateNonEmptyCssValue(`${path}.${cue}`, value, issues)
}

@ -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[] = []

Loading…
Cancel
Save

Powered by TurnKey Linux.