Firma 1 del acta, diseño presentado y firmado. **La medición desmontó el
encargo**: el handoff la vendía como una capa NUEVA de ~220 knobs, y la capa ya
existía de hecho, sin nombre. `--calendar-*` se emite en `:root` (76 claves) y
sus consumidores no acuñan NADA — medido: `range-calendar` 114 referencias
prestadas y 0 propias, `month-grid` 77/0, `year-grid` 77/0, y lo único ajeno
que leen es sistema puro (`--focus-ring-*`, `--state-hover`). Su 0 % era el
artefacto de `listbox` (§13), pero total.
**`lib/calendar-surface.css`** (hook `data-calendar-surface`):
- cuatro coordenadas por talla — `padding`, `control-size`, `day-size`,
`font-size` — xs..lg, porque la familia NO tiene xl, y con la celda DOS pasos
por debajo del bundle de control. Esa desviación estaba escondida en cuatro
bloques `[data-size]` idénticos, uno por receta, cada uno puenteando a un
privado con otro nombre; ahora se lee en un sitio.
- la FORMA del anillo de evento y de la marca de festivo.
**Capa HÍBRIDA, y es lo que la distingue de sus hermanas**: `list-surface` y
`viewport-placement` componen primitivos del sistema, así que declaran sus
públicos en el fichero y no tienen entrada de receta. Ésta no puede: su
vocabulario son 76 claves SEMÁNTICAS que un tema alcanza una a una por config,
así que la entrada `calendar` de `recipes/base.ts` pasa a ser la de la FAMILIA
y la capa posee sólo lo que una entrada de receta no sabe expresar.
**El defecto que la justificaba, medido**: `--calendar-event-shadow` y
`--calendar-day-holiday-shadow` se emitían con ámbito `[data-calendar]`
(audit B.2 los host-scopeó por buenas razones) mientras `range-calendar`,
`month-grid` y `year-grid` los leían desde hosts que nunca llevan ese atributo:
variable VACÍA, `box-shadow` inválido en computed, **el anillo sema de
`commit-select` / `commit-set` no pintaba jamás en tres componentes**. Un token
prestado cuyo ÁMBITO no te cubre no es un préstamo, es un agujero silencioso, y
ningún guard lo veía. Ahora la forma vive en la capa y el acento entra por
`--_calendar-surface-accent`, que cada superficie alimenta con su propio
forward de paleta THM-2.
**Siete wrappers estampan, no cuatro** — y esto casi se me cuela: `DatePicker`
y `DateRangePicker` renderizan la superficie soma por sus PROPIOS wrappers
(`date-picker-calendar`, `-month-view`, `-year-view`,
`date-range-picker-calendar`) y un panel portalado no hereda nada del root del
picker. Con sólo los cuatro standalone sellando, ambos quedaban con
`--calendar-padding` VACÍA y el panel a padding 0 (medido). La tentación era
enganchar la capa a las cuatro identidades de componente: eso viola la regla 1
de capas compartidas, y la respuesta correcta es un sello por wrapper.
computed 0 diffs en range-calendar (19.285 valores) · month-grid (3.451) ·
year-grid (3.451) · date-picker (464) · date-range-picker (406).
`calendar` da 12, y son del INSTRUMENTO: dos corridas del MISMO
código dan 24 en los mismos nodos y las mismas dos propiedades.
Los «missing node» son el propio sello entrando en la clave.
**Una incidencia nueva, medida y NO arreglada aquí** (§13): el font-size de los
selectores month/year es una moneda al aire —
`[data-calendar-month-select][data-button]` (0,2,0) empata con
`[data-popover-trigger]:not([data-archetype='field-trigger'])` (0,2,0), la
MISMA regla de popover que dejó muerto el cromo de `gradient-picker`, y gana la
hoja que cargue después: 16px o 14px según la recarga. Arreglarlo fija el píxel
en un lado ⇒ decisión.
De paso, `calendar-select.css` deja de puentear un privado que sólo
`[data-calendar]` declaraba: en range / date-range corría SIEMPRE por el
fallback, clavado a md fuera cual fuera la talla.
**El censo deja de penalizar hacer lo correcto**: `LAYER_VOCABULARY` en
`theming-census.ts`, mismo precedente que D-TH.2-b con `--style-*`. Global
43 % → **45 %**, `calendar` 75 % → **84 %**. `list-surface` NO se registra: sus
consumidores puentean por privados, otra forma, y mueve diez componentes de
golpe. Y aparece el techo de debajo, anotado: a los tres consumidores sólo les
quedan los forwards de paleta THM-2 —que el censo cuenta como `private` en TODO
el catálogo— así que siguen leyendo 0 %.
`recipe-css-contract` aprende que una CAPA también declara públicos (antes sólo
miraba la receta, y una capa que comparte prefijo con un componente la hacía
fallar). Sin debilitarla: un nombre que no declara nadie sigue en rojo.
eidos-lint 0 invalid (calendar 29/8 · range-calendar 41/12 · los grids 23/5) ·
audit --only calendar PASS · vitest eidos 434/435 (el rojo conocido) ·
rtl 0/181 · docs 0/813 · check 0 errores en tocados · prettier: revertido el
reformateo en masa que se coló en cuatro README, el test y el censo (churn
ajeno, no mío)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-background
parent
df27351ffc
commit
8400e5bdd4
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Calendar-surface — the SINGLE SOURCE for the size rhythm of every grid-of-
|
||||
* dates surface, and the shape of its event ring.
|
||||
*
|
||||
* Shared by the four surfaces that render a grid of selectable date cells:
|
||||
* - `calendar` (the day grid — the reference implementation)
|
||||
* - `range-calendar` (the same grid with two endpoints)
|
||||
* - `month-grid` (12 months instead of 31 days)
|
||||
* - `year-grid` (a decade of years)
|
||||
* plus the pickers that host them (`date-picker`, `date-range-picker`), which
|
||||
* inherit through the surface they render.
|
||||
*
|
||||
* Each surface IMPORTS this file and STAMPS `data-calendar-surface=""` +
|
||||
* `data-size` on its eidos Provider — a VISUAL-LAYER identity, never declared
|
||||
* in the morfo (the `viewport-placement` split: the layer hook is not the
|
||||
* component's identity, so `morfo-check` skips it). Nothing else is needed:
|
||||
* the vocabulary below is already emitted at `:root`.
|
||||
*
|
||||
* ── Where the vocabulary lives, and why this layer is a HYBRID ─────────────
|
||||
* `list-surface` and `viewport-placement` compose SYSTEM primitives, so they
|
||||
* declare their public tokens here and carry no `recipes/base.ts` key. This
|
||||
* family cannot: its vocabulary is 76 SEMANTIC keys (`--calendar-day-today-
|
||||
* border`, `--calendar-weekday-fg`, the two range endpoints…) that a theme has
|
||||
* to reach one by one through `EidosConfig.recipes`. So the vocabulary stays
|
||||
* in the `calendar` recipe entry — the entry is the FAMILY's, not one
|
||||
* component's — and this file owns only what a recipe entry cannot express:
|
||||
* the per-size RESOLUTION on a shared hook, and the ring shape.
|
||||
*
|
||||
* ── The size axis ─────────────────────────────────────────────────────────
|
||||
* Four coordinates move together, and the family runs xs | sm | md | lg —
|
||||
* there is NO xl. The cell reads TWO steps below the control bundle
|
||||
* (`md → --size-sm-control-height`, `xs → --size-xxs-*`): a date grid packs 31
|
||||
* targets into a panel, so a day is not a control-sized row. That deviation
|
||||
* used to hide inside four identical `[data-size]` blocks (one per component,
|
||||
* bridging to a private with a different name each time); naming it once here
|
||||
* is the point of the layer.
|
||||
*
|
||||
* Doctrine: a consumer does NOT mint `--{component}-{axis}` for an axis this
|
||||
* layer owns — it reads `--calendar-*` directly.
|
||||
*/
|
||||
|
||||
/* ── Every renderer of a calendar surface stamps the hook ───────────────────
|
||||
* SEVEN wrappers, not four: `DatePicker` and `DateRangePicker` render the soma
|
||||
* surface straight into their popover through their OWN eidos wrappers
|
||||
* (`date-picker-calendar`, `-month-view`, `-year-view`,
|
||||
* `date-range-picker-calendar`), and a portaled panel inherits nothing from
|
||||
* the picker root. Measured 2026-08-21: with only the four standalone
|
||||
* wrappers stamping, both pickers rendered with `--calendar-padding` EMPTY and
|
||||
* the panel at padding 0. Rule 1 of shared layers still holds — the geometry
|
||||
* hooks on the LAYER attr, never on a component identity — so the answer is a
|
||||
* stamp in each wrapper, not a selector on the four hosts. */
|
||||
[data-calendar-surface] {
|
||||
--calendar-padding: var(--calendar-padding-md);
|
||||
--calendar-control-size: var(--calendar-control-size-md);
|
||||
--calendar-day-size: var(--calendar-day-size-md);
|
||||
--calendar-font-size: var(--calendar-font-size-md);
|
||||
}
|
||||
|
||||
[data-calendar-surface][data-size='xs'] {
|
||||
--calendar-padding: var(--calendar-padding-xs);
|
||||
--calendar-control-size: var(--calendar-control-size-xs);
|
||||
--calendar-day-size: var(--calendar-day-size-xs);
|
||||
--calendar-font-size: var(--calendar-font-size-xs);
|
||||
}
|
||||
|
||||
[data-calendar-surface][data-size='sm'] {
|
||||
--calendar-padding: var(--calendar-padding-sm);
|
||||
--calendar-control-size: var(--calendar-control-size-sm);
|
||||
--calendar-day-size: var(--calendar-day-size-sm);
|
||||
--calendar-font-size: var(--calendar-font-size-sm);
|
||||
}
|
||||
|
||||
[data-calendar-surface][data-size='lg'] {
|
||||
--calendar-padding: var(--calendar-padding-lg);
|
||||
--calendar-control-size: var(--calendar-control-size-lg);
|
||||
--calendar-day-size: var(--calendar-day-size-lg);
|
||||
--calendar-font-size: var(--calendar-font-size-lg);
|
||||
}
|
||||
|
||||
/* ── The event ring and the holiday mark ────────────────────────────────────
|
||||
* SHAPE here, ACCENT from the consumer. Both used to be emitted scoped to
|
||||
* `[data-calendar]` while `range-calendar`, `month-grid` and `year-grid` read
|
||||
* them from hosts that never carry that attribute: the variable resolved EMPTY
|
||||
* there, `box-shadow: var(--calendar-event-shadow)` was invalid at
|
||||
* computed-value time, and the sema ring of `commit-select` / `commit-set`
|
||||
* never painted in three components (measured 2026-08-21, adversarial review
|
||||
* of F2-A — a borrowed token whose SCOPE does not cover you is not a loan, it
|
||||
* is a silent hole, and no guard sees it).
|
||||
*
|
||||
* `--_calendar-surface-accent` is the OVERRIDE SLOT (the `menu-indicator`
|
||||
* pattern): every consumer sets it from its OWN per-instance palette forward
|
||||
* (`--_{component}-palette-border`, THM-2), because the accent is per-instance
|
||||
* and the shape is not. The fallback keeps the layer standalone-correct.
|
||||
*/
|
||||
[data-calendar-surface] {
|
||||
--calendar-day-holiday-shadow: inset 0 -2px 0
|
||||
var(--_calendar-surface-accent, var(--color-primary-border));
|
||||
--calendar-event-shadow:
|
||||
0 0 0 var(--focus-ring-offset) var(--color-surface-default),
|
||||
0 0 0 calc(var(--focus-ring-offset) + var(--focus-ring-width))
|
||||
var(--_calendar-surface-accent, var(--color-primary-border));
|
||||
}
|
||||
Loading…
Reference in new issue