uix(sidebar): la fila que navega decía «te sentí» a nadie, y el libro dice cómo se dice
«Navegar una fila es nativo y no suena» era una decisión de la fase 1 tomada en
silencio (el plan la dejó como «decidir en fase 1») sobre una premisa falsa —
«item = Link puro»—: la fila es `<button>` cuando no tiene `href`, la acción es
un botón pelado y el `MenuItem` posee un flyout. Medido en navegador: pulsar
fila, sub-fila o acción estampaba CERO; el Trigger sí acusaba recibo, porque
compone `Button`.
El libro no deja margen: cap. 22 §9 compone el enlace como «contact.press
seguido de shift.navigate: la presión no es la navegación», cap. 27 §8 lo
repite y cap. 22 §12 llama antipatrón al contacto mudo. Así que la fila declara
las dos ocurrencias, y la aparición del flyout —cap. 26 §5-§6, un menú anclado—
deja de ser un cambio de estado sin evento.
Dónde se estampa cada una lo decidió la doctrina, no la comodidad: el gesto en
la mano (la fila, anclado por instancia: es parte REPETIDA) y el cruce en la
superficie que cruza como unidad (el provider). Eso evita la quinta pareja
`queue` que yo iba a declarar: calendar ya movió su `shift-navigate` fuera del
botón porque las dos ocurrencias se comían la única ranura y el press-squeeze
no llegaba a pintarse.
Voz: contact y shift toman la de su familia (`touch`, `slide`) sin escribir
nada; los dos `emerge-*-sub` van SILENT como el tooltip — el flyout se abre al
pasar el puntero, y un barrido por el raíl serían ocho apariciones sonando
(cap. 26 §7, Criterio 12).
El `trigger` NO declara contacto: lo trae por composición de `Button`. El
`rail` sí, porque es una franja de cromo y nadie más lo estampa.
Medido en el navegador tras el cambio: fila con href → `contact-activate`
(press-squeeze corriendo) en ESA fila + `shift-navigate` en el shell; fila
deshabilitada → nada; sub-fila → el mismo par; rail → contacto propio +
`emerge-collapse` del panel; en modo icono, hover → `emerge-open-sub`
(present-rise), hover repetido → nada, salir → `emerge-close-sub`
(dismiss-fade). Suite nueva de 8 contratos, `check` con delta CERO contra la
base medida con stash (72/72), morfo:check PASS, contracts sin fallos nuevos.
Plan y decisiones firmadas: docs/process/PLAN-sidebar.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2 months ago
|
|
|
|
# PLAN — `Sidebar`: la fila habla lo que es (sema por el libro) y viste la talla del sistema (eje `size`)
|
|
|
|
|
|
|
|
|
|
|
|
> **Estado: PLAN ENTREGADO 2026-08-19 — NADA CONSTRUIDO, NADA FIRMADO.** Falta
|
|
|
|
|
|
> que el autor firme las decisiones D-SB de §4 (una por mensaje, con sus cinco
|
|
|
|
|
|
> preguntas) y ordene el arranque de F1.
|
|
|
|
|
|
>
|
|
|
|
|
|
> **Origen (conversación 2026-08-19)**: el autor pidió analizar por qué
|
|
|
|
|
|
> `Sidebar.MenuButton` / `Sidebar.MenuItem` no emiten semántica y contrastarlo
|
|
|
|
|
|
> con el libro, y añadió: «el componente sidebar está mal construido, la
|
|
|
|
|
|
> tipografía y los tamaños no responden a los del sistema». Su veredicto,
|
|
|
|
|
|
> textual, manda sobre este documento: **«emitir o no emitir no es una
|
|
|
|
|
|
> decisión, ya que el libro es el canon, y la naturaleza de los eventos es la
|
|
|
|
|
|
> que es; por lo tanto tienen que estar acorde al tipo de eventos que emite. Hay
|
|
|
|
|
|
> que corregirlo.»**
|
|
|
|
|
|
>
|
|
|
|
|
|
> **Kickoff para sesión nueva**: _«Lee `docs/process/PLAN-sidebar.md`; si las
|
|
|
|
|
|
> D-SB de §4 están firmadas, ejecuta la fase que toque de §5 con sus gates; si
|
|
|
|
|
|
> no, PARA y preséntalas una a una.»_ Cada fase lista qué leer antes, qué
|
|
|
|
|
|
> producir y con qué guard se verifica.
|
|
|
|
|
|
>
|
|
|
|
|
|
> Fuentes de doctrina que este plan cita y NO copia: el libro
|
|
|
|
|
|
> (`docs/Disenando_lo_que_ocurre_FINAL.pdf`, anclas en `docs/book-map.md`),
|
|
|
|
|
|
> [`docs/CANON.md`](../CANON.md), [`docs/architecture/sema.md`](../architecture/sema.md),
|
|
|
|
|
|
> [`docs/theming/reference.md`](../theming/reference.md) §5,
|
|
|
|
|
|
> [`docs/guides/component-guide.md`](../guides/component-guide.md) §Build contract.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 0. Veredicto corto (la valoración que pidió el autor)
|
|
|
|
|
|
|
|
|
|
|
|
1. **La fila muda contradice el libro, no lo aplica.** Cap. 22 §9 compone el
|
|
|
|
|
|
enlace como «Link → contact.press seguido de shift.navigate: la presión no
|
|
|
|
|
|
es la navegación»; cap. 27 §8 repite «Navegación → contact.press ·
|
|
|
|
|
|
shift.navigate»; cap. 22 §12 nombra el antipatrón «Contacto mudo»; CANON §8
|
|
|
|
|
|
regla 1 hace obligatorio el contact «when there is direct action». La fila
|
|
|
|
|
|
del sidebar es un control de navegación POR DECLARACIÓN (`<a href>` dentro
|
|
|
|
|
|
del landmark `<nav>`, `aria-current="page"` del mismo prop que
|
|
|
|
|
|
`data-active`): sabe que navega, luego declara las dos ocurrencias. Lo que
|
|
|
|
|
|
NO sabe —la orientación del destino: foco, título, landmarks (cap. 27 §9)—
|
|
|
|
|
|
sigue siendo deber del shell/app y no excusa a la fila.
|
|
|
|
|
|
2. **«Nativo y sin cue» era una decisión de fase 1 tomada en silencio**, no una
|
|
|
|
|
|
firma: el plan dejó el punto abierto («activación de item si el item es
|
|
|
|
|
|
más que un `Link` — decidir en fase 1», `PLAN-blocks.md:394-397`), la fase 0
|
|
|
|
|
|
firmó sólo el `emerge` del panel (`PLAN-blocks.md:1659-1664`), y la premisa
|
|
|
|
|
|
«item = Link puro» no se cumple: `MenuButton` es `<button>` sin `href`,
|
|
|
|
|
|
`MenuAction` es un `<button>` pelado, `MenuItem` POSEE un flyout.
|
|
|
|
|
|
3. **El framework ya se contradice a sí mismo** sobre el mismo gesto:
|
|
|
|
|
|
`navigation-menu` emite `commit-select + affirm` al pulsar su `link`
|
|
|
|
|
|
(«Navigating IS the evaluable act... it had no voice»,
|
|
|
|
|
|
`navigation-menu-provider.svelte.ts:715-728`), mientras `link` /
|
|
|
|
|
|
`anchor-nav` / `breadcrumb` / `nav-tree` / `sidebar` callan «por contrato
|
|
|
|
|
|
escrito» (A-50 del ledger de blocks). Por el libro ninguna de las dos es
|
|
|
|
|
|
exacta: «shift no es commit» (cap. 27 §2). Este plan arregla el sidebar;
|
|
|
|
|
|
los demás son deuda transversal (§6) que se firma aparte, sin cascada.
|
|
|
|
|
|
4. **El silencio sólo es respuesta válida como CANAL, nunca como evento sin
|
|
|
|
|
|
declarar**: CANON §7 («where no base exists, silence — a valid answer») y el
|
|
|
|
|
|
pack `SILENT` del tooltip hablan de la VOZ; Apéndice A del libro exige el
|
|
|
|
|
|
evento declarado («el runtime no debe adivinar»). La corrección es declarar
|
|
|
|
|
|
los eventos que la naturaleza de cada parte dicta y dejar que el pack decida
|
|
|
|
|
|
cuánto se oye.
|
|
|
|
|
|
5. **Tallas**: el sidebar se construyó fuera del canon de tamaño (sin `size`,
|
|
|
|
|
|
un token de ESPACIO como altura de control, `sm` clavado, fuente del UA en la
|
|
|
|
|
|
acción). La corrección es mecánica y tiene precedentes en el propio repo
|
|
|
|
|
|
(`breadcrumb` para el eje `data-size`, `dialog/context.ts` para la
|
|
|
|
|
|
derivación container→part).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Inventario de defectos — medidos, con evidencia y canon
|
|
|
|
|
|
|
|
|
|
|
|
Todo lo de abajo está **medido en navegador** (dev server `verify`, ruta
|
|
|
|
|
|
`/uix/components/sidebar`, 2026-08-19): MutationObserver sobre `data-event*`
|
|
|
|
|
|
en todo el documento; control positivo el `Trigger` (compone `Button`), que
|
|
|
|
|
|
estampó `contact-activate` (sig-0) y `emerge-collapse` en el `panel` (sig-1).
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 Sema — el libro es el canon
|
|
|
|
|
|
|
|
|
|
|
|
| Id | Parte | Hoy (medido) | Canon que incumple | Evidencia en código |
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
| **S-1** | `menu-button` (`<a href>` — la fila que navega) | clic → **0 estampas**, 0 sonido | Cap. 22 §9 «Link → contact.press · shift.navigate»; cap. 27 §5/§8; cap. 22 §12 «Contacto mudo»; CANON §8 r.1; cap. 36 §8 («un botón puede participar en… shift.navigate») | morfo `sidebar.ts:48-52` («Navigating a menu item is native and unsonified»), `:319-342` (0 eventos, sin archetype); provider `sidebar-provider.svelte.ts:584-589` (props sin `onclick`); pack `sema/components/sidebar.ts:16-18` |
|
|
|
|
|
|
| **S-1b** | `menu-button` (`<button>` — sin `href`, «only opens a sub-menu») | clic → 0 estampas y **0 comportamiento** | Cap. 22 §9 «Botón → contact.press»; A-62 del ledger («el contacto inicia y nada resuelve» — aquí ni inicia) | `sidebar-menu-button.svelte:48-51` |
|
|
|
|
|
|
| **S-2** | `menu-sub-button` | clic → 0 estampas | = S-1 («Same active contract as MenuButton», `sidebar.ts:399`) | `sidebar-provider.svelte.ts:732-735` |
|
|
|
|
|
|
| **S-3** | `menu-action` (`<button archetype=trigger>`) | clic → 0 estampas; **fuente 13,33px / `line-height: normal` del UA** | Cap. 22 §9 «Botón → contact.press»; Build contract «Composition» (componer `Button`, no reimplementar) | morfo `sidebar.ts:347-363`; eidos `sidebar-menu-action.svelte:3-6` («Native button… not the `Button` component») |
|
|
|
|
|
|
| **S-4** | `rail` (`<button archetype=trigger>`) | clic → `emerge-*` en `panel`, **nada en el propio rail** (el `Trigger` sí acusa recibo porque compone `Button`) | Cap. 22 §9; CANON §8 r.1 — el mismo toggle, dos voces distintas según el botón que se pulse | `sidebar-provider.svelte.ts:267-273`; eidos `sidebar-rail.svelte:3-6` |
|
|
|
|
|
|
| **S-5** | `menu-item` — dueño del **flyout** de modo icono (`openSub`/`closeSub`) | hover/foco → el sub-menú flotante aparece y desaparece con **0 eventos** | Cap. 26 §5 (`emerge.open/close`: «algo se abre o se cierra con marco propio: menú, popover, panel»), §6 «aparición anclada — dropdown»; precedente `dropdown-menu` `emerge-open-sub`/`-close-sub` («Not a wiring bug: there was no event», `dropdown-menu.ts:62-96`) | `sidebar-provider.svelte.ts:484-496`, `:470-475`; morfo `menu-sub` `:381-397` ya declara `states: ['open','closed']` |
|
|
|
|
|
|
|
|
|
|
|
|
`menu-item` como `<li>` posicionador es marco estructural (CANON §5): 0
|
|
|
|
|
|
eventos es correcto **en esa faceta**. Su defecto es S-5: posee una aparición
|
|
|
|
|
|
y no la declara.
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 Eidos — el canon de tamaño y el Build contract
|
|
|
|
|
|
|
|
|
|
|
|
| Id | Qué | Hoy (medido / leído) | Canon que incumple | Evidencia |
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
| **E-1** | Eje `size` inexistente | filas `font-size: var(--font-size-sm)` (14px medido) + `min-block-size: var(--space-8)` (32px) — un token de ESPACIO (escala `density-space`) como altura de CONTROL (escala `density-control`); `data-size` ausente; `group-label` clava `xs` | theming/reference §5: bundle `--size-{k}-*` obligatorio; «Nav controls xs..lg»; size→font 1:1 y «compact md=14» REVOCADO 2026-06-17; Build contract «Size (controls)» («consuming none of the bundle» es la deriva nombrada) y «Density / spacing» | `sidebar.css:209-231`, `:173-181`; tokens `recipes/base.ts:4725-4741`; eidos `types.ts:35-42` (sin `size`) |
|
|
|
|
|
|
| **E-2** | El guard no lo ve | `recipe-css-contract.test.ts:613-639` escanea `RECIPE_TOKENS`; el `font-size` va escrito en el `.css` y `--space-8` no casa con `RAW_COORDINATE` | memoria `a-guard-that-inspects-nothing-passes` — verde y ciego | `sidebar.css:221` |
|
|
|
|
|
|
| **E-3** | `MenuAction` sin fuente | 13,33px `line-height: normal` (UA) — fuera de la escala | theming §5 1:1; Build contract «Composition» | `sidebar.css:260-273` |
|
|
|
|
|
|
| **E-4** | Hover a mano | `background: var(--color-surface-raised)` en `:hover` | Build contract «State (hover/active)»: capa `--state-{hover,press,selected}`, no `--x-hover-bg` por componente (`archetypes.css:59-83`) | `sidebar.css:233-237`, `:275-278` |
|
|
|
|
|
|
| **E-5** | Tallas clavadas en las composiciones | `Trigger` `size="sm"`; `MenuBadge` `size="xs"` | theming §5 «Container→part derivation: capped at md» (precedente `dialog/context.ts:6-13` + `Dialog.Close`) | `sidebar-trigger.svelte:15`; `sidebar-menu-badge.svelte:16` |
|
|
|
|
|
|
| **E-6** | Ancho del raíl de iconos fijo | `--sidebar-width-icon: 3rem` = 32px de fila + 2×8px; en cuanto la fila siga al bundle (md = 36px) el raíl **no cabe** | derivación, no constante (la propia justificación del token: «widths are TOKENS, never TS constants») | `recipes/base.ts:4727`; `sidebar.css:102-104` |
|
|
|
|
|
|
| **E-7** | Iconos de la fila | la demo pasa `size="sm"` a cada `Icon` a mano; la fila no dimensiona su glifo | bundle `--size-{k}-icon-size` (como `Button`) | `web/routes/uix/components/sidebar/+page.svelte:158-220` |
|
|
|
|
|
|
|
|
|
|
|
|
**Corrección a mi propio análisis inicial**: dije que `--sidebar-width` /
|
|
|
|
|
|
`--sidebar-width-mobile` (16rem / 18rem) «ignoran `--scaling`». Son anchos de
|
|
|
|
|
|
LAYOUT, y el sistema fija sus propios anchos de layout en rem
|
|
|
|
|
|
(`--content-width-*`, `base.css:146-152`) — no siguen `--scaling` por
|
|
|
|
|
|
diseño. **No son defecto.** Sólo `width-icon` (E-6) lo es, porque es una
|
|
|
|
|
|
medida de CONTROL (fila + padding).
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Contrato objetivo — sema
|
|
|
|
|
|
|
|
|
|
|
|
Los eventos que el morfo del sidebar declara tras la corrección. Nombres bajo
|
|
|
|
|
|
la gramática `{family}-{verb}[-{nuance}]` (CANON §6); anclaje por instancia
|
|
|
|
|
|
porque `menu-button`, `menu-sub-button` y `menu-sub` son partes REPETIDAS
|
|
|
|
|
|
(memoria `repeated-part-cannot-be-the-subject`; `SomaRuntimePart.trigger` anclado,
|
|
|
|
|
|
`runtime.svelte.ts:243-258`; `partInstance` `:299`).
|
|
|
|
|
|
|
|
|
|
|
|
| Evento | Familia · verbo | `target` (+ `allowedTargets`) | `sequence` | Cuándo lo emite el soma | Voz por defecto (pack) |
|
|
|
|
|
|
|---|---|---|---|---|---|
|
|
|
|
|
|
| `emerge-expand` / `emerge-collapse` | (sin cambio) | `panel` | post | `toggle()` | `emerge.soft` / `emerge.exit.soft` (como hoy) |
|
|
|
|
|
|
| **`contact-activate`** | contact · activate — el verbo de `Button` (`button.ts:56-71`); cap. 22 §6: los verbos de contact «no son gestos distintos que exijan semánticas distintas» | `menu-button` + `allowedTargets: [menu-sub-button, rail]` | **pre** | `onclick` de la fila / sub-fila (`<a>` y `<button>`, ratón y Enter/Espacio nativos) y del rail ANTES de `toggle()`; guardado por `disabled`; el `mergeProps` de la casa ya deja al consumidor vetar con `preventDefault` (`props.ts:20-27`) | familia: `touch` + háptico `tick` (`sema-map.ts:411-424`); firma visual `press-squeeze` GRATIS por regla de familia (`base.css:6636`) — sin CSS nuevo |
|
|
|
|
|
|
| **`shift-navigate`** | shift · navigate — cap. 27 §5 «Navegación: página A → página B» | **`provider`** (el shell que cruza como unidad — D-SB.1bis; antes decía `menu-button` + `queue`) | **post**, sin `regime` | mismo `onclick`, emitido SIN anclar (`runtime.trigger`), sólo si hay `href` y no está `disabled` (la fila es un control de navegación; el `<button>` sin `href` NO lo emite — su consecuencia la pone lo que el consumidor compone). Sin `direction` (CANON §9: «an invented sense is worse than none»); sin intent | familia: `slide` (`sema-map.ts:472-482`); sin firma visual (sin `direction` no hay cruce que dibujar, `presets/css.ts:387-412`) — el cruce lo pinta la página |
|
|
|
|
|
|
| **`emerge-open-sub`** / **`emerge-close-sub`** | emerge · open / close — cap. 26 §5-§6 (menú anclado) | `menu-sub` (repetida → anclada) | post / pre; `commits` `data-state` open/closed sobre `menu-sub` (patrón `dropdown-menu.ts:62-96`) | `openSub()` / `closeSub()` — los ÚNICOS mutadores (pointerenter, focusin, focusout, pointerleave, Escape, y el `watch` de `floating`); sólo en modo icono (`floating`) y sólo cuando el estado CAMBIA (nada de emisiones fantasma, lección de nav-menu) | **D-SB.3** (recomiendo `SILENT` como el tooltip: barrido del puntero por el raíl = una aparición por fila; cap. 26 §7 «el sonido, normalmente, sobra»; cap. 7 §13 Criterio 12) |
|
|
|
|
|
|
|
|
|
|
|
|
Dos hechos que fijan la forma:
|
|
|
|
|
|
|
|
|
|
|
|
- **Dos ocurrencias, dos nodos — sin `queue`.** La superficie `data-event-*` es
|
|
|
|
|
|
UNA RANURA por elemento (memoria `perceptual-surface-one-slot`) y `sema.md`
|
|
|
|
|
|
manda «reach for `regime` last»: el gesto se estampa donde está la mano (la
|
|
|
|
|
|
fila) y el cruce en la superficie que cruza como unidad (el `provider`) —
|
|
|
|
|
|
el precedente de `calendar` (el sello de shift se movió del botón porque la
|
|
|
|
|
|
compresión no se pintaba) y de `field-langs` (`provider`). ~~La fila que
|
|
|
|
|
|
navega es la quinta pareja `queue`~~ — enmendado por D-SB.1bis.
|
|
|
|
|
|
- **`contact` de `Trigger` y `MenuAction` NO va en el morfo**: lo aporta
|
|
|
|
|
|
`Button` por composición (regla de nav-menu, `navigation-menu.ts:64-68`:
|
|
|
|
|
|
«re-declaring it here would put the same verb twice on the same node»). El
|
|
|
|
|
|
`rail` sí se declara porque NO compone `Button` (es una franja de cromo, no
|
|
|
|
|
|
un control de acción — razón válida del wrapper). La FILA tampoco compone
|
|
|
|
|
|
`Button`: su cromo es de fila (`data-active`, `aria-current`, glifo+badge) y
|
|
|
|
|
|
su segundo evento (`shift`) es del sidebar; declarar ambos en el mismo morfo
|
|
|
|
|
|
es lo que hace `queue` verificable (una sola runtime).
|
|
|
|
|
|
|
|
|
|
|
|
Precedentes que este contrato copia: `toggle` (contact + consecuencia en un
|
|
|
|
|
|
nodo con `queue`), `field-langs` / `carousel` / `calendar` (shift emitido por
|
|
|
|
|
|
el componente que sabe que cruza), `dropdown-menu` (`emerge-*-sub` desde el
|
|
|
|
|
|
mutador único), `navigation-menu` (anclaje por instancia en una parte
|
|
|
|
|
|
repetida), «CTA que navega» del README de `Button` (un `<a>` que navega SÍ
|
|
|
|
|
|
lleva contact en esta casa).
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Contrato objetivo — eidos (`size`)
|
|
|
|
|
|
|
|
|
|
|
|
- **Prop `size`** en el wrapper eidos `<Sidebar>`: `xs | sm | md | lg`
|
|
|
|
|
|
(«Nav controls», theming §5), `ResponsiveProp` resuelto con
|
|
|
|
|
|
`eidos.resolve(size, 'md')` y estampado como **`data-size` en el provider**
|
|
|
|
|
|
— atributo VISUAL del wrapper, **no del morfo** (`render-css.ts:2122-2127`;
|
|
|
|
|
|
memoria memoria `visual-wrapper-attrs-not-in-morfo`); patrón exacto
|
|
|
|
|
|
`breadcrumb.svelte:11-17`. Default **`md`** (theming §5: «`md` is the
|
|
|
|
|
|
default»; 1:1 → fila 36px / 16px). Quien quiera la densidad de hoy pasa
|
|
|
|
|
|
`size="sm"` (30px / 14px).
|
|
|
|
|
|
- **Tokens por talla** en `recipes/base.ts` (patrón `breadcrumb`
|
|
|
|
|
|
`base.ts:519-529`), todos referencias al bundle — así el guard del bundle
|
|
|
|
|
|
por fin los VE: `row-height-{k}: var(--size-{k}-control-height)` ·
|
|
|
|
|
|
`font-size-{k}: var(--size-{k}-font-size)` · `line-height-{k}` ·
|
|
|
|
|
|
`letter-spacing-{k}` · `icon-size-{k}` · `row-padding-inline-{k}` ·
|
|
|
|
|
|
`row-gap-{k}` · `row-radius-{k}`. Internos `--_sidebar-*` conmutados por
|
|
|
|
|
|
`[data-sidebar][data-size]`; el `.css` sólo consume internos (E-2 deja de ser
|
|
|
|
|
|
ciego porque el valor vive en el token). Se retiran `row-height`,
|
|
|
|
|
|
`row-radius`, `row-gap` (sustituidos por su forma por talla).
|
|
|
|
|
|
- **`group-label`**: **D-SB.4** — recomiendo un paso por debajo de la fila
|
|
|
|
|
|
(`--size-{k-1}-font-size`), porque ES una etiqueta (nombra al `menu` por
|
|
|
|
|
|
`aria-labelledby`) y la doctrina de etiqueta (theming §5 «Fields: input 1:1
|
|
|
|
|
|
+ the label one step below») se funda en la jerarquía etiqueta↔control, no
|
|
|
|
|
|
en que sea un `<label>`. A `sm` reproduce la proporción de shadcn (xs/sm).
|
|
|
|
|
|
- **`width-icon` derivado**, con `scope: 'host'` (TSC: nada derivado en
|
|
|
|
|
|
`:root`): `calc(var(--_sidebar-row-height) + 2 * var(--sidebar-padding))`.
|
|
|
|
|
|
- **Derivación container→part, capada en `md`** (theming §5; contexto eidos
|
|
|
|
|
|
como `dialog/context.ts`): `Trigger` (compone `Button`), `MenuAction`
|
|
|
|
|
|
(pasa a componer **`IconButton variant="ghost"`** — contact + talla + capa
|
|
|
|
|
|
de estado gratis; el recipe de la acción desaparece), `MenuBadge`
|
|
|
|
|
|
(`Badge`, recortado a su subset `xs..lg`). Un `size` explícito en la parte
|
|
|
|
|
|
gana. Recogido en **D-SB.5**.
|
|
|
|
|
|
- **Capa de estado**: hover de fila y sub-fila por
|
|
|
|
|
|
`background-image: linear-gradient(var(--state-hover), var(--state-hover))`
|
|
|
|
|
|
(las filas no llevan archetype a propósito — precedente `anchor-nav.ts:68-69`
|
|
|
|
|
|
— así que el recipe aplica la capa, como documenta `archetypes.css:59-83`);
|
|
|
|
|
|
el acento `[data-active]` (`--color-primary-track/-text`) se queda: «per-variant
|
|
|
|
|
|
accent stays in the recipe». La pulsación NO se escribe: la pone la firma de
|
|
|
|
|
|
familia `contact`.
|
|
|
|
|
|
- **Glifo de fila**: la fila dimensiona su primer hijo `svg` con
|
|
|
|
|
|
`--_sidebar-icon-size` (como `Button`); la demo deja de pasar `size="sm"`.
|
|
|
|
|
|
- **Se conservan** `width`, `width-mobile` (layout), `padding`, `gap`,
|
|
|
|
|
|
`sub-indent`, `sub-z`, `bg`, `border-color`, `rail-width`.
|
|
|
|
|
|
- **A verificar en F3, no detectado**: hit-area táctil de las filas bajo
|
|
|
|
|
|
`pointer: coarse` (Build contract §37) — sin archetype, la regla de
|
|
|
|
|
|
`archetypes.css` no las alcanza; si falta, `::before` slop.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Decisiones que sólo el autor firma (D-SB)
|
|
|
|
|
|
|
|
|
|
|
|
Una por mensaje, con las cinco preguntas. Todas traen mi recomendación; el
|
|
|
|
|
|
resto del plan asume la recomendada salvo firma en contra.
|
|
|
|
|
|
|
|
|
|
|
|
| Id | Decisión | Recomendación y por qué |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| **D-SB.1** | ¿La fila declara **`contact-activate` + `shift-navigate`** (libro) — o sólo `contact` y el `shift` lo emite app-land en `afterNavigate`? | **Las dos en la fila.** El libro compone el enlace así (cap. 22 §9, 27 §8); la fila es control de navegación por declaración; `field-langs`/`carousel` ya emiten `shift` desde el componente que sabe que cruza. App-land conserva `shift` para las consecuencias que ningún componente conoce (el `Select` de idioma) y la orientación del destino (cap. 27 §9). |
|
|
|
|
|
|
| **D-SB.2** | Verbo de contact: `activate` (Button) o `press` (toggle/switch) | **`activate`** — misma gramática que `Trigger` (Button) y que el rail; el sonido es idéntico (`touch`, verbo indiferente). |
|
|
|
|
|
|
| **D-SB.3** | Voz del flyout `emerge-open-sub` / `-close-sub` | **`SILENT`** en el pack (como tooltip): hover por el raíl = una aparición por fila; cap. 26 §7; Criterio 12. La firma visual (`present-rise`) se queda. |
|
|
|
|
|
|
| **D-SB.4** | Talla del `group-label` | **Un paso por debajo de la fila** (doctrina de etiqueta, §3). Alternativa: misma talla que la fila. |
|
|
|
|
|
|
| **D-SB.5** | Derivación de `Trigger` / `MenuAction` / `MenuBadge` | **La regla que ya existe** (container→part capada en `md`), sin inventar un «un paso por debajo» para controles anidados. `MenuAction` = `IconButton ghost`. |
|
|
|
|
|
|
| **D-SB.6** | Default de `size` | **`md`** (canon). Cambia el aspecto de la demo y del `app-shell` (filas 32/14 → 36/16): se mide y se enseña. |
|
|
|
|
|
|
| **D-SB.7** | Rama de trabajo y ejecutor | Rama del eje a nombrar por el autor; construye un agente Opus con brief de §5, Fable supervisa (patrón `PLAN-background.md` §10-11). |
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 D-SB.1 — la contra-lectura `commit.select` (app-shell README, Gaps) y por qué el libro no la sostiene
|
|
|
|
|
|
|
|
|
|
|
|
La sesión que destapó el defecto lo dejó escrito en la fila de Gaps de
|
|
|
|
|
|
`src/uix/blocks/app-shell/README.md` («`Sidebar.MenuButton` es MUDO y
|
|
|
|
|
|
`NavigationMenu.Link` no») con un matiz: «en un shell la fila del raíl casi
|
|
|
|
|
|
nunca navega: SELECCIONA la sección (`aria-current="page"`), que es exactamente
|
|
|
|
|
|
el caso de `commit.select`. No se decide desde el tier». La pregunta es la
|
|
|
|
|
|
correcta —¿qué queda FIJADO o qué MARCO cambia?— y el libro la responde en
|
|
|
|
|
|
contra de `commit.select` para esta fila:
|
|
|
|
|
|
|
|
|
|
|
|
1. **El libro nombra el acto**: cap. 8 (TABLA de reubicación) «selection →
|
|
|
|
|
|
commit.select» y, aparte, «navigation → shift.navigate»; «context → shift si
|
|
|
|
|
|
cambia marco; emerge si sólo aparece superficie». Cap. 27 §1: «ir a otra
|
|
|
|
|
|
pantalla, avanzar en un flujo, **cambiar de vista**… todo eso es shift»; §5
|
|
|
|
|
|
«Navegación — shift.navigate: lista → detalle, **página A → página B**»; §3
|
|
|
|
|
|
«lista frente a detalle: cambia qué puedo hacer, qué significa cada acción,
|
|
|
|
|
|
qué es relevante y **cómo vuelvo**». Inbox → Drafts → Settings cambia las
|
|
|
|
|
|
tres cosas y el botón «atrás» del navegador lo prueba.
|
|
|
|
|
|
2. **`aria-current="page"` es HUELLA, no evento** — y es huella de NAVEGACIÓN.
|
|
|
|
|
|
Cap. 1 §7 (TABLA plano/pregunta): «seleccionado» es ESTADO, «seleccionar» es
|
|
|
|
|
|
EVENTO; cap. 12 §6 la huella. La fila no fija nada al pulsar: `active` es un
|
|
|
|
|
|
PROP que el app le pasa DESPUÉS de que el router cambió de ruta; en `Tabs`
|
|
|
|
|
|
el componente posee `value` y lo fija — ahí `commit.select` es honesto,
|
|
|
|
|
|
aquí sería atribuirle a la fila un estado que no es suyo. Y ARIA reserva la
|
|
|
|
|
|
selección a `aria-selected` (tabs, listbox, grid); `aria-current="page"`
|
|
|
|
|
|
significa «la página actual dentro de un conjunto de páginas» — el
|
|
|
|
|
|
vocabulario de la navegación, elegido a propósito por el dossier §P4
|
|
|
|
|
|
(APG Disclosure Navigation + enlaces + `<nav>`). Un lector anuncia «enlace,
|
|
|
|
|
|
página actual»; sema no puede decir «elegido» sobre el mismo nodo.
|
|
|
|
|
|
3. **La lectura «selecciona la sección» ya tiene componente**: `Tabs`
|
|
|
|
|
|
(vertical) — que existe y habla `commit-select`. El morfo del sidebar sólo
|
|
|
|
|
|
conoce dos formas de fila: `<a>` «when it navigates», `<button>` «when it
|
|
|
|
|
|
only opens a sub-menu». No hay tercera («selecciona sin navegar»); si un
|
|
|
|
|
|
shell la necesita, compone `Tabs`, no re-semantiza el `Sidebar`.
|
|
|
|
|
|
4. **«casi nunca navega» confunde persistencia del cromo con ausencia de
|
|
|
|
|
|
navegación**: en la demo (`href="/app/drafts"`) cambian URL, historial y
|
|
|
|
|
|
título; que el shell persista es la DEFINICIÓN de app shell, no la negación
|
|
|
|
|
|
de «página A → página B».
|
|
|
|
|
|
5. **`commit-select + affirm` sobre un enlace es un antipatrón con nombre**:
|
|
|
|
|
|
cap. 10 §12 (TABLA de antipatrones) «Success de navegación — cambio de
|
|
|
|
|
|
contexto con logro → shift + commit.fulfill si procede». Es exactamente lo
|
|
|
|
|
|
que hace hoy `navigation-menu` (T-1): el precedente que hay que reabrir,
|
|
|
|
|
|
no el que hay que copiar.
|
|
|
|
|
|
|
|
|
|
|
|
Consecuencia: D-SB.1 se mantiene (contact + shift en la fila); la fila de Gaps
|
|
|
|
|
|
del `app-shell` se cierra desde este eje cuando F1 aterrice, y T-1 gana la cita
|
|
|
|
|
|
del antipatrón.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 5. Fases y gates
|
|
|
|
|
|
|
|
|
|
|
|
Cada fase: **qué leer ANTES** (entero, no a medias — memoria `read-full-doctrine-before-auditing`),
|
|
|
|
|
|
**qué producir**, **guard**. Ninguna fase arranca sin la anterior en verde.
|
|
|
|
|
|
|
|
|
|
|
|
### F0 — Lectura obligatoria (sin producir nada)
|
|
|
|
|
|
|
|
|
|
|
|
Libro cap. 22 (§6, §9, §11-12), cap. 26 (§5-§7, §9), cap. 27 (§2, §5, §8-§10),
|
|
|
|
|
|
cap. 30 §4 (regla 1), Apéndice A · `docs/CANON.md` entero ·
|
|
|
|
|
|
`docs/architecture/sema.md` (cascada, pack, `regime`) ·
|
|
|
|
|
|
`docs/architecture/morfo.md` (partes repetidas, `allowedTargets`) ·
|
|
|
|
|
|
`docs/theming/reference.md` §5 y §23 · `docs/guides/component-guide.md`
|
|
|
|
|
|
§Build contract + §2 (audit Morfo/Sema) · `src/uix/eidos/components/README.md`
|
|
|
|
|
|
· los tres precedentes: `toggle.ts`, `dropdown-menu.ts`, `breadcrumb.*` ·
|
|
|
|
|
|
`CONTINUE-perceptual-surface.md` §3.0 (una ranura, `queue`, anclaje) ·
|
|
|
|
|
|
memorias: memoria `perceptual-surface-one-slot`, memoria `repeated-part-cannot-be-the-subject`,
|
|
|
|
|
|
memoria `hidden-pane-suspends-raf`, memoria `hmr-stale-tab-phantom-findings`,
|
|
|
|
|
|
memoria `a-guard-that-inspects-nothing-passes`.
|
|
|
|
|
|
|
|
|
|
|
|
### F1 — Morfo + sema (el contrato)
|
|
|
|
|
|
|
|
|
|
|
|
- Producir: `sidebar.ts` — los 5 eventos nuevos de §2 con sus docblocks
|
|
|
|
|
|
citando el libro (borrar «native and unsonified»); `sema/components/sidebar.ts`
|
|
|
|
|
|
— docblock nuevo + reglas sólo donde se DIFIERE del default (D-SB.3 →
|
|
|
|
|
|
`sound: SILENT` en los dos `emerge-*-sub`; nada para contact/shift si se
|
|
|
|
|
|
acepta la familia).
|
|
|
|
|
|
- Guard: `npx vitest run src/uix/morfo src/uix/sema src/uix/contracts.test.ts --project=server`
|
|
|
|
|
|
(validateMorfo: nombres/verbos/`intentRequirement`; pack-census:
|
|
|
|
|
|
selectores sobre `target`/`allowedTargets`) · `npm run morfo:check` ·
|
|
|
|
|
|
`npm run check` delta 0 contra base medida con **stash** (comparar por
|
|
|
|
|
|
MÉTODO, no por cifra).
|
|
|
|
|
|
|
|
|
|
|
|
### F2 — Soma (la emisión)
|
|
|
|
|
|
|
|
|
|
|
|
- Producir: `SidebarMenuButtonProvider.props.onclick` y
|
|
|
|
|
|
`SidebarMenuSubButtonProvider.props.onclick` → `runtimePart.trigger('contact-activate')`
|
|
|
|
|
|
y, si `href && !disabled`, `runtimePart.trigger('shift-navigate')`;
|
|
|
|
|
|
`SidebarToggleProvider` (rail): contact anclado antes de `toggle()` — sólo
|
|
|
|
|
|
la instancia `rail`, el `trigger` sigue callado en soma porque el eidos le
|
|
|
|
|
|
compone `Button`; `SidebarMenuItemProvider.openSub/closeSub` →
|
|
|
|
|
|
`runtime.partInstance('menu-sub', subRef)?.trigger('emerge-open-sub' | 'emerge-close-sub')`
|
|
|
|
|
|
sólo en `floating` y sólo si el estado cambia; `events` del runtime sin
|
|
|
|
|
|
handler para los nuevos (no mutan estado del sidebar).
|
|
|
|
|
|
- Guard: suite browser del componente (`*.svelte.test.ts` — crear si no
|
|
|
|
|
|
existe, con `createActiveUix` REAL, nunca fakes): clic en fila →
|
|
|
|
|
|
`contact-activate` y después `shift-navigate` con `data-event-id` distintos
|
|
|
|
|
|
y el segundo tras el hold; `<button>` sin href → sólo contact; disabled →
|
|
|
|
|
|
nada; rail → contact en rail + emerge en panel; icono+hover →
|
|
|
|
|
|
`emerge-open-sub` en ESA `menu-sub`, leave → `-close-sub`, expandido → nada.
|
|
|
|
|
|
|
|
|
|
|
|
### F3 — Eidos (`size` + capa de estado + composición)
|
|
|
|
|
|
|
|
|
|
|
|
- Producir: `types.ts` (`size`), `sidebar.svelte` (`resolvedSize` +
|
|
|
|
|
|
`data-size` + contexto eidos de talla), tokens por talla en
|
|
|
|
|
|
`recipes/base.ts` (+ regenerar `generated/base.css` por el script, NUNCA a
|
|
|
|
|
|
mano), `sidebar.css` (internos `--_sidebar-*`, capa `--state-hover`, glifo,
|
|
|
|
|
|
`width-icon` derivado), `sidebar-menu-action.svelte` → `IconButton`,
|
|
|
|
|
|
`sidebar-trigger.svelte` / `sidebar-menu-badge.svelte` → talla derivada.
|
|
|
|
|
|
- Guard: `npx vitest run src/uix/eidos` (recipe-css-contract: bundle, sin
|
|
|
|
|
|
literales px/rem en `font-size-*`; elevation; TSC) · `npx tsx scripts/eidos-lint.ts sidebar`
|
|
|
|
|
|
(0 invalid) · `npm run rtl:check` · `npm run component:audit -- --only sidebar`
|
|
|
|
|
|
PASS · **navegador**: computed `min-block-size` de la fila =
|
|
|
|
|
|
`--size-md-control-height` (36px) y `font-size` = `--size-md-font-size`
|
|
|
|
|
|
(16px) por defecto; con `size="sm"` 30/14; `MenuAction` = `IconButton`
|
|
|
|
|
|
con fuente de la escala; `--sidebar-width-icon` = fila + 2·padding en modo
|
|
|
|
|
|
icono; hover pinta `--state-hover`; `data-size` responsive cambia al
|
|
|
|
|
|
redimensionar; densidad `compact` mueve fila y padding a la vez.
|
|
|
|
|
|
|
|
|
|
|
|
### F4 — Docs + demo
|
|
|
|
|
|
|
|
|
|
|
|
- Producir: README soma (§Sema events: tabla completa con `Cuándo` y
|
|
|
|
|
|
«anclado por instancia») · README eidos (eventos + `size` + tokens) ·
|
|
|
|
|
|
demo `+page.svelte`: control `size` (paridad demos↔subset,
|
|
|
|
|
|
`demo-authoring.md` §6), texto de la pestaña sema, tabla de tokens ·
|
|
|
|
|
|
`PLAN-blocks.md` §F1.7: una línea de registro que apunte aquí ·
|
|
|
|
|
|
memoria del proyecto actualizada.
|
|
|
|
|
|
- Guard: `npm run docs:check` 0 errores · `npm run lint`.
|
|
|
|
|
|
|
|
|
|
|
|
### F5 — Verificación final en navegador (Chrome REAL, panel visible)
|
|
|
|
|
|
|
|
|
|
|
|
- Con `sound: true` (BootUix): clic en fila → `touch` y luego `slide` (osc +2
|
|
|
|
|
|
y +2, separados por el hold); rail → `touch` + `emerge.exit.soft`; hover en
|
|
|
|
|
|
raíl → 0 osciladores (SILENT) y `present-rise` visible; nada al hacer hover
|
|
|
|
|
|
en filas expandidas. `press-squeeze` corriendo en la fila durante la ventana
|
|
|
|
|
|
de contact. RTL (`side="right"` + `:dir(rtl)`) sin regresión del
|
|
|
|
|
|
off-canvas. 375px: Drawer intacto. Reduced motion: sin transiciones.
|
|
|
|
|
|
- Trampas conocidas: pane oculto congela rAF y el sello nunca cae
|
|
|
|
|
|
(memoria `hidden-pane-suspends-raf`); pestaña con HMR encima = hallazgos fantasma;
|
|
|
|
|
|
`performance` desde `javascript_tool` miente; el `once:true` ya enganchó.
|
|
|
|
|
|
- Cierre: commit(s) por fase con `-F`, sin `--no-verify`; el `check` final
|
|
|
|
|
|
comparado con la base por stash.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Deuda transversal detectada — NO se ejecuta en este eje
|
|
|
|
|
|
|
|
|
|
|
|
Cada una necesita su propia firma y su propia sesión (memoria `no-cascade-changes`).
|
|
|
|
|
|
Se listan para que no se pierdan, con la contradicción delante del autor.
|
|
|
|
|
|
|
|
|
|
|
|
| Id | Dónde | Qué | Por qué es del libro |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| **T-1** | `navigation-menu` `link` | emite `commit-select + affirm` al navegar (`navigation-menu.ts:16-32`; provider `:715-728`; pack con `haptic tap`) | cap. 27 §2 «shift no es commit»; cap. 22 §9; cap. 10 §12 antipatrón «Success de navegación» (cambio de contexto con logro). Mismo gesto que la fila del sidebar → misma familia. Decidido en la auditoría sema del 2026-08-05: hay que REABRIRLO, no pisarlo. La fila de Gaps de `src/uix/blocks/app-shell/README.md` («`Sidebar.MenuButton` es MUDO y `NavigationMenu.Link` no») se cierra con este eje + T-1 (ver §4.1). |
|
|
|
|
|
|
| **T-2** | `nav-tree` link (F1.8) · `anchor-nav` link (F1.6) · `breadcrumb` link | «links nativos → 0 eventos» (`nav-tree.ts:27-39`, `anchor-nav.ts:25-30`, `breadcrumb.ts:8-11`) | = S-1. Misma forma de arreglo (contact + shift en la parte `link`, anclado). Cada uno en su sesión; `nav-tree.css:17` arrastra además el pin `sm` (= E-1). |
|
|
|
|
|
|
| **T-3** | `Link` (primitivo) | eidos-only (`scope: ['eidos']`, `link.ts:21`): sin runtime, no PUEDE emitir; la casa cubre el caso con «CTA que navega» (`Button` + `child`) | Darle voz exige membresía soma (decisión de autor). Mientras, la ficha **A-62** del ledger de blocks se equivoca al suponer que un `Link` «emite `shift-navigate`»: hoy no emite nada — corregir la ficha en el eje de blocks. |
|
|
|
|
|
|
| **T-4** | Ledger blocks **A-50** (REFUTADO) | se sostuvo en «el silencio de `Link` es contrato escrito, no deriva» | Bajo el veredicto del autor, ese contrato escrito es el defecto. Reabrir en el eje de blocks. |
|
|
|
|
|
|
| **T-5** | Guard del bundle de talla | `recipe-css-contract` no lee `font-size:` escritos en `.css` (E-2) | Un guard que inspecciona el conjunto equivocado pasa en verde; endurecerlo es un eje de eidos (probar por mutación). |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Riesgos y trampas ya pagadas (memoria)
|
|
|
|
|
|
|
|
|
|
|
|
- **`queue` medido con timers falsos pasa y está mal** — sólo el navegador
|
|
|
|
|
|
real dice si el `shift` llega tras el hold y no 1,6 s tarde.
|
|
|
|
|
|
- **Partes repetidas**: registrar/anclar por instancia; nunca `runtime.trigger`
|
|
|
|
|
|
pelado desde una fila — caería en la última registrada.
|
|
|
|
|
|
- **Un hijo que se registra desde `$effect` mata el efecto raíz** (A30) — el
|
|
|
|
|
|
`hasSub` del sub ya lo hace desde `$effect`; no añadir más registros ahí.
|
|
|
|
|
|
- **`display:none` corta la firma de cierre**: si `emerge-close-sub` (pre) no
|
|
|
|
|
|
se ve porque el flyout se oculta en el mismo tick, gatear la ocultación en el
|
|
|
|
|
|
`Presence` que el shell flotante ya crea (dropdown lo hace); se decide
|
|
|
|
|
|
midiendo en F5, no antes.
|
|
|
|
|
|
- **Instrumento antes que código**: dev server sirviendo pre-HEAD por caché de
|
|
|
|
|
|
Vite; contar nodos WebAudio no es medir la salida.
|
|
|
|
|
|
- **`mergeProps`**: el consumidor que hace `preventDefault` en su `onclick`
|
|
|
|
|
|
apaga la emisión de la fila (semántica de la casa, `props.ts:20-27`) — es
|
|
|
|
|
|
la vía para «yo poseo este gesto», y se documenta en el README.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 8. Registro
|
|
|
|
|
|
|
|
|
|
|
|
- 2026-08-19 — Análisis + medición en navegador (esta sesión). Plan
|
|
|
|
|
|
entregado. **Nada construido, nada firmado.**
|
|
|
|
|
|
- 2026-08-19 — **D-SB.1 FIRMADA** («ok, seguimos»): la fila declara
|
|
|
|
|
|
`contact-activate` + `shift-navigate` (queue); la contra-lectura
|
|
|
|
|
|
`commit.select` del app-shell README queda refutada en §4.1.
|
|
|
|
|
|
- 2026-08-19 — **D-SB.2 FIRMADA**: verbo `activate` (`contact-activate`)
|
|
|
|
|
|
para fila, sub-fila y rail.
|
|
|
|
|
|
- 2026-08-19 — **D-SB.3 FIRMADA**: `emerge-open-sub` / `emerge-close-sub`
|
|
|
|
|
|
declarados sobre `menu-sub`, `SILENT` en el pack (como tooltip).
|
|
|
|
|
|
- 2026-08-19 — **D-SB.4 FIRMADA**: `group-label` un paso por debajo de la
|
|
|
|
|
|
fila (`label-font-size-{k}` = `--size-{k-1}-font-size`).
|
|
|
|
|
|
- 2026-08-19 — **D-SB.5 FIRMADA**: derivación container→part capada en `md`
|
|
|
|
|
|
vía contexto eidos; `Trigger` = `Button`, `MenuAction` = `IconButton ghost`,
|
|
|
|
|
|
`MenuBadge` = `Badge` recortado a `xs..lg`; `size` explícito gana.
|
|
|
|
|
|
- 2026-08-19 — **D-SB.6 FIRMADA**: default `md` (36px / 16px); `sm` para la
|
|
|
|
|
|
densidad actual; se mide antes/después en demo y `app-shell`.
|
|
|
|
|
|
- 2026-08-19 — **D-SB.7 FIRMADA**: en la rama actual (`alpha-0.1-background`),
|
|
|
|
|
|
ejecuta esta sesión, un commit por fase. **Las 7 firmadas → arranca F0/F1.**
|
|
|
|
|
|
- 2026-08-19 — **D-SB.1bis FIRMADA** (salió de F0): `shift-navigate` se
|
|
|
|
|
|
estampa en el **`provider`** (el shell que cruza como unidad), post, **sin
|
|
|
|
|
|
`queue`**, emitido sin anclar desde el `onclick` de la fila; `contact-activate`
|
|
|
|
|
|
queda en la fila (la mano), anclado. Precedentes: `calendar` (el sello de
|
|
|
|
|
|
shift se movió del botón a la vista que cruza — la compresión no se pintaba)
|
|
|
|
|
|
y `field-langs` (`provider`). «Reach for `regime` last» (`sema.md`). §2 del
|
|
|
|
|
|
plan queda enmendado por esta línea.
|