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/process/gradient-finish-plan-2026-0...

177 lines
10 KiB

eidos(gradient-finish): acabado anclado a la sombra de la tinta - v1 Button+Badge + dial temable + guard El gradiente entra al sistema como ACABADO (material) del fill, jamas como valor del eje de color: prop `gradient` -> attr eidos-only `data-gradient` (familia data-variant; NO morfo - el runtime lo resolveria desde props de soma y mergeProps clobberea el stamp del wrapper). - Fill por capas: background-color = base solida (degrada sola en forced-colors) + background-image = rampa derivada de los slots de LA instancia (roles + 33 escalas + custom gratis, tinta heredada). - Dial unico de tema: primitives.gradientFinish.lift (26%) -> --gradient-finish-lift (0% = apagado; override por instancia via cascada). - RAMPA ANCLADA a la sombra de la tinta ("la rampa huye de la tinta"): tinta blanca -> #000 fuerte abajo (CTA sombreado); tinta oscura -> #fff fuerte arriba (glossy). Ancla+angulo por color x modo con el MISMO flip del slot contrast. Rectificacion MEDIDA: el lift global hacia blanco rompia la tinta heredada en 52/84 combos a 26% (techo global 0%) - el contraste ahora solo puede mejorar: dial sin topes. - Guard ejecutable gradient-finish-guard.test.ts (3/3): no-regresion <=40% sobre 84 combos + set flat-fail clavado (cyan/orange, deuda on-solid preexistente). - Generador emite la VAR (--_{c}-fill-finish); el recipe pinta (solid + re-assert en hover: su shorthand background resetea el longhand). - Lab temas/gradientes: 9 casos con componentes reales sobre el token real (dial en vivo, polaridad observable, evidencia de pasos 7/11, forced-colors). - Docs: capitulo theming/gradient-finish.md (registro de decisiones D1-D9, alternativas rechazadas con evidencia, leccion de proceso: medir ANTES de fijar defaults) + reference.md paragrafo 39 + changelog paragrafo 42 + plan docs/process/gradient-finish-plan-2026-07.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
# Gradient finish — plan de implementación (2026-07)
> Estado: **v1 en ejecución** (2026-07-15). Diseño cerrado tras tres análisis + revisión
> de coherencia + investigación de frameworks de referencia + mockup interactivo
> verificado en Chrome real. Este documento es el plan operativo; la doctrina queda
> en `docs/theming/reference.md` (sección «Gradient finish») cuando aterrice.
## La decisión (resumen ejecutivo)
**El gradiente es un ACABADO (finish) del fill, no una identidad de color.**
- `color` sigue diciendo *quién* es el componente (rol / escala / custom) — intacto.
- El prop nuevo `gradient` decide *cómo se pinta su fill saturado*: el wrapper eidos
estampa el attr `data-gradient` (eidos-only, familia `data-variant`/`data-size`;
NO se declara en morfo — ver la fila del checklist: el runtime lo resolvería desde
el espacio de props de soma y clobbearía el stamp).
- Taxonomía: color = identidad · variant = porte · depth/frost = material ·
state layer = feedback → el gradiente entra como **material/acabado**, pariente
de frost, jamás como valor de `data-color`. (El diseño «gradiente como valor del
eje color» fue analizado y descartado: un `<image>` no puede cumplir el contrato
del eje — 10 slots derivados mecánicamente + toda variante lo expresa. La historia
completa vive en la memoria del proyecto y en el mockup.)
## La receta v1 (rampa derivada, un dial)
Fill por capas:
```
background-color = la base sólida (--_{c}-bg — sin cambio; degrada en forced-colors)
background-image = acabado (--_{c}-fill-finish) [+ veil del state layer, ver «Diferido»]
```
El acabado se compone EN CSS desde los slots que la identidad ya provee, escalado
por **un solo dial del tema**.
> **Desviación declarada al implementar (2026-07-15):** el hover del recipe es
> `[data-button]:hover:not(...):not(...)` = (0,4,0) con shorthand `background:` —
> un re-assert genérico del generador a (0,3,0) PERDERÍA. Reparto final:
> **el generador emite solo la VAR** (`--_{c}-fill-finish`) y **el recipe pinta**
> (base + su propio hover guard) — coherente con «el generador fabrica, el
> recipe decide dónde pinta», como las variantes.
> **Rectificación medida (2026-07-15, antes de commitear):** el lift global
> hacia blanco rompía la tinta heredada en **52/84** combos a 26% (sonda con
> floors APCA≥60∧WCAG≥3; techo global seguro = 0% — teal 1%, azules/verdes de
> modo oscuro 0%: el paso 9 no tiene margen hacia el blanco en media paleta).
> Forma FINAL: **rampa ANCLADA al lado de sombra de la tinta** («la rampa huye
> de la tinta») — tinta blanca → ancla `#000`, extremo fuerte abajo (CTA
> sombreado); tinta oscura → ancla `#fff`, extremo fuerte arriba (glossy).
> Ancla + ángulo se resuelven por color×modo con el MISMO flip del slot
> `contrast`. El contraste solo puede mejorar → seguridad constructiva, dial
> sin topes (0 regresiones hasta 40%). Guard: `gradient-finish-guard.test.ts`.
```css
/* generated/base.css — renderRecipeGradientFinish (gate v1: button, badge) */
[data-button][data-gradient] {
--_button-finish-anchor: var(--color-primary-finish-anchor);
--_button-finish-angle: var(--color-primary-finish-angle);
--_button-fill-finish: linear-gradient(
var(--_button-finish-angle),
color-mix(in oklch, var(--button-palette-solid), var(--_button-finish-anchor) calc(var(--gradient-finish-lift) / 3)),
color-mix(in oklch, var(--button-palette-solid-hover), var(--_button-finish-anchor) var(--gradient-finish-lift))
);
}
[data-button][data-gradient][data-color] {
--_button-finish-anchor: var(--palette-finish-anchor, var(--color-primary-finish-anchor));
--_button-finish-angle: var(--palette-finish-angle, var(--color-primary-finish-angle));
}
/* button.css — el recipe pinta el fill saturado + re-assert en hover */
[data-button][data-gradient][data-variant='solid'] {
background-image: var(--_button-fill-finish);
}
[data-button][data-gradient][data-variant='solid']:hover:not([data-disabled]):not([data-loading]) {
background-image: var(--_button-fill-finish);
}
```
- **Dial**: `--gradient-finish-lift` (default `26%`), emitido por el generador desde
`primitives.gradientFinish.lift` — mismo patrón que las magnitudes del state layer
(config data, §38/§40 del theming reference). `lift: 0%` ≈ acabado apagado.
- **Derivación**: claro = `solid + lift` · profundo = `solid-hover − lift/3`.
Un dial escala toda la rampa. Override por instancia gratis (cascada).
- **Tinta**: HEREDADA (`palette-contrast`) — el extremo profundo (paso 10) ya está
validado por práctica shipped; el extremo claro (lift) lo acota el guard de build
(pendiente, ver Cola).
- **Por qué no saltar pasos (9→7 / 9→11)**: la escala de 12 pasos es funcional y
relativa al modo (7 = borde, 11 = texto; en oscuro el 11 es claro `#d19dff` →
la rampa se invertiría y mataría la tinta; en claro 9/10/11 están apiñados,
ΔL ≈ 0.04). El único ancla estable entre modos es el solid — por eso el lift se
define relativo al solid. Valores verificados en `generated/base.css`.
## Fases
### v1 — rampa derivada en Button (EN EJECUCIÓN)
| Pieza | Fichero | Estado |
| --- | --- | --- |
| Config `gradientFinish.lift` | `src/uix/eidos/lib/config-types.ts` | ✅ 2026-07-15 |
| Default `26%` | `src/uix/eidos/lib/primitives/static.ts` | ✅ |
| Emisión `--gradient-finish-lift` | `src/uix/eidos/lib/render-css.ts` | ✅ |
| Var finish por recipe (gate v1: button) + pintura en recipe | `render-css.ts` + `button.css` | ✅ (ver desviación) |
| Attr `data-gradient` | eidos-only de wrapper (familia `data-variant`) — **NO morfo**: declararlo hizo que el runtime lo resolviera desde props de soma → `undefined` → `mergeProps` clobbereó el stamp (bug cazado en vivo 2026-07-15 y revertido) | ✅ |
| Prop `gradient?: boolean` + estampado | `src/uix/eidos/components/button/{button.svelte,types.ts}` | ✅ |
| Regenerar `generated/base.css` | `npm run generate:eidos-css` | ✅ (dial en `:root:224`, var en `:4123`) |
| README de Button (prop nueva) | `src/uix/eidos/components/button/README.md` | ✅ |
| Lab del eje | `web/routes/temas/gradientes/+page.svelte` | ✅ |
| Doctrina + changelog | `reference.md §39` + `changelog.md §42` | ✅ |
| Checks | contrato 26/28 (2 fallos preexistentes de `proof-of-human`, ajenos) · audit 144/144 · eidos-lint button `invalid: 0` · svelte-check sin errores nuevos | ✅ |
| Navegador real (lab + dial + hover + claro/oscuro) | — | ☐ |
### v1.x — cola inmediata (no bloquea v1)
- ✅ **Guard del dial** (2026-07-15): `src/uix/eidos/gradient-finish-guard.test.ts`
— no-regresión ≤40% sobre 84 combos + set flat-fail clavado (cyan/orange,
deuda preexistente del on-solid). Con la rampa anclada el guard pasa 3/3.
- ✅ **Badge** (2026-07-15): gate ampliado (paleta privada; sin `solid-hover` →
extremo profundo cae a `solid`); attr eidos-only de wrapper (NO morfo).
- ✅ **Override por tema/modo del lift** (2026-07-15):
`ThemeDefinition.gradientFinish.lift` → re-emisión en el bloque de tema
(misma especificidad, después en cascada → el tema gana; `0%` apaga por
tema). Test: `active-eidos-config.test.ts` («per-theme gradient-finish dial
override»). **v1.x COMPLETA.**
eidos(gradient-finish): acabado anclado a la sombra de la tinta - v1 Button+Badge + dial temable + guard El gradiente entra al sistema como ACABADO (material) del fill, jamas como valor del eje de color: prop `gradient` -> attr eidos-only `data-gradient` (familia data-variant; NO morfo - el runtime lo resolveria desde props de soma y mergeProps clobberea el stamp del wrapper). - Fill por capas: background-color = base solida (degrada sola en forced-colors) + background-image = rampa derivada de los slots de LA instancia (roles + 33 escalas + custom gratis, tinta heredada). - Dial unico de tema: primitives.gradientFinish.lift (26%) -> --gradient-finish-lift (0% = apagado; override por instancia via cascada). - RAMPA ANCLADA a la sombra de la tinta ("la rampa huye de la tinta"): tinta blanca -> #000 fuerte abajo (CTA sombreado); tinta oscura -> #fff fuerte arriba (glossy). Ancla+angulo por color x modo con el MISMO flip del slot contrast. Rectificacion MEDIDA: el lift global hacia blanco rompia la tinta heredada en 52/84 combos a 26% (techo global 0%) - el contraste ahora solo puede mejorar: dial sin topes. - Guard ejecutable gradient-finish-guard.test.ts (3/3): no-regresion <=40% sobre 84 combos + set flat-fail clavado (cyan/orange, deuda on-solid preexistente). - Generador emite la VAR (--_{c}-fill-finish); el recipe pinta (solid + re-assert en hover: su shorthand background resetea el longhand). - Lab temas/gradientes: 9 casos con componentes reales sobre el token real (dial en vivo, polaridad observable, evidencia de pasos 7/11, forced-colors). - Docs: capitulo theming/gradient-finish.md (registro de decisiones D1-D9, alternativas rechazadas con evidencia, leccion de proceso: medir ANTES de fijar defaults) + reference.md paragrafo 39 + changelog paragrafo 42 + plan docs/process/gradient-finish-plan-2026-07.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- **Composición del veil del state layer**: `background-image: veil, var(--_{c}-fill-finish, none)`
a nivel archetype — solo necesaria cuando un componente que DEPENDE del veil
(item/option) gane gradiente; Button/Badge no lo necesitan (su hover re-asserta).
### v1.5 — kind `spread` ✅ (2026-07-15)
MEDIDO ANTES DE IMPLEMENTAR (doctrina D6b, esta vez en orden): (1) sonda de
84 combos — la rotación pura a L constante rompía grass (±4°) y gold (±27°)
porque L de OKLCH ≠ luminancia; con la mezcla débil del ancla (lift/3) en
ambos stops: **0 fallos hasta ±45°**; (2) RCS en Chromium por valores
computados — el canal `h` es `<number>`: `calc(h ± 30deg)` computa `none` →
el token `--gradient-finish-spread` es SIN unidad (`'30'`). Shipped:
`gradient="spread"` en Button+Badge (`data-gradient='spread'` overridea la
var del acabado; cero cambios de recipe CSS), guard 4/4, lab con especímenes
reales (incl. gris ≈plano, C≈0). Registro: D10 en
`docs/theming/gradient-finish.md`.
eidos(gradient-finish): acabado anclado a la sombra de la tinta - v1 Button+Badge + dial temable + guard El gradiente entra al sistema como ACABADO (material) del fill, jamas como valor del eje de color: prop `gradient` -> attr eidos-only `data-gradient` (familia data-variant; NO morfo - el runtime lo resolveria desde props de soma y mergeProps clobberea el stamp del wrapper). - Fill por capas: background-color = base solida (degrada sola en forced-colors) + background-image = rampa derivada de los slots de LA instancia (roles + 33 escalas + custom gratis, tinta heredada). - Dial unico de tema: primitives.gradientFinish.lift (26%) -> --gradient-finish-lift (0% = apagado; override por instancia via cascada). - RAMPA ANCLADA a la sombra de la tinta ("la rampa huye de la tinta"): tinta blanca -> #000 fuerte abajo (CTA sombreado); tinta oscura -> #fff fuerte arriba (glossy). Ancla+angulo por color x modo con el MISMO flip del slot contrast. Rectificacion MEDIDA: el lift global hacia blanco rompia la tinta heredada en 52/84 combos a 26% (techo global 0%) - el contraste ahora solo puede mejorar: dial sin topes. - Guard ejecutable gradient-finish-guard.test.ts (3/3): no-regresion <=40% sobre 84 combos + set flat-fail clavado (cyan/orange, deuda on-solid preexistente). - Generador emite la VAR (--_{c}-fill-finish); el recipe pinta (solid + re-assert en hover: su shorthand background resetea el longhand). - Lab temas/gradientes: 9 casos con componentes reales sobre el token real (dial en vivo, polaridad observable, evidencia de pasos 7/11, forced-colors). - Docs: capitulo theming/gradient-finish.md (registro de decisiones D1-D9, alternativas rechazadas con evidencia, leccion de proceso: medir ANTES de fijar defaults) + reference.md paragrafo 39 + changelog paragrafo 42 + plan docs/process/gradient-finish-plan-2026-07.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
### v2 — named finishes + Surface
- ✅ **Primitiva `Surface`** (v2.1, 2026-07-15): Box + tratamiento — compone
`<Box>` (patrón Section) y estampa `data-surface` + `color`/`variant`
(soft/solid)/`gradient` (ambos kinds)/`rounded`; recipe palette-tint espejo
de Card sin chrome (`_palette-*` 5×8 → THM-2: roles + 33 escalas gratis);
morfo declarativo patrón Box (0 eventos justificados); audit **145/145**;
verificada por valores en navegador (rampa/spread/soft/tinta contrast).
Ficha con Decisiones/Gaps propios: `components/surface/README.md`.
eidos(gradient-finish): v2.2 named finishes + on/data-on - EL PLAN COMPLETO D11 - Named finishes (el diseno A rescatado como opt-in explicito): - gradientFinish.named opta gradientes del open cage como acabado, cada uno con su tinta AUTORADA obligatoria (--gradient-{name}-ink, override de la convencion --_{c}-fg a especificidad (0,3,0) sobre el slice de variante). - La base sigue siendo el solid de la identidad (fill por capas, D2) -> el aurora shipped son blobs de rol con alfa SIN color base final: background-image-valido POR ARQUITECTURA (el caveat del mesh disuelto, no exceptuado), y re-tine por tema y modo via referencias de rol. - Honestidad documentada: la validacion numerica al peor stop de un string CSS arbitrario no es implementable (stops desconocidos); posible solo para gradientes de modelo (buildGradient) - diferida. El guard clava la sanidad del config (named subconjunto de gradients + ink presente). D12 - on/data-on minimo (Surface on="light|dark"): - La foundation re-vincula --color-content-*/--color-border-default para el subarbol (dark -> tinta on-solid; light -> on-solid-contrast; mixes oklch 82/64/32%). Verificado EN VIVO: un parrafo muted dentro del aurora computa la tinta del contexto al 64%. - Limites POR CONSTRUCCION y documentados en cada consumidor: componentes anidados con tokens propios y contenido portaleado NO se re-entintan (la inversion completa sigue siendo iniciativa independiente). forced-colors: el contexto resuelve a CanvasText/GrayText (bloque extendido). Sin color-scheme a proposito (solo chrome UA). Ademas: prop gradient ampliada a (string & {}) para named en Button/Badge/ Surface; poda del orphan-guard (border/text de Surface: 3 slots x 8 colores, lo que las variantes consumen); guard 5/5; audit 145/145; lab CASO 07 con el aurora REAL (<Surface gradient="aurora" on="dark">) verificado por valores. Docs: capitulo D11+D12 + registro D1-D12 + roadmap COMPLETO; reference p39; changelog p42; plan cerrado (quedan declaradas: inversion completa, demo propia de Surface, validacion de modelo para tintas named). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
- ✅ **Named finishes** (v2.2, 2026-07-15 — D11): `gradientFinish.named` opta
gradientes del open cage como acabado CON tinta autorada obligatoria
(`--gradient-{name}-ink`, override de `--_{c}-fg` a (0,3,0)); la base sigue
siendo el solid de la identidad (fill por capas) → `aurora` shipped como
blobs de rol con alfa SIN color base final. Honestidad: la validación
numérica al peor stop solo es posible para gradientes de modelo
(`buildGradient`) — diferida y documentada; el guard clava la sanidad del
config (named ⊆ gradients + ink presente).
- ✅ **`on`/`data-on` mínimo** (v2.2 — D12): `<Surface on="dark">` re-vincula
`--color-content-*`/`--color-border-default` para el subárbol (verificado
en vivo: muted = tinta del contexto al 64%); forced-colors → `CanvasText`.
Límites POR CONSTRUCCIÓN: anidados con tokens propios y portales excluidos
— la inversión COMPLETA de subárbol sigue siendo iniciativa independiente
(Card solid la necesita hoy sin gradientes).
**EL PLAN ESTÁ COMPLETO (2026-07-15).** Fuera del plan quedan, declarados:
la iniciativa de inversión completa de subárbol, la demo propia
`web/routes/uix/components/surface` (gap en la ficha de Surface) y la
validación de modelo para tintas de named finishes.
eidos(gradient-finish): acabado anclado a la sombra de la tinta - v1 Button+Badge + dial temable + guard El gradiente entra al sistema como ACABADO (material) del fill, jamas como valor del eje de color: prop `gradient` -> attr eidos-only `data-gradient` (familia data-variant; NO morfo - el runtime lo resolveria desde props de soma y mergeProps clobberea el stamp del wrapper). - Fill por capas: background-color = base solida (degrada sola en forced-colors) + background-image = rampa derivada de los slots de LA instancia (roles + 33 escalas + custom gratis, tinta heredada). - Dial unico de tema: primitives.gradientFinish.lift (26%) -> --gradient-finish-lift (0% = apagado; override por instancia via cascada). - RAMPA ANCLADA a la sombra de la tinta ("la rampa huye de la tinta"): tinta blanca -> #000 fuerte abajo (CTA sombreado); tinta oscura -> #fff fuerte arriba (glossy). Ancla+angulo por color x modo con el MISMO flip del slot contrast. Rectificacion MEDIDA: el lift global hacia blanco rompia la tinta heredada en 52/84 combos a 26% (techo global 0%) - el contraste ahora solo puede mejorar: dial sin topes. - Guard ejecutable gradient-finish-guard.test.ts (3/3): no-regresion <=40% sobre 84 combos + set flat-fail clavado (cyan/orange, deuda on-solid preexistente). - Generador emite la VAR (--_{c}-fill-finish); el recipe pinta (solid + re-assert en hover: su shorthand background resetea el longhand). - Lab temas/gradientes: 9 casos con componentes reales sobre el token real (dial en vivo, polaridad observable, evidencia de pasos 7/11, forced-colors). - Docs: capitulo theming/gradient-finish.md (registro de decisiones D1-D9, alternativas rechazadas con evidencia, leccion de proceso: medir ANTES de fijar defaults) + reference.md paragrafo 39 + changelog paragrafo 42 + plan docs/process/gradient-finish-plan-2026-07.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3 months ago
## Referencias
- Mockup interactivo de los 9 casos (verificado en Chrome real, dial en vivo):
artifact `38814ae1-7922-46b2-b344-12dc9528316d` (claude.ai).
- Memoria del proyecto: `project_gradient_as_color_2026-07-14` (historia completa:
diseño A descartado, mapa de 3 mecanismos de fondo, research de referencia,
revisión de coherencia).

Powered by TurnKey Linux.