Review de 6 dimensiones × 3 verificadores escépticos (135 agentes): 43
hallazgos brutos → 31 confirmados, 12 rechazados. Todos los confirmados
arreglados y verificados en navegador real.
Estado (los dos ejes)
- `collapsible='none'` ya no deja el sidebar INALCANZABLE en móvil: la
inercia del modo se limita al escritorio, que es donde «nunca colapsa»
significa algo. En móvil el panel es un Drawer que arranca cerrado, el raíl
está oculto y el drawer no tiene trigger propio: con el toggle inerte no
había forma humana de abrir la navegación en un teléfono.
- `'none'` PINTA el estado a expandido: antes `data-state`, `aria-expanded` y
`useSidebar().open` podían decir «colapsado» sobre un panel plenamente
visible, y ninguna regla del recipe ni el toggle podían reconciliarlo.
- El estado del drawer móvil se DESCARTA al salir de la presentación móvil:
abrirlo en un teléfono y rotar dos veces remontaba el Drawer ya abierto,
con overlay, foco atrapado y scroll bloqueado sin tocar nada.
Submenú flotante (era el nudo con más hallazgos)
- Cierra también por PUNTERO: se abría en `pointerenter` de la fila, pero el
único cierre por puntero vivía en el propio flyout — salir hacia la página
sin cruzarlo lo dejaba pintado para siempre.
- Escape funciona desde donde el foco ESTÁ (la fila), no solo dentro del sub.
- Entrar/salir se resuelve en el `menu-item`, que contiene fila Y sub, así que
viajar entre ambos no lo cierra.
- El flag se limpia al cambiar de presentación: expandir y volver a colapsar
reabría un flyout que nadie había tocado.
- El motor flotante se engancha solo cuando el sub está ABIERTO, no por modo:
antes `autoUpdate` (rAF + observers) corría por cada fila del raíl.
- `hasSub` deja de latir a true: se limpia en el teardown del sub.
A11y
- Las filas del raíl recuperan NOMBRE: un tooltip solo describe
(`aria-describedby`), así que con la etiqueta oculta el texto de `tooltip`
pasa también a `aria-label` — incluidas las filas con submenú, que no llevan
tooltip visible.
- El panel off-canvas colapsado sale del tab order y del árbol de
accesibilidad (`visibility: hidden` con la transición retrasada para que el
deslizamiento siga animando).
- El diálogo móvil recibe el mismo nombre que el landmark.
- El raíl se llama «Contraer barra lateral» (lo que hace) en vez de
«Redimensionar», que prometía un arrastre fuera de alcance; cursor de
puntero en vez de `ew-resize`.
Recipe
- El signo del off-canvas se deriva de side × dirección: en RTL la fila flex
se invierte, así que el negativo fijo barría el panel POR ENCIMA de la
página en vez de sacarlo por su borde.
- El flyout declara banda `z-index` (nuevo token `--sidebar-sub-z`); era la
única superficie flotante del ecosistema sin ella.
- La transición de `inline-size` (layout por frame) queda SOLO bajo el modo
icono; la base anima únicamente `transform`.
- El `Drawer` es el único dueño del ancho móvil (dos dueños dejaban una banda
de cromo en dos tonos) y se compone con `dragToDismiss`.
- Las reglas de modo icono ya no alcanzan el subárbol flotante: sus filas
conservan badges y acciones.
- Bloque `prefers-reduced-motion` — la traslación del panel es la mayor del
sistema.
Demo
- Copias corregidas donde afirmaban de más: el tooltip ahora sí NOMBRA (y se
explica por qué), y los anchos son rem fijos a propósito (no los alcanza el
escalado; sí el interior).
- Tabla de eventos y de teclado en la pestaña morfo; `onOpenChange` y una
tabla de props por parte en API.
- Los chips de estado se inhabilitan en `collapsible='none'`, que es lo que
el componente hace; botón «show me» que pone icon + colapsado de una vez.
- El trigger pasa a ser solo icono: el morfo le da `aria-label`, que pisaba
cualquier texto visible.
Verificado en navegador: nombres accesibles en el raíl · flyout que cierra
por puntero, por foco y por Escape (con retorno de foco) y que no resucita al
cambiar de modo · off-canvas fuera del tab order · RTL sacando el panel por su
propio borde · z-index 80 en el flyout · drawer móvil nombrado, a ancho
completo y con el panel a ras · `collapsible='none'` abrible en un teléfono.
Gates: audit PASS 0E/0W · eidos-lint 47 morfo-backed / 0 invalid ·
svelte-check 0 errores propios · vitest src/uix/eidos 353/353 ·
contracts.test solo con los 3 fallos ajenos conocidos.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>