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