diff --git a/src/uix/eidos/components/color-swatch/README.md b/src/uix/eidos/components/color-swatch/README.md index 903f4b4af..e476e1fcf 100644 --- a/src/uix/eidos/components/color-swatch/README.md +++ b/src/uix/eidos/components/color-swatch/README.md @@ -63,3 +63,41 @@ control) when the colour conveys meaning. `src/uix/morfo/components/color-swatch.ts` — single part, `scope: ['eidos']`, passive (no events). Eidos-native, no soma (pure visual, like `Kbd` / `Icon`). + +## Baseline + +Sin baseline air: el primitivo se EXTRAJO durante el rediseño Untitled-UI de +ColorPicker (2026-06-28) para matar los chips ad-hoc — la muestra canónica de +color, reutilizada por `ColorPicker` / `ColorField` (preview del valor, +presets, dot del trigger). La única referencia headless que lo publica como +primitivo es React Aria (`ColorSwatch`). + +## Comparativa + +| Capacidad | UIX | React Aria ColorSwatch | Chakra/Radix | Chips ad-hoc | +| --- | --- | --- | --- | --- | +| Primitivo dedicado reutilizable | ✓ | ✓ | No | — | +| Checker de transparencia bajo alfa | ✓ | ✓ | — | Raro | +| Escala de tallas + `rounded`/`full` | ✓ | CSS externo | — | — | +| Ring inset (legibilidad sobre claros) | ✓ | No | — | No | +| Pasivo por contrato (0 eventos) | ✓ | ✓ | — | — | + +## Decisiones + +- **Una sola muestra canónica** (doctrina compose-first): cualquier color que + el sistema enseñe pasa por ColorSwatch — nunca un `
` con background + inline (los consumidores del picker lo prueban). +- **El checker es parte del primitivo**, no del consumidor: un color con alfa + SIEMPRE se lee contra el damero. +- **Ring inset sutil** para que una muestra clara no se funda con la + superficie clara — legibilidad, no decoración. +- **Pasivo por contrato** (S7, átomo ejemplar): mostrar un color no tiene + momento perceptivo propio; la interacción (elegir, copiar) pertenece al + consumidor que lo compone. + +## Gaps + +| Gap | Disposición | Detalle | +| --- | --- | --- | +| Nombre accesible del color (aria-label con naming, "azul cielo") | **diferir** | Requiere lib de color-naming; hoy el consumidor pasa el label cuando el contexto lo pide. | +| Conciencia de roles de paleta (pintar `--color-primary-solid` por nombre) | **descartar** | El swatch enseña VALORES; los roles se resuelven antes (eidos.resolveToken). | diff --git a/src/uix/eidos/components/onion-menu/README.md b/src/uix/eidos/components/onion-menu/README.md index f29826305..a2a2760ed 100644 --- a/src/uix/eidos/components/onion-menu/README.md +++ b/src/uix/eidos/components/onion-menu/README.md @@ -98,7 +98,7 @@ elevation (`--shadow-raised` sectors, `--shadow-overlay` trigger — single-laye drop straight into `drop-shadow()`), the label / icon typography, and the `+`→`×` trigger glyph. All colours come through tokens; no raw hex in the recipe. -## Decisions +## Decisiones - **Compositional, not data-driven** — parts register a node tree; the root paints. An option's children nest *inside* the option (`DropdownMenu.Sub` shape). @@ -109,3 +109,32 @@ trigger glyph. All colours come through tokens; no raw hex in the recipe. nav maths) the root drives, rather than a compound provider tree. - **Focus on open transition, not mount** — a controlled `open=true` at mount does not steal focus; user/programmatic open transitions move focus into the ring. + +## Baseline + +Sin baseline air y sin análogo headless publicado: ningún ecosistema +(Radix/Ark/Bits/React Aria) publica un menú radial. Las referencias del +patrón son los pie/radial menus de herramientas (Blender, juegos, marking +menus de Autodesk) — todos mouse-only y ninguno con drill de anillos +concéntricos temado por profundidad. Semánticamente ES un Menu Button por +APG (el morfo lo cita): trigger `aria-haspopup` → `role="menu"` de +`menuitem`s; la geometría radial y el color por profundidad salen de los +engines del framework. + +## Comparativa + +| Capacidad | UIX | Pie menus (Blender/juegos) | Marking menus | Radix/Ark/RAC | +| --- | --- | --- | --- | --- | +| Menú radial con drill concéntrico | ✓ | Anillo único | Jerárquico gestual | No publicado | +| Tema por rama + tintes por profundidad + trail muted | ✓ | No | No | — | +| Semántica Menu Button (haspopup/menu/menuitem) | ✓ | No (canvas) | No | — | +| Navegación radial por teclado | ✓ | No | No | — | +| Carácter perceptivo (pack radial S3b) | ✓ | — | — | — | + +## Gaps + +| Gap | Disposición | Detalle | +| --- | --- | --- | +| Gestos marking-menu (trazo rápido sin esperar el render) | **diferir** | El patrón experto de Autodesk; espera caso real. | +| Virtualización de anillos con muchas opciones | **descartar** | Un anillo con >12 opciones es un problema de diseño del consumidor, no del componente. | +| Touch: objetivo de 44px en arcos finos | **diferir** | Auditar los arcos bajo `pointer: coarse` cuando el censo touch llegue a los radiales. | diff --git a/src/uix/eidos/components/radio-cards/README.md b/src/uix/eidos/components/radio-cards/README.md index 530893ac7..c71f916f2 100644 --- a/src/uix/eidos/components/radio-cards/README.md +++ b/src/uix/eidos/components/radio-cards/README.md @@ -102,3 +102,40 @@ The interactive contract (Provider/Item, events, keyboard, a11y) lives in soma, no new sema (`commit-select` fires from RadioGroup's runtime + pack). This mirrors `toggle-group ↔ toggle` and `css-field ↔ spin-field`: share the machinery, layer a distinct visual. + +## Baseline + +El patrón visual es el **Radio Cards de Radix Themes** (tarjetas +single-select con contenido rico); Ark/Bits no lo publican y shadcn lo +compone a mano. La diferencia arquitectural de UIX está en la cabecera de +este README: **cero comportamiento duplicado** — RadioCards reusa el soma de +`RadioGroup` entero (selección, roving, `role=radio`, HiddenInput de form) y +solo añade la presentación de tarjeta. + +## Comparativa + +| Capacidad | UIX | Radix Themes | shadcn (manual) | Ark/Bits | +| --- | --- | --- | --- | --- | +| Tarjetas single-select con contenido rico | ✓ | ✓ | Compuesto a mano | No publicado | +| Reusa el soma del RadioGroup (cero duplicación) | **✓** | (interno) | No (re-cablea) | — | +| Radio de esquina siempre visible | ✓ | Opcional | Variable | — | +| Form participation (HiddenInput) | ✓ | ✓ | Manual | — | +| Roving + teclado del grupo | ✓ | ✓ | Depende | — | + +## Decisiones + +- **Delegación como arquitectura** (la cita canónica del veredicto S11c vive + en el header del morfo): RadioCards no declara eventos propios — el + `commit-select` del RadioGroup compuesto ES su semántica; el morfo solo + aporta la identidad de tarjeta. +- **El radio de esquina SIEMPRE presente**: la tarjeta entera es clicable, + pero el affordance de "esto es un radio" no se negocia (a11y + escaneo). +- **Contenido libre en la tarjeta** (icon · title · description como + children): la presentación no impone estructura de datos. + +## Gaps + +| Gap | Disposición | Detalle | +| --- | --- | --- | +| Variante multi-select de tarjetas | **descartar** | Ya resuelto en otro sitio: `CardGroup` + `ToggleGroup` poseen la cardinalidad (decisión 2026-06-27). | +| Tarjetas con imagen de cabecera | **diferir** | Componible hoy (children); slot dedicado espera caso real. | diff --git a/src/uix/eidos/components/scroll-frames/README.md b/src/uix/eidos/components/scroll-frames/README.md index 4a6d472fc..fad5f73b2 100644 --- a/src/uix/eidos/components/scroll-frames/README.md +++ b/src/uix/eidos/components/scroll-frames/README.md @@ -76,3 +76,46 @@ selector is morfo-backed; no eidos-only, intent or variant selectors. surrounding page, not the canvas/video. - Scroll-linked: the user controls pace directly, so it never auto-animates. For a reduced-motion path, render a single representative frame. + +## Passive justification + +Superficie display eidos-only de 0 eventos: el frame mostrado es una FUNCIÓN +PURA del progreso de scroll (0→1) — el gesto de scroll pertenece a la página, +no al componente (no hay superficie interactiva propia, ni foco, ni teclado). +Mismo criterio contract-level que SText / Image: observación, no acto. + +## Baseline + +El patrón es el scrollytelling de secuencia de imágenes estilo Apple +(AirPods/producto): canvas pegajoso + track alto + scrub por progreso. Las +referencias del campo son librerías JS (scrolly-video, GSAP ScrollTrigger) — +ninguna integrada a un sistema de tokens/disciplina DOM; la sección +"Adapted to the framework" de arriba documenta la adaptación. + +## Comparativa + +| Capacidad | UIX | scrolly-video | GSAP ScrollTrigger | Ad-hoc | +| --- | --- | --- | --- | --- | +| Modo frames (canvas, cover-fit, HiDPI) | ✓ | No (solo video) | Manual | Variable | +| Modo video con easing (`smooth`) | ✓ | ✓ | Manual | — | +| Track alto + viewport sticky integrados | ✓ | ✓ | Manual | — | +| Disciplina del framework (ActiveDom/tokens) | ✓ | — | — | — | +| Dependencia externa | Ninguna | Dependencia | Dependencia | — | + +## Decisiones + +- **Dos modos mutuamente excluyentes** (`frames` XOR `video`): mezclar ambos + en una API única invitaría a estados imposibles. +- **Frame = función pura del progreso**: sin estado interno de reproducción — + scrubbing determinista, resistente a saltos de scroll. +- **Canvas con cover-fit HiDPI** para el modo frames: nitidez a cualquier DPR + sin CLS. +- **`smooth` solo en video**: el easing del `currentTime` evita el stutter + del seek frame-a-frame; los frames de canvas no lo necesitan. + +## Gaps + +| Gap | Disposición | Detalle | +| --- | --- | --- | +| Carga perezosa/progresiva de la secuencia de frames | **diferir** | Hoy el consumidor pasa las URLs; un prefetch por ventana de progreso espera caso real. | +| Eventos sema de scrub | **descartar** | Display pasivo por contrato; sonorizar el scroll de la página sería ruido (misma razón que link-preview). | diff --git a/src/uix/eidos/components/timeline/README.md b/src/uix/eidos/components/timeline/README.md index 63eea79ad..c19db2747 100644 --- a/src/uix/eidos/components/timeline/README.md +++ b/src/uix/eidos/components/timeline/README.md @@ -92,3 +92,42 @@ Tokens: `src/uix/eidos/lib/recipes/base.ts` (`timeline`). Selectors: - `R-1.5 exception:` no focusable part of its own — items are display (`item` / `title` / `description`); anything focusable inside is consumer content that brings its own treatment. - `A-1.4 exception:` the APG defines no timeline pattern; the component is a display list — anything interactive inside is consumer content with its own contract. + +## Baseline + +Sin baseline air. Referencias del patrón: Ant Design Timeline, MUI Lab +Timeline y Flowbite — todos con UN eje de color (el estado tiñe). El +diferencial UIX es la sección "Two color axes" de arriba: **estado** +(`data-status` done/current/upcoming — la forma/relleno del marker) e +**intent** (`data-intent` — la valencia del evento) DESACOPLADOS, más el +feed vivo (`emerge-reveal` en soma) que ninguna de las referencias modela. + +## Comparativa + +| Capacidad | UIX | Ant Timeline | MUI Lab | Flowbite | +| --- | --- | --- | --- | --- | +| Ejes estado × intent desacoplados | **✓** | No (un color) | No | No | +| Feed vivo con revelado perceptivo (`emerge-reveal` + pack) | ✓ | No | No | No | +| Orientación + layout alternado | ✓ | ✓ | ✓ | Parcial | +| Markers/rail/connector como partes themables | ✓ | Parcial | ✓ | CSS | +| Iconos por evento | ✓ | ✓ | ✓ | ✓ | + +## Decisiones + +- **Dos ejes, dos vocabularios**: `data-status` gobierna la GEOMETRÍA del + marker (hecho/actual/futuro) y `data-intent` la VALENCIA (los tokens + intent→color del sistema) — un evento "fallo" pasado pinta rojo sin fingir + que no ocurrió. Es la doctrina data-color≠intent aplicada a un display. +- **El modelo de estado vive en soma** (derivación de `active`, el feed y + `emerge-reveal`) — este layer solo pinta rail/markers/connectors. +- **Tipografía del item en `1em` anotado** (`literal:`): título y descripción + heredan la fuente escalada del item (reset del UA size del heading), no + imponen tamaño propio. + +## Gaps + +| Gap | Disposición | Detalle | +| --- | --- | --- | +| Agrupación por fecha (cabeceras de día/mes) | **diferir** | Componible por el consumidor; slot dedicado espera caso real. | +| Colapso de tramos largos ("ver 12 más") | **diferir** | Idem — composición con Collapsible cubre hoy. | +| Zoom temporal (escala proporcional al tiempo) | **descartar** | Eso es un chart (eje temporal real) — pertenece a `$libs/plots`, no al display secuencial. | diff --git a/src/uix/eidos/components/timeline/timeline.css b/src/uix/eidos/components/timeline/timeline.css index b7047c45a..e9fcf20e8 100644 --- a/src/uix/eidos/components/timeline/timeline.css +++ b/src/uix/eidos/components/timeline/timeline.css @@ -434,7 +434,7 @@ [data-timeline-title] { margin: 0; - font-size: 1em; + font-size: 1em; /* literal: inherits the item's size-scaled font (resets the heading UA size) */ font-weight: var(--timeline-title-font-weight); color: var(--timeline-title-color); line-height: var(--timeline-line-height); @@ -442,7 +442,7 @@ [data-timeline-description] { margin: 0; - font-size: 1em; + font-size: 1em; /* literal: inherits the item's size-scaled font */ color: var(--timeline-description-color); line-height: var(--timeline-line-height); } diff --git a/src/uix/morfo/components/timeline.ts b/src/uix/morfo/components/timeline.ts index eaba1728e..eed8a48c9 100644 --- a/src/uix/morfo/components/timeline.ts +++ b/src/uix/morfo/components/timeline.ts @@ -37,6 +37,7 @@ export const timelineMorfo = { name: 'Timeline', kebab: 'timeline', scope: ['soma', 'sema', 'eidos'], + apg: 'none — chronological display list with no APG pattern; interactive content inside items (links, actions) carries its own patterns (checkpoint C5 form)', expression: 'pack', texts: { label: '#?components.timeline.label|Timeline'