Eidos Dialog
Dialog si existe en la rama Air anterior (glm-5:src/uix/air/components/dialog).
Air envolvia Terra y exponia Root, Trigger, Portal, Overlay, Content,
Header, Footer, Title, Description y Close. Eidos conserva la anatomia,
pero cambia la superficie publica a Option C: <Dialog> root con partes
attached, sin Root ni Provider publico.
Baseline
- air: tenía
Dialog.Root + parts compound. Cubría modal/non-modal,
focus trap/restore, escape/outside policies, scroll lock, force mount,
nested dialogs. Tamaños sm/md/lg/full.
- soma actual: cubre todo lo de Air en behavior (focus, dismissal,
scroll lock, modal/non-modal, alertdialog variant) más outcome-specific
close events (
close-save / close-cancel / close-dismiss /
close-dismiss-outside / close-after-fail) que ningún competidor
expone.
- morfo: declara 7 events (open + 5 close variants + open intent
fromProp), focus policy con trap/return/restore, parts Provider /
Trigger / Content / Overlay / Title / Description / Close. Eidos
añade Header / Footer visuales y dimensiones / position.
Superficie
<Dialog bind:open modal>
<Dialog.Trigger>Open</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay overlayOpacity={0.52} />
<Dialog.Content
size="lg"
position="middle-center"
maxWidth="min(90vw, 720px)"
maxHeight="calc(100dvh - 4rem)"
>
<Dialog.Header>
<Dialog.Title>Title</Dialog.Title>
<Dialog.Description>Description</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Dialog.Close>Close</Dialog.Close>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Portal>
</Dialog>
Partes publicas: Trigger, Portal, Overlay, Content, Title,
Description, Close, Header, Footer.
Comparativa
| Capacidad |
Air |
Eidos/Soma |
Radix |
Bits UI |
Ark UI |
shadcn-svelte |
| Compound API |
Si: Root.* |
Si: <Dialog.*> |
Si |
Si |
Si |
Si |
| Portal |
Si |
Si |
Si |
Si |
Si |
Si |
| Overlay/backdrop |
Si |
Si |
Si |
Si |
Backdrop |
Si |
| Header/Footer visuales |
Si |
Si, Eidos-only |
Ejemplo |
Manual |
Ejemplo |
Si |
| Title/Description a11y |
Si |
Si |
Si |
Si |
Si |
Si |
| Modal / non-modal |
Terra |
Si, Soma |
Si |
Si |
Si |
Primitive |
| Non-modal sin trap/scroll lock |
Terra |
Si, cuando modal=false |
Si |
Si |
Si |
Primitive |
| Alert dialog role |
Terra |
Si: variant="alertdialog" |
Separado |
Props |
Si |
Separado |
| Focus trap / restore |
Terra |
Si, Soma |
Si |
Si |
Si |
Primitive |
| Initial/final focus hooks |
Terra |
Si |
Si |
Si |
Si |
Primitive |
| Escape/outside policies |
Terra |
Si: behavior props |
Callbacks |
Props/callbacks |
Si |
Primitive |
| Scroll lock |
Terra |
Si, Soma |
Si |
Si |
Si |
Primitive |
| Force mount |
Air motion |
Si |
Si |
Si |
lazy/unmount opts |
Primitive |
| Nested dialogs |
Terra |
Si: depth attrs/vars |
Basico |
Si |
Si |
Primitive |
| Positioner part |
No |
No, Content.position |
No |
No |
Si |
No |
| Responsive size |
Si: sm/md/lg/full |
Si: sm/md/lg/xl/full |
CSS/Theme |
CSS |
CSS |
Tailwind |
| Width/height overrides |
No |
Si, visual-only vars |
Theme props/CSS |
CSS |
CSS |
Tailwind |
| Sheet on small viewport |
No |
Si, auto data-sheet |
Custom |
Custom |
Custom |
Manual |
| Outcome-specific close events |
No |
Si: save/cancel/dismiss/fail |
No |
No |
No |
No |
Decisiones
- Air era
Root + partes. Eidos usa <Dialog> como root unico para alinear
toda la capa visual con Option C.
- No se crea
Dialog.Positioner. Ark separa Positioner, pero en UIX la
posicion es una responsabilidad visual de Content.position. Crear otra part
duplicaria una decision que no tiene ARIA ni estado propio.
Header y Footer siguen siendo Eidos-only. No tienen ARIA obligatoria ni
comportamiento; si algun dia coordinan foco, titulo o acciones, subiran a
Morfo/Soma.
size, position, width, height, min/max-* y overlayOpacity son
props visuales. Se serializan como data-* o variables privadas de recipe;
Soma no los conoce.
modal=false debe comportarse como en Radix/Bits/Ark: fuera de modal, el
contenido no atrapa foco ni bloquea scroll salvo override explicito
(trapFocus={true} / preventScroll={true}).
variant="alertdialog" vive en Soma porque cambia comportamiento: role,
escape/outside defaults y expectativa de accion explicita.
- Los eventos de cierre (
close-save, close-cancel, close-dismiss,
close-dismiss-outside, close-after-fail) son ventaja propia de UIX:
permiten que Sema/Eidos distingan causa perceptiva sin inventar props visuales.
Diferencias con Air
| Area |
Air |
Eidos |
| Root publico |
Dialog.Root |
<Dialog> |
| Behaviour base |
Terra |
Soma + Morfo + Sema |
| Motion |
contextIn/contextOut, fade |
data-event-*, presence attrs y recipe |
| Size |
sm/md/lg/full |
sm/md/lg/xl/full |
| Position |
No |
3x3 Position en Content |
| Dimensions |
No |
width/height/min/max overrides |
| Small viewport |
Full/dialog por CSS Air |
auto sheet con data-sheet |
| Exit cause |
No |
data-last-action y eventos causales |
Gaps
| Gap |
Disposición |
Detalle |
Dialog.Positioner como part público |
descartar |
Ark UI lo expone pero la posición en UIX es responsabilidad visual de Content.position (3x3 grid de posiciones). Exponerlo como part DOM duplicaría responsabilidad sin ARIA propia. |
Animaciones SSR-safe con forceMount por part |
diferir |
Soma soporta forceMount global; falta el granular per-part. Sin caso de uso urgente. |
Confirm / dismiss callbacks tipo Radix onCloseAutoFocus |
implementar |
Soma ya soporta los callbacks; falta documentación en README de Eidos y wireup en el demo. |
| Drag-to-dismiss en sheet móvil |
diferir |
Patrón móvil-only. Bibliotecas como vaul lo implementan. Sin demanda actual; cuando llegue, vive en una capa de interaction, no en Dialog. |
| Stacking visual de dialogs anidados |
implementar |
Soma ya emite data-depth y data-nested/data-nested-open en el morfo. Falta CSS canónica para inset/offset por depth en la recipe. |
| Bottom sheet drag handle |
diferir |
Composición de Drawer + indicator. Si llega un caso, Drawer es el dueño. |
Dialog.AlertDialog como variante separada |
descartar |
Vivo como alert-dialog (componente aparte) por diferencias semánticas (intent, regime, role). No se duplica aquí. |
Referencias