From 9f24a31eb93ca3da6cdddefbdaa141a63a01988a Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 22 Aug 2026 01:14:14 +0200 Subject: [PATCH] =?UTF-8?q?uix(mark):=20temable=20=E2=80=94=200=20%=20?= =?UTF-8?q?=E2=86=92=2060=20%,=20y=203/3=20en=20el=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3 claves fusionadas en el bloque `mark` de `recipes/base.ts` (donde ya vivían los dos forwards de paleta). Censo 0 % → 60 %, cero globales y cero literales. **No tiene ni un knob de tipo, y es deliberado**: un resaltado hereda la tipografía ambiente del párrafo en que vive (`font: inherit`, `color: inherit`). Lo que posee es la decoración — el padding en sus dos ejes y el radio. **El padding es em-relativo y deliberadamente diminuto** (0.16em / 0.04em): el tinte tiene que leerse SIN romper el interlineado del párrafo que lo contiene, y eso es exactamente lo que esos dos números protegen. Queda escrito en el README para que quien los mueva sepa que está tocando el ritmo vertical del texto, no sólo el grosor del resaltado. Los dos forwards de paleta siguen privados (THM-2): el fondo tiñe con el rol y la tinta sólo cuando hay `data-color`, porque el defecto es `inherit`. Son el 40 % que no alcanza. Artefactos y gates: - Sonda antes/después = 0 diffs en 174 valores × 7 estados. - R-5.4: **3/3**, sin una sola entrada en el ledger. - censo --only 60 % · component:audit PASS · suite eidos sin rojos nuevos · rtl:check 0 · docs:check 0 · check por fichero limpio. - README con «Talla y tema» y tab `Tokens` en la demo (3 claves). Co-Authored-By: Claude Fable 5 --- docs/audit/theming/README.md | 12 +++---- docs/audit/theming/mark.md | 37 +++++++++++---------- src/uix/eidos/components/mark/README.md | 23 +++++++++++++ src/uix/eidos/components/mark/mark.css | 6 ++-- src/uix/eidos/generated/base.css | 3 ++ src/uix/eidos/lib/recipes/base.ts | 7 ++++ web/routes/uix/components/mark/+page.svelte | 10 +++++- 7 files changed, 71 insertions(+), 27 deletions(-) diff --git a/docs/audit/theming/README.md b/docs/audit/theming/README.md index 83af524cc..5a0b825c4 100644 --- a/docs/audit/theming/README.md +++ b/docs/audit/theming/README.md @@ -10,9 +10,9 @@ - **Medido**: 2026-08-21 · **162 recetas** con CSS + **8 componentes sin receta** = 170 fichas, el árbol entero de `eidos/components/` - **La pregunta**: ¿cuánto de la apariencia de cada componente puede cambiar un tema **sin tocar el sistema ni la receta**? -- **Alcance global**: **50%** — 2326 de 4676 knobs pasan por un token público del componente -- **Reparto**: público 2326 · privado 627 · global 1157 · literal 566 · sistema transversal 509 _(fuera del ratio)_ -- **Sin token público propio**: 31 · **alcance < 20 %**: 24 · **alcance 100 %**: 10 · **con eje `size`**: 59 +- **Alcance global**: **50%** — 2329 de 4676 knobs pasan por un token público del componente +- **Reparto**: público 2329 · privado 627 · global 1156 · literal 564 · sistema transversal 509 _(fuera del ratio)_ +- **Sin token público propio**: 30 · **alcance < 20 %**: 23 · **alcance 100 %**: 10 · **con eje `size`**: 59 ## Cómo se lee @@ -91,12 +91,12 @@ theming §6.7 lo pone delante (`hover-bg`). Las fichas proponen la forma | 19 | [timeline](./timeline.md) | 24 | 51% | 49 | 45 | | 20 | [avatar](./avatar.md) | 23 | 36% | 36 | 0 | -## Sin ningún token público propio (31) +## Sin ningún token público propio (30) Ni una clave pública en `lib/recipes/base.ts` — alguno tiene privados forward (`_palette-*`), que no son contrato: un tema no puede nombrarlos. -[`alert-dialog`](./alert-dialog.md) · [`aspect-ratio`](./aspect-ratio.md) · [`auto-grid`](./auto-grid.md) · [`avatar`](./avatar.md) · [`cascade`](./cascade.md) · [`css-field`](./css-field.md) · [`date-picker`](./date-picker.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`knob`](./knob.md) · [`mark`](./mark.md) · [`mask-field`](./mask-field.md) · [`menu-dial`](./menu-dial.md) · [`mockup`](./mockup.md) · [`month-grid`](./month-grid.md) · [`motion`](./motion.md) · [`number-field`](./number-field.md) · [`palabras`](./palabras.md) · [`range-calendar`](./range-calendar.md) · [`skin-media-player`](./skin-media-player.md) · [`stack`](./stack.md) · [`text`](./text.md) · [`text-blur`](./text-blur.md) · [`text-circular`](./text-circular.md) · [`text-focus`](./text-focus.md) · [`text-gradient`](./text-gradient.md) · [`text-scramble`](./text-scramble.md) · [`wrap`](./wrap.md) · [`year-grid`](./year-grid.md) +[`alert-dialog`](./alert-dialog.md) · [`aspect-ratio`](./aspect-ratio.md) · [`auto-grid`](./auto-grid.md) · [`avatar`](./avatar.md) · [`cascade`](./cascade.md) · [`css-field`](./css-field.md) · [`date-picker`](./date-picker.md) · [`display`](./display.md) · [`field-langs`](./field-langs.md) · [`heading`](./heading.md) · [`highlight`](./highlight.md) · [`knob`](./knob.md) · [`mask-field`](./mask-field.md) · [`menu-dial`](./menu-dial.md) · [`mockup`](./mockup.md) · [`month-grid`](./month-grid.md) · [`motion`](./motion.md) · [`number-field`](./number-field.md) · [`palabras`](./palabras.md) · [`range-calendar`](./range-calendar.md) · [`skin-media-player`](./skin-media-player.md) · [`stack`](./stack.md) · [`text`](./text.md) · [`text-blur`](./text-blur.md) · [`text-circular`](./text-circular.md) · [`text-focus`](./text-focus.md) · [`text-gradient`](./text-gradient.md) · [`text-scramble`](./text-scramble.md) · [`wrap`](./wrap.md) · [`year-grid`](./year-grid.md) ## Tabla completa (162 recetas, por alcance ascendente) @@ -110,7 +110,6 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [field-langs](./field-langs.md) | 0% | 41 | 0 | 0 | 31 | 8 | 2 | 0 | – | | [skin-media-player](./skin-media-player.md) | 0% | 11 | 0 | 0 | 2 | 7 | 2 | 0 | y | | [menu-dial](./menu-dial.md) | 0% | 8 | 0 | 3 | 1 | 4 | 0 | 0 | y | -| [mark](./mark.md) | 0% | 5 | 0 | 2 | 1 | 2 | 0 | 0 | – | | [aspect-ratio](./aspect-ratio.md) | 0% | 3 | 0 | 0 | 1 | 2 | 0 | 0 | – | | [date-picker](./date-picker.md) | 0% | 2 | 0 | 0 | 0 | 2 | 0 | 0 | – | | [sticky](./sticky.md) | 0% | 2 | 0 | 1 | 0 | 1 | 0 | 1 | – | @@ -184,6 +183,7 @@ La columna «contrato» cuenta las claves **públicas** del bloque del component | [radio-cards](./radio-cards.md) | 60% | 72 | 42 | 0 | 24 | 4 | 2 | 39 | y | | [card](./card.md) | 60% | 36 | 21 | 14 | 0 | 0 | 1 | 97 | y | | [cropper](./cropper.md) | 60% | 27 | 15 | 0 | 3 | 7 | 2 | 15 | – | +| [mark](./mark.md) | 60% | 5 | 3 | 2 | 0 | 0 | 0 | 3 | – | | [natural-time-picker](./natural-time-picker.md) | 61% | 77 | 46 | 10 | 14 | 6 | 1 | 62 | y | | [emoji-picker](./emoji-picker.md) | 61% | 32 | 17 | 0 | 8 | 3 | 4 | 17 | – | | [pin-input](./pin-input.md) | 61% | 19 | 11 | 0 | 6 | 1 | 1 | 20 | y | diff --git a/docs/audit/theming/mark.md b/docs/audit/theming/mark.md index 348a215f6..67acfb707 100644 --- a/docs/audit/theming/mark.md +++ b/docs/audit/theming/mark.md @@ -5,18 +5,16 @@ > Vista de conjunto: [README](./README.md) · método y protocolo: > [`PLAN-theming.md`](../../process/PLAN-theming.md) §1, §2, §7. -- **Medido**: 2026-08-20 · **Alcance**: **0%** — 0 de 5 knobs por token público -- **Knobs de apariencia**: 5 — público 0 · privado 2 · global 1 · literal 2 · sistema 0 _(fuera del ratio)_ -- **Contrato hoy** (`lib/recipes/base.ts`): 0 pública(s) · 2 privada(s) forward — `_palette-element`, `_palette-text` +- **Medido**: 2026-08-21 · **Alcance**: **60%** — 3 de 5 knobs por token público +- **Knobs de apariencia**: 5 — público 3 · privado 2 · global 0 · literal 0 · sistema 0 _(fuera del ratio)_ +- **Contrato hoy** (`lib/recipes/base.ts`): 3 pública(s) — `padding-inline`, `padding-block`, `radius` · 2 privada(s) forward — `_palette-element`, `_palette-text` - **Eje `size`**: no · **ficheros**: `mark.css` ## 1. Knobs fuera de alcance -### 1.1 Directo a primitivo global (1) +### 1.1 Directo a primitivo global (0) -| # | fichero:línea | selector | propiedad | valor | -| ---: | --- | --- | --- | --- | -| 1 | `mark.css:16` | `[data-mark]` | `border-radius` | `var(--radius-sm)` | +_Ninguno._ ### 1.2 A través de un privado (2) @@ -25,12 +23,9 @@ | 1 | `mark.css:13` | `[data-mark]` | `background` | `var(--_mark-palette-element)` | | 2 | `mark.css:24` | `[data-mark][data-color], [data-mark][data-color-custom]` | `color` | `var(--_mark-palette-text)` | -### 1.3 Literales (2) +### 1.3 Literales (0) -| # | fichero:línea | selector | propiedad | valor | -| ---: | --- | --- | --- | --- | -| 1 | `mark.css:14` | `[data-mark]` | `padding-inline` | `0.16em` | -| 2 | `mark.css:15` | `[data-mark]` | `padding-block` | `0.04em` | +_Ninguno._ ## 2. Sistema transversal (0) — informativo, fuera del ratio @@ -46,7 +41,7 @@ Consumidos y **no declarados en el CSS** (vienen de `base.ts` o de un estilo inl ## 4. Propuesta de corrección -### 4.1 Tokens a declarar en `lib/recipes/base.ts` (3) +### 4.1 Tokens a declarar en `lib/recipes/base.ts` (0) Valor **verbatim** del CSS de hoy: el default no se mueve, sólo cambia quién puede moverlo. Nombres derivados de recipe-contract §1 (ejes lógicos, talla @@ -56,9 +51,6 @@ no distingue lo que debería — se marca `⚠`. | token (`--mark-…`) | scope TSC | valor propuesto | usos | | --- | --- | --- | ---: | -| `padding-inline` | `root` | `0.16em` | 1 | -| `padding-block` | `root` | `0.04em` | 1 | -| `radius` | `root` | `var(--radius-sm)` | 1 | ### 4.2 Sin nombre mecánico (2) @@ -76,6 +68,17 @@ no distingue lo que debería — se marca `⚠`. -_(pendiente — lo escribe el autor; se conserva al regenerar)_ +**Medido 2026-08-22.** Cinco knobs, y el reparto lo decide la propia doctrina +del componente: **hereda la tipografía ambiente** (`font: inherit`, +`color: inherit`) porque un resaltado vive dentro de un párrafo, así que no +tiene ni un solo knob de tipo. Lo suyo es la decoración. + +1. **Los dos forwards de paleta se quedan privados** (THM-2): el fondo tiñe con + el rol y la tinta sólo cuando hay `data-color` — el defecto es `inherit`. + Son el 40 % que no alcanza. +2. **Los tres knobs propios son la decoración**: el padding en sus dos ejes y + el radio. El padding es em-relativo a propósito y muy pequeño (0.16em / + 0.04em): el resaltado tiene que leerse **sin romper el interlineado** del + párrafo que lo contiene, que es justo lo que esos dos números protegen. diff --git a/src/uix/eidos/components/mark/README.md b/src/uix/eidos/components/mark/README.md index 474a3d22c..d2440250b 100644 --- a/src/uix/eidos/components/mark/README.md +++ b/src/uix/eidos/components/mark/README.md @@ -65,6 +65,29 @@ Adaptaciones para eidos: Mark declara 0 eventos. Visual-only — el highlight es un decorador inline. +## Talla y tema + +**Guard R-5.4** (`npm run theming:sentinel -- mark `): **3/3** tokens +mueven un computed en vivo, sin una sola excepción. Corrido 2026-08-22. + +Contrato en `lib/recipes/base.ts > mark` (3 claves, fusionadas con los dos +forwards de paleta). Alcance del censo: **60 %** (era 0 %). + +**No tiene ni un knob de tipo, y es deliberado**: un resaltado hereda la +tipografía ambiente del párrafo en que vive (`font: inherit`, +`color: inherit`). Lo que posee es la decoración — el padding en sus dos ejes y +el radio. + +**El padding es em-relativo y deliberadamente diminuto** (0.16em / 0.04em): el +tinte tiene que leerse **sin romper el interlineado** del párrafo que lo +contiene, y eso es exactamente lo que esos dos números protegen. Quien los +mueva debe saber que está tocando el ritmo vertical del texto, no sólo el +grosor del resaltado. + +Los dos forwards de paleta siguen privados (THM-2) — el fondo tiñe con el rol y +la tinta sólo cuando hay `data-color`, porque el defecto es `inherit`. Son el +40 % que no alcanza. + ## Gaps | Gap | Disposición | Detalle | diff --git a/src/uix/eidos/components/mark/mark.css b/src/uix/eidos/components/mark/mark.css index d21b60111..0a3a5820d 100644 --- a/src/uix/eidos/components/mark/mark.css +++ b/src/uix/eidos/components/mark/mark.css @@ -11,9 +11,9 @@ font: inherit; color: inherit; background: var(--_mark-palette-element); - padding-inline: 0.16em; - padding-block: 0.04em; - border-radius: var(--radius-sm); + padding-inline: var(--mark-padding-inline); + padding-block: var(--mark-padding-block); + border-radius: var(--mark-radius); } /* Colour comes from the shared palette forward (`--_mark-palette-*`): the diff --git a/src/uix/eidos/generated/base.css b/src/uix/eidos/generated/base.css index 2646b92df..272eff1c1 100644 --- a/src/uix/eidos/generated/base.css +++ b/src/uix/eidos/generated/base.css @@ -2127,6 +2127,9 @@ --link-external-icon-size: 0.85em; --link-underline-width: 1px; --link-underline-offset: 2px; + --mark-padding-inline: 0.16em; + --mark-padding-block: 0.04em; + --mark-radius: var(--radius-sm); --code-radius: var(--radius-sm); --code-soft-bg: var(--color-neutral-element); --code-border-width: var(--border-width); diff --git a/src/uix/eidos/lib/recipes/base.ts b/src/uix/eidos/lib/recipes/base.ts index b659c98ae..d7bd6ba47 100644 --- a/src/uix/eidos/lib/recipes/base.ts +++ b/src/uix/eidos/lib/recipes/base.ts @@ -3076,6 +3076,13 @@ export const THEME_BASE_RECIPE_TOKENS = defineRecipes({ // the classic highlight), `text` tints the ink but ONLY when data-color is set // (the base stays `color: inherit`), so text host is the forward fallback only. mark: { + // No type knobs on purpose: a highlight inherits the ambient typography of + // the paragraph it lives in (`font: inherit`). What it owns is decoration. + // The padding is em-relative and deliberately tiny: the tint has to read + // WITHOUT breaking the line-height around it. + 'padding-inline': '0.16em', + 'padding-block': '0.04em', + radius: 'var(--radius-sm)', '_palette-element': { value: 'var(--color-fulfill-element)', scope: 'host' }, '_palette-text': { value: 'var(--color-primary-text)', scope: 'host' } }, diff --git a/web/routes/uix/components/mark/+page.svelte b/web/routes/uix/components/mark/+page.svelte index bd11f4734..1f56002bc 100644 --- a/web/routes/uix/components/mark/+page.svelte +++ b/web/routes/uix/components/mark/+page.svelte @@ -2,8 +2,9 @@ import { Mark, type MarkColor } from '$uix/eidos/components/mark'; import { compileMorfo } from '$uix/morfo'; import { markMorfo } from '@/uix/morfo/components/mark'; + import TokensPanel from '../../lib/TokensPanel.svelte'; - type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'a11y'; + type Tab = 'live' | 'api' | 'morfo' | 'sema' | 'recipe' | 'tokens' | 'a11y'; type TraceEntry = { event: string; family: string; intent?: string; at: number }; let tab = $state('live'); let trace = $state([]); @@ -130,6 +131,9 @@ + @@ -361,6 +365,10 @@ {/if} + {#if tab === 'tokens'} + + {/if} + {#if tab === 'a11y'}

Accessibility