Documentación
Overlays
0.2.0 añade una capa completa de overlays: diálogos, drawers, menús, popovers,
menús contextuales y tooltips. Todos viven tras @yoltra/ds/client (necesitan
APIs del navegador y se portan a document.body) y cada uno tiene su propia hoja
de estilo.
#Por qué se portan
Dialog, Drawer y las superficies ancladas se renderizan a través de Portal
en un nodo bajo document.body, no donde se escriben. Renderizar en su sitio
pierde contra el CSS de tres formas, y ningún z-index arregla ninguna: un
ancestro con overflow: hidden recorta el panel; un ancestro con
transform/filter se vuelve el bloque contenedor de position: fixed; y un
ancestro que estableció un contexto de apilamiento atrapa el overlay por debajo de
lo que haya encima de ese ancestro.
Portarlo al body deja el overlay compitiendo solo con el orden de apilamiento del
propio documento —los tokens --yl-z-*—. Su orden codifica la contención: un
popover abierto dentro de un diálogo va encima de él, y un tooltip encima de ambos.
#La capa modal — Dialog, Drawer
Ambos son controlados: open y onClose son todo el contrato de estado, así
la superficie nunca discrepa con tu app sobre si está visible. Traen el
comportamiento que hace un overlay usable y no solo visible —foco atrapado dentro
y restaurado al cerrar, scroll de la página bloqueado (con recuento de
referencias) y cierre por Escape / clic fuera que se apila (un Escape cierra el
menú, no el diálogo de detrás)—.
title es obligatorio: un modal sin nombre accesible se anuncia como "diálogo"
y nada más. Envuélvelo en VisuallyHidden si el diseño no lleva encabezado
visible.
import { Dialog } from "@yoltra/ds/client";
import "@yoltra/ds/styles/modal.css";
<Dialog open={open} onClose={close} title="Buscar" size="lg">
<input type="search" placeholder="Busca en la documentación…" />
</Dialog>;Drawer es la misma maquinaria anclada a un borde —side="left" | "right" | "top" | "bottom" y un size (cualquier longitud CSS)—. Úsalo cuando el contenido es una
lista o un formulario largo para el que una caja centrada necesitaría igualmente
su propia barra de scroll.
#La capa anclada — Menu, Popover, ContextMenu, Tooltip
Se posicionan respecto a un disparador (o, en ContextMenu, a un punto). No son
modales: sin foco atrapado ni bloqueo de scroll; se cierran con Escape, con un
clic fuera y cuando el foco sale. La render prop trigger te entrega el cableado
ARIA (aria-expanded, aria-haspopup, aria-controls) y la ref contra la que
anclar —espárcelo; el estado de apertura sigue siendo tuyo—.
import { Menu, MenuItem } from "@yoltra/ds/client";
import "@yoltra/ds/styles/popover.css";
<Menu
open={open}
onClose={close}
label="Idioma"
trigger={(props) => (
<Button {...props} onClick={() => setOpen((v) => !v)}>ES</Button>
)}
>
<MenuItem onSelect={() => setLang("en")}>English</MenuItem>
<MenuItem onSelect={() => setLang("es")}>Español</MenuItem>
</Menu>;Menu implementa el patrón de teclado completo (flechas con envolvente, Home/End,
Enter/Espacio, Tab para cerrar). Popover es la misma cáscara para controles
arbitrarios en vez de una lista de comandos. Tooltip es la excepción:
no controlado, porque su visibilidad pertenece al puntero y al anillo de foco,
y cableado con aria-describedby (complementa el nombre de un control, no lo
sustituye):
import { Tooltip } from "@yoltra/ds/client";
import "@yoltra/ds/styles/tooltip.css";
<Tooltip content="Cambiar tema">
{(props) => <IconButton {...props} label="Cambiar tema">🌙</IconButton>}
</Tooltip>;Cada overlay necesita su hoja de estilo —modal.css para Dialog/Drawer,
popover.css para Menu/Popover/ContextMenu, tooltip.css para Tooltip—
además de los siempre obligatorios tokens.css y base.css. Consulta la
referencia de la API para cada prop.