diff --git a/docs/audit/theming/calendar.md b/docs/audit/theming/calendar.md index 4511cc927..2648a08cb 100644 --- a/docs/audit/theming/calendar.md +++ b/docs/audit/theming/calendar.md @@ -98,6 +98,56 @@ no distingue lo que debería — se marca `⚠`. -_(pendiente — lo escribe el autor; se conserva al regenerar)_ +**EJECUTADO 2026-08-24 — 84 % → 89 %.** **CERO claves acuñadas**: el censo no +tenía nada que coser (`global` ya era 0 tras la capa `calendar-surface` del +2026-08-21). Lo único fuera de alcance eran cuatro literales de IDENTIDAD, ahora +firmados. El trabajo real fue **MEDIR**: el guard R-5.4 pasó de **45/74 a +68/74** sin tocar una línea de CSS. + +**Los cuatro literales firmados** (§1.3 → §1.4, fuera del ratio): el +`inline-size: max-content` del panel y de la tabla (su anchura ES `day-size` × 7 +más el espaciado — precedente `progress`), el `min-inline-size: max-content` del +panel de mes y el `inline-size: 100%` de la cabecera. + +**Lo que queda fuera (7 privados, techo del componente):** los forwards de +paleta THM-2 (`--_calendar-palette-{solid,solid-hover,track,border,text}`) que +pintan el día seleccionado y el enfocado. No se acuñan. **CERRADO en su 89 %.** + +**La §4 generada proponía `width` / `month-panel-width` / `grid-width` para los +tres `max-content`**: sería acuñar una anchura para una caja cuya anchura es su +contenido, y un tema que la moviera rompería la rejilla. Se firman como +identidad, que es lo que son. + +### Lo que enseñó — una entrada de FAMILIA no se juzga en la ruta de UN componente + +1. **23 de sus 29 «tokens muertos» eran el instrumento.** Esta entrada de + `recipes/base.ts` es la vocabulario de la FAMILIA (lo dice + `lib/calendar-surface.css`): `month-grid`, `year-grid` y `range-calendar` + consumen `--calendar-*` y no acuñan nada — de ahí su 0 % HONESTO. Sus partes + llevan `data-month-grid-*` / `data-range-calendar-*`, invisibles al filtro + `data-calendar`. Con `urls` a las tres rutas de la familia + `extraNodes` + + barrido de `variant`: **45/74 → 68/74**. Ninguno de los tres consumidores se + tocó ni cambió de cifra. +2. **Y la propia demo tapa tres claves más**: su cabecera monta SELECTORES de mes + / año donde iría el `heading`, así que `heading-{fg,font-size,font-weight}` no + tienen nodo en esta ruta — los tiene `month-grid`. +3. **⚠ HALLAZGO: el tamaño de letra de los selectores de mes / año es NO + DETERMINISTA.** La sonda dio 36 diffs contra un cambio que era SÓLO + comentarios; corrida dos veces sobre el MISMO código dio 24. Causa medida: + `[data-calendar-month-select][data-button]` (calendar-select.css) y + `[data-popover-trigger]:not([data-archetype='field-trigger'])` (popover.css) + empatan a **(0,2,0)** y gana el módulo que Vite inyecte el ÚLTIMO — cuatro + cargas dieron 14px / 16px / 14px / 14px, con el orden de las hojas invertido + en la que dio 16. La regla que existe *precisamente* para fijar la letra del + Button a la del calendario pierde la mitad de las veces. Es la clase del + empate por orden de carga de `tree-grid` (§12); **arreglarlo mueve píxel** → + `next-features` §13. + **Regla que confirma**: ante un diff inesperado, corre la sonda dos veces + sobre el mismo código ANTES de sospechar de tu cambio. +4. **`select-min-width` no tiene nodo en NINGUNA página del repo**: su único + consumidor es `[data-range-calendar-month-select]` / `-year-select`, y ninguna + demo monta esas partes (la de range-calendar renderiza un heading de texto; el + DateRangePicker importa los selectores del CALENDARIO, que llevan + `data-calendar-month-select`). Hueco de demo, registrado. diff --git a/docs/next-features.md b/docs/next-features.md index 08236725c..e52a8aadc 100644 --- a/docs/next-features.md +++ b/docs/next-features.md @@ -1452,5 +1452,32 @@ Lo que `chart` destapó (2026-08-22): pinta nada en un panel que desborde. El arreglo (`flex: 0 0 auto` en `[data-select-separator]`) mueve píxel → fuera del eje de theming, pendiente. +- **Una entrada de receta de FAMILIA no se puede juzgar en la ruta de UN + componente** (medido 2026-08-24 en `calendar`). Su entrada de + `recipes/base.ts` es la de las cuatro superficies de rejilla de fechas — + `range-calendar`, `month-grid` y `year-grid` consumen `--calendar-*` y no + acuñan nada—, pero sus partes llevan `data-month-grid-*` / + `data-range-calendar-*` y el filtro `data-calendar` no las ve: **23 de sus 29 + «tokens muertos» eran eso**. Con `urls` a las tres rutas + `extraNodes` + + barrido de `variant`: 45/74 → 68/74. Vale para toda capa híbrida que conserve + entrada de receta. +- **El tamaño de letra de los selectores de mes / año del calendario es NO + DETERMINISTA** (medido 2026-08-24). `[data-calendar-month-select][data-button]` + —la regla de `calendar-select.css` que existe precisamente para fijar la letra + del Button a la del calendario— y + `[data-popover-trigger]:not([data-archetype='field-trigger'])` de `popover.css` + empatan a **(0,2,0)**, así que gana el módulo que Vite inyecte el ÚLTIMO: + cuatro cargas de la misma página dieron 14px / 16px / 14px / 14px, con el orden + de las hojas invertido en la que dio 16. Consecuencia de medición: la sonda dio + **24 diffs corriendo dos veces el MISMO código**, así que todo diff en esos dos + nodos es ruido hasta que se resuelva. Es la familia del empate por orden de + carga de §12 (tree-grid); arreglarlo mueve píxel. +- **`calendar.select-min-width` no tiene nodo en NINGUNA página del repo** + (medido 2026-08-24): su único consumidor es + `[data-range-calendar-month-select]` / `-year-select`, y la demo de + `range-calendar` renderiza un heading de texto en su lugar, mientras + `DateRangePicker` importa los selectores del CALENDARIO (que llevan + `data-calendar-month-select`). Hueco de DEMO: la parte existe en soma y en + eidos y ninguna superficie la monta. **Deps**: ninguna. Son mejoras del instrumental del eje, ejecutables cuando estorben. diff --git a/docs/process/PLAN-theming.md b/docs/process/PLAN-theming.md index a40d30953..f3f5f796c 100644 --- a/docs/process/PLAN-theming.md +++ b/docs/process/PLAN-theming.md @@ -486,6 +486,27 @@ commits de corrección. ## 8. Registro +- 2026-08-24 — **`calendar` 84 % → 89 %** (COMMIT), contrato **74 claves sin + tocar**, centinela **45/74 → 68/74** con 6 adjudicadas. **CERO claves + acuñadas: el censo no tenía nada que coser** (`global` ya era 0 desde la capa + `calendar-surface`), así que lo único fuera de alcance eran cuatro literales + de IDENTIDAD (`max-content` ×3 + `100%`), ahora firmados con su razón. **Todo + el trabajo fue MEDIR**, y enseñó la ley del bloque: **una entrada de receta de + FAMILIA no se juzga en la ruta de UN componente** — 23 de sus 29 «muertos» + eran el cromo que `month-grid` y `range-calendar` pintan con sus propios + atributos, invisible al filtro `data-calendar`; y la propia demo pone + SELECTORES de mes/año donde iría el `heading`, así que tres claves más no + tenían nodo aquí. Con `urls` a las tres rutas de la familia + `extraNodes` + + barrido de `variant`, sin tocar a los tres consumidores (siguen a 0 % honesto). + **Hallazgo de píxel**: el tamaño de letra de los selectores de mes / año es NO + DETERMINISTA — `[data-calendar-month-select][data-button]` y + `[data-popover-trigger]:not([data-archetype='field-trigger'])` empatan a + (0,2,0) y gana el módulo que Vite inyecte el último (cuatro cargas: + 14/16/14/14 px). Lo destapó la sonda dando **24 diffs corriendo dos veces el + MISMO código**; es la clase de `tree-grid` y va a §13. Sonda 22.656 valores × + 7 estados: **0 diffs fuera de esos dos nodos**; capturas 2× idénticas byte a + byte. Guards: censo `--only` 89 % (global 68 %, no baja) · audit PASS · + eidos-lint 0 invalid · rtl 0 · docs 0. - 2026-08-24 — **`select` 83 % → 92 %** (COMMIT), contrato 101 → **103** claves, centinela **69/103** con 34 adjudicadas. Dos claves de COSTURA: **`invalid-border`** (las TRES declaraciones del estado inválido leían diff --git a/scripts/theming-sentinel-exceptions.ts b/scripts/theming-sentinel-exceptions.ts index bc1929fa9..8deefdb94 100644 --- a/scripts/theming-sentinel-exceptions.ts +++ b/scripts/theming-sentinel-exceptions.ts @@ -1092,6 +1092,24 @@ export const SENTINEL_EXCEPTIONS: Record> = { 'the guard freezes transitions and this token IS the transition; measured unfrozen on the real trigger -> reaches (0.12s -> 11.5s on all four properties)', 'transition-ease': 'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))' + }, + calendar: { + // 2026-08-24. This entry is the FAMILY's vocabulary (calendar-surface.css), + // so the guard measures it across calendar + month-grid + range-calendar; + // with those three routes 68 of its 74 keys move. These six are what the + // family cannot show, each measured on the real node. + 'invalid-border': + 'no calendar surface of the three routes boots invalid; forced [data-invalid] on the real host -> reaches (oklch(0.8059 0.1123 59.96) -> rgb(1, 2, 3))', + 'select-min-width': + 'its ONLY consumer is [data-range-calendar-month-select] / -year-select, and NO demo in the repo mounts those parts (0 nodes on the range-calendar route, which renders a text heading instead; DateRangePicker imports the CALENDAR selectors, so its nodes carry data-calendar-month-select). A part no page mounts has no node to paint — demo gap, registered in next-features §13', + 'range-start-solid-hover': + 'the hover pass stops at the first 30 nodes and the range endpoints sit past them on the range-calendar route; hovered the real [data-range-start] day -> reaches (oklch(0.6193 0.1094 181.3) -> rgb(1, 2, 3))', + 'range-end-solid-hover': + 'same limit on the other endpoint; hovered the real [data-range-end] day -> reaches (oklch(0.5246 0.1755 305.39) -> rgb(1, 2, 3))', + 'transition-duration': + 'the guard freezes transitions and this token IS the transition; measured unfrozen on a real day cell -> reaches (0.12s -> 11.5s on all five properties)', + 'transition-ease': + 'same freeze; measured unfrozen -> reaches (cubic-bezier(0.4, 0, 0.2, 1) -> steps(3))' } }; diff --git a/scripts/theming-sentinel.ts b/scripts/theming-sentinel.ts index 6b5ad6ac5..417166590 100644 --- a/scripts/theming-sentinel.ts +++ b/scripts/theming-sentinel.ts @@ -463,6 +463,25 @@ const COMPONENT_OVERRIDES: Record< select: { extraNodes: '[data-floating-wrapper]:has(> [data-select-content]), [data-select-content] [data-scroll-area-viewport]' + }, + // The `calendar` recipe entry is the FAMILY's vocabulary, not this + // component's: `lib/calendar-surface.css` says so, and `month-grid`, + // `year-grid` and `range-calendar` consume `--calendar-*` while minting + // NOTHING of their own (0 % honest in the census). So a token of this entry + // lives if ANY surface of the family follows it — and their parts carry + // `data-month-grid-*` / `data-range-calendar-*`, invisible to the + // `data-calendar` filter. The header chrome (`control-*`, `heading-*`) has no + // node on THIS route either: the demo puts month / year SELECTORS where the + // text heading would be, and prev / next compose the system IconButton. + calendar: { + urls: [ + '/uix/components/calendar', + '/uix/components/month-grid', + '/uix/components/range-calendar' + ], + extraNodes: + '[data-month-grid], [data-month-grid] *, [data-range-calendar], [data-range-calendar] *', + sweepAttr: { attr: 'data-variant', values: ['surface', 'outline', 'ghost'] } } }; diff --git a/src/uix/eidos/components/calendar/README.md b/src/uix/eidos/components/calendar/README.md index c78ffc921..554e6aa52 100644 --- a/src/uix/eidos/components/calendar/README.md +++ b/src/uix/eidos/components/calendar/README.md @@ -144,7 +144,8 @@ Contrato en `lib/recipes/base.ts > calendar` — y esa entrada es la de la **FAMILIA**, no sólo la de este componente: `RangeCalendar`, `MonthGrid`, `YearGrid` y los calendarios de los dos pickers leen el mismo vocabulario `--calendar-*` (medido 2026-08-21: 114 / 77 / 77 referencias y CERO tokens -propios). Alcance del censo **84 %**. +propios). Alcance del censo **89 %** (2026-08-24); **74 claves públicas**, que +la pestaña **Tokens** de la demo resuelve en vivo. La resolución por talla vive en [`lib/calendar-surface.css`](../../lib/calendar-surface.css), enganchada al @@ -158,6 +159,35 @@ El anillo de evento y la marca de festivo también son de la capa: la FORMA allí, el ACENTO desde aquí (`--_calendar-surface-accent` ← el forward de paleta THM-2 de cada superficie). +Lo que entró el **2026-08-24** (84 % → 89 %), sin una línea de CSS nueva: **el +censo no tenía nada que acuñar** — cero knobs leían un primitivo global — así +que lo único fuera de alcance eran **cuatro literales de IDENTIDAD**, ahora +firmados con su razón (`recipe-contract` §3) y fuera del ratio: el +`inline-size: max-content` del panel y de la tabla (su anchura ES `day-size` × 7 +más el espaciado, no un knob), el `min-inline-size: max-content` del mes (nunca +se estrecha por debajo de su propia rejilla) y el `inline-size: 100%` de la +cabecera. Lo que queda fuera son los **siete forwards de paleta THM-2**, el +puente que no se acuña. + +**El trabajo real fue MEDIR.** El guard R-5.4 leía 45 de 74 claves porque +juzgaba la entrada de la FAMILIA sobre la ruta de UN componente: 23 de esas 29 +«muertas» eran el cromo que `month-grid` y `range-calendar` pintan con +`data-month-grid-*` / `data-range-calendar-*`, invisible al filtro +`data-calendar` — y la cabecera de esta demo pone SELECTORES de mes / año donde +iría el `heading`, así que sus tres claves tampoco tenían nodo aquí. Con las +tres rutas de la familia y el barrido de `variant`: **68 / 74**. Las seis +restantes van adjudicadas una a una en `theming-sentinel-exceptions.ts` +(inválido forzado, los dos hovers de extremo de rango, el par de transición — +que ES lo que el guard congela — y `select-min-width`, cuya parte **no la monta +ninguna demo del repo**: registrado en `docs/next-features.md` §13). + +⚠️ **El tamaño de letra de los selectores de mes / año es NO DETERMINISTA en +dev**: `[data-calendar-month-select][data-button]` (esta capa) y +`[data-popover-trigger]:not([data-archetype='field-trigger'])` (popover) empatan +a (0,2,0), así que gana el módulo que Vite inyecte el último — medido 2026-08-24 +en cuatro cargas: 14px / 16px / 14px / 14px. Registrado en §13 con las demás del +empate por orden de carga; arreglarlo mueve píxel. + ## Gaps | Gap | Disposición | Detalle | diff --git a/src/uix/eidos/components/calendar/calendar.css b/src/uix/eidos/components/calendar/calendar.css index 7dc81d494..0b7899073 100644 --- a/src/uix/eidos/components/calendar/calendar.css +++ b/src/uix/eidos/components/calendar/calendar.css @@ -9,7 +9,7 @@ display: inline-flex; flex-direction: column; gap: var(--calendar-gap); - inline-size: max-content; + inline-size: max-content; /* literal: identity — the panel is as wide as the grid it holds; its width is `day-size` × 7, not a knob */ max-inline-size: 100%; padding: var(--calendar-padding); border: var(--calendar-border-width) solid var(--calendar-border); @@ -44,7 +44,7 @@ grid-template-columns: var(--calendar-control-size) minmax(0, 1fr) var(--calendar-control-size); align-items: center; gap: var(--calendar-header-gap); - inline-size: 100%; + inline-size: 100%; /* literal: identity — the header spans the panel it heads */ min-inline-size: 0; } @@ -85,7 +85,7 @@ flex-direction: column; align-items: center; gap: var(--calendar-grid-gap); - min-inline-size: max-content; + min-inline-size: max-content; /* literal: identity — a month never narrows below its own grid */ } [data-calendar-month-panel] > [data-calendar-heading] { @@ -109,7 +109,7 @@ a [data-calendar] host, still get the styling when they reuse the selectors. */ [data-calendar-grid] { - inline-size: max-content; + inline-size: max-content; /* literal: identity — the table is seven `day-size` columns wide plus their spacing */ max-inline-size: 100%; margin-inline: auto; border-collapse: separate; diff --git a/web/routes/uix/components/calendar/+page.svelte b/web/routes/uix/components/calendar/+page.svelte index 5680f7c57..376e5d35b 100644 --- a/web/routes/uix/components/calendar/+page.svelte +++ b/web/routes/uix/components/calendar/+page.svelte @@ -11,8 +11,9 @@ import { calendarMorfo } from '@/uix/morfo/components/calendar'; import { getActiveUix } from '$active-uix'; import { CalendarDate, type DateValue, type WeekStartsOn } from '$libs/days'; + 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 }; type Direction = 'ltr' | 'rtl'; type WeekStartControl = 'locale' | '0' | '1'; @@ -587,6 +588,9 @@ + @@ -1016,6 +1020,10 @@ {/if} + {#if tab === 'tokens'} + + {/if} + {#if tab === 'recipe'}

Eidos recipe