uix(mark): temable — 0 % → 60 %, y 3/3 en el guard

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 <noreply@anthropic.com>
alpha-0.1-background
dev 2 months ago
parent 1f692629e5
commit 9f24a31eb9

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

@ -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 `⚠`.
<!-- veredicto:start -->
_(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.
<!-- veredicto:end -->

@ -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 <url>`): **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 |

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

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

@ -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' }
},

@ -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<Tab>('live');
let trace = $state<TraceEntry[]>([]);
@ -130,6 +131,9 @@
<button data-uix-tab data-active={tab === 'recipe'} onclick={() => (tab = 'recipe')}
>Recipe</button
>
<button data-uix-tab data-active={tab === 'tokens'} onclick={() => (tab = 'tokens')}>
Tokens <span data-uix-tab-count>3</span>
</button>
<button data-uix-tab data-active={tab === 'a11y'} onclick={() => (tab = 'a11y')}>A11y</button>
</div>
@ -361,6 +365,10 @@
</section>
{/if}
{#if tab === 'tokens'}
<TokensPanel component="mark" stage={stageRef ?? undefined} />
{/if}
{#if tab === 'a11y'}
<section data-uix-section>
<h2 data-uix-section-title>Accessibility</h2>

Loading…
Cancel
Save

Powered by TurnKey Linux.