D-BG.20. El scrim tomaba prestado `--color-overlay`, que es `surface.backdrop`:
el dim de los modales (MD3/Radix/Vaul según el changelog), afinado POR MODO
porque una página clara necesita menos atenuación que una oscura. Es una
herramienta de ATENUACIÓN, y el scrim la usaba como tinta de LEGIBILIDAD — la
misma enfermedad que la escala `--opacity-*` prestada que se arregló esta misma
mañana, y que trajo tres defectos.
El primero, que la misma fotografía se leía distinto según el modo: 1,48:1 en
claro y 2,10:1 en oscuro. Una fotografía no cambia con el modo, y la legibilidad
no es un eje que el modo pueda mover. El segundo, un TECHO: el alpha propio de
la tinta (0,42 / 0,66) acotaba la escala entera, de modo que ni el peso máximo
alcanzaba AA y ningún paso nuevo podría haberlo alcanzado. El tercero es el que
no había visto y es el peor: con `on="light"` el velo era OSCURO bajo tinta
oscura. El scrim combatía a la tinta que existía para sostener.
La tinta del velo pasa a ser el SUELO de la tinta que `on` puso en vigor, que es
lo que D12 ya define: `--color-content-on-solid-contrast` bajo `on="dark"`,
`--color-content-on-solid` bajo `on="light"`, y `--color-surface-default` sin
`on`, porque ahí la copia toma la tinta de la página. Tres roles que ya existían
—ninguno inventado, §16.C— consumidos como capa 4 → capa 3 (§3) y sin alias. Con
la tinta opaca el peso ES el alpha, así que `strength` significa una sola cosa;
antes significaba dos, porque con `color="teal"` el ink ya era `--palette-solid`,
opaco, y sólo el default era translúcido.
La escala se re-afina por el TRABAJO de cada paso, medida con la matemática del
propio framework (`$color`: `apcaLc` + `wcagContrastRatio`) sobre la peor obra de
cada contexto: `xs` 0,08 · `sm` 0,13 · `md` 0,19 · `lg` 0,40 · `xl` 0,70. `xl` es
el único paso que promete legibilidad sobre CUALQUIER fotografía, y lo promete
contra los cuatro suelos que el framework ya usa —el criterio del par on-solid
(`lib/on-solid.ts`: APCA |Lc| ≥ 60 ∧ WCAG ≥ 3) y el AA 4,5 de §40— en ambos
contextos: `on="dark"` sobre foto blanca 6,45:1 con Lc 85, `on="light"` sobre
foto negra 8,29:1 con Lc 61. 0,70 es el mínimo que cierra los cuatro; el más
exigente, el APCA de `on="light"`, pedía 0,695. Los pasos por debajo son
atmósfera y el README lo dice: no son garantías.
Verificado en Chrome, no calculado: sin `on` el velo sale `oklch(0.9911 0 0 /
0.19)`, el suelo de la página en claro; con `on="dark"`, `#1c1917` al 19%; con
`on="light"`, BLANCO al 19% bajo tinta oscura, que es el arreglo; `xl` en
`on="light"` sobre foto negra mide 8,25:1 por píxel donde `$color` predijo 8,29.
Y el consumidor real: el hero en layout `background` pinta `#1c1917 / 0.19` con
titular blanco y da 1,48 / 5,17 — exactamente lo que daba antes en modo claro,
paridad a la cifra. Los heroes en modo oscuro se aclaran de 0,297 a 0,19, que es
el modo soltando una decisión que nunca fue suya.
El selector de contexto va envuelto en `:where()`, y no por estética: a pelo
llega a (0,4,0) y le gana a `[data-color]`, de modo que un
`<Background.Scrim color="teal">` dentro de un stack `on="dark"` pintaba el suelo
en vez de teal — el sistema de color abierto de §25 derrotado por un selector de
conveniencia. Envuelto, la familia baja a (0,2,0): el contexto gana al bloque
base por orden y pierde contra el color explícito. Medido antes y después.
Queda anotado que el vignette sigue consumiendo `--color-overlay`, y ahí es
correcto: es una trama que atenúa bordes, no un velo de legibilidad.
Esta decisión salió de leer la doctrina entera después de que el autor preguntara
si mi recomendación era conforme al sistema de color. No lo era: yo proponía una
tinta opaca única, que servía sólo a `on="dark"` e ignoraba los otros dos
contextos, y medía el contraste con un canvas WCAG-only en vez de con los suelos
del framework. Los tres puntos los corrigió la documentación.
Gates: audit PASS 0/0 · eidos-lint invalid 0, class-hooks 0 · rtl:check 0/180 ·
vitest eidos 434/435 (el rojo es `skin-media-player`, el de siempre) · `check`
con los mismos 72 errores preexistentes y ninguno propio · prettier limpio.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
| Stacked layers with blend / weight / mask | ✓ one primitive | ✗ (two components, no stack) | ✗ | ✓ (layers array) | ✗ (one effect = one wrapper) |
| Host adopted automatically | ✓ (`:has`, foundation) | ✗ (the parent must be positioned; silent when it is not) | n/a | ✗ | ✗ |
| Patterns from theme tokens | ✓ 8, re-tint per theme + mode | ✗ | ✗ | ✗ | ✓ but colours hand-picked per snippet |
| Scrim / overlay | ✓ (flat · graded · frosted; ink = the floor of the ink in force, one step measured to clear AA over any photo) | ✓ `Overlay` (color + opacity + blur — the consumer picks the number) | ✗ | ✗ | ad hoc |
| ~~The `strength` scale is not ordered by veil weight~~ — `subtle` (0.80) veiled MORE than `overlay` (0.65), which tied with `muted`. Measured 2026-08-17 | **RESUELTO 2026-08-18** — the veil got its own five-step scale (`xs`…`xl`), ordered by construction, `md` holding the shipped 0.45. See §Decisiones |
| **A bright photograph is illegible at EVERY weight, and the scale has a ceiling** — 1.48:1 at the default, 2.66:1 at `xl`, which already spends the whole ink. Re-measured per pixel 2026-08-18 (the old 2.10 / 4.42 assumed an ink at 0.66) | **decisión del autor** — no extra step can fix it: the bound is the ink's own alpha (0.42). Either the veil stops inheriting that alpha (an opaque ink reaches 5.45:1 at weight 0.65, 9.22:1 at 0.80 — measured), which changes what a scrim IS and its relation to the modal veil, or the doctrine is that the graded scrim is the answer |
| The hero's `background` layout still hand-builds its layers | **implementar** — F2, once `Image`/`Video` exist |
| `Ambient` reading the context to pause its scene | **diferir** — a task of the PACK (D-BG.8), after F2 |
| Per-layer scroll velocity in a nested scroller | **descartar** — the fondo does not orchestrate content; `ScrollFrames` owns scrub, and pinned storytelling is another initiative |
| ~~The `strength` scale is not ordered by veil weight~~ — `subtle` (0.80) veiled MORE than `overlay` (0.65), which tied with `muted`. Measured 2026-08-17 | **RESUELTO 2026-08-18** — the veil got its own five-step scale (`xs`…`xl`), ordered by construction, `md` holding the shipped 0.45. See §Decisiones |
| ~~A bright photograph is illegible at EVERY weight, and the scale has a ceiling~~ — 1.48:1 at the default and 2.66:1 at the top, because the borrowed ink's own alpha bounded it. Re-measured per pixel 2026-08-18 | **RESUELTO 2026-08-18** — the veil's ink became the FLOOR of the ink in force (opaque, three existing roles), so weight IS alpha and the ceiling is gone: `xl` clears both framework floors in both contexts over the worst artwork (6.45:1 / Lc 85 and 8.29:1 / Lc 61). The same change fixed `on="light"`, whose veil used to be DARK under dark ink. See §Legibility |
| The hero's `background` layout still hand-builds its layers | **implementar** — F2, once `Image`/`Video` exist |
| `Ambient` reading the context to pause its scene | **diferir** — a task of the PACK (D-BG.8), after F2 |
| Per-layer scroll velocity in a nested scroller | **descartar** — the fondo does not orchestrate content; `ScrollFrames` owns scrub, and pinned storytelling is another initiative |