Skip to content

Modal

Componente de diálogo para interacciones concentradas sobre la página actual, con captura del foco, bloqueo del desplazamiento e inertización del fondo.

Úsalo cuando necesites un diálogo para una interacción concentrada sobre la página, con captura del foco, bloqueo del desplazamiento e inertización del fondo. Gobierna <r-modal> con el atributo open o con los ayudantes imperativos Modal.confirm / Modal.info.

Inicio rápido

Uso básico

La visibilidad del modal se controla con el atributo open (o la propiedad open). Empieza cerrado y no dibuja nada hasta que se abre, así que conecta un disparador que lo alterne.

Abrir modal

Este es el contenido del modal.

Aceptar
html
<r-button onclick="modal.open = true">Abrir modal</r-button>

<r-modal id="modal" heading="Modal básico">
  <p>Este es el contenido del modal.</p>
  <div slot="footer">
    <r-button type="primary" onclick="modal.open = false">Aceptar</r-button>
  </div>
</r-modal>

Referencia de la API

Propiedades

PropiedadTipoPor defectoDescripción
openbooleanfalseSi el modal está visible
headingstring''Texto del título de la cabecera (si está vacío, recurre a Modal)
closablebooleantrueSi se muestra el botón de cerrar (x)
maskClosablebooleantrueSi al hacer clic en la máscara de fondo se cierra el modal
closeOnEscbooleantrueSi pulsar Escape cierra el modal
lockScrollbooleantrueSi el desplazamiento del body se bloquea mientras el modal está abierto
autoFocusbooleantrueSi al abrir se enfoca el primer elemento enfocable
hideHeaderbooleanfalseSuprime por completo la barra de título y deja un botón de cerrar flotante
sheetstring''CSS inyectado en el shadow DOM

closing es un atributo de solo lectura que el elemento refleja sobre sí mismo (no una propiedad asignable): está presente desde el instante en que se ejecuta close() hasta que la transición de desvanecido y reducción de la máscara y el diálogo termina de verdad (~0,3 s después, la misma marca que el evento afterclose). Es útil para una página anfitriona que necesita que el modal siga contando como «presente» durante esa cola visual; véanse las buenas prácticas más abajo.

Título title

html
<r-modal open heading="Eliminar elemento">
  <p>¿Seguro que quieres eliminar este elemento?</p>
</r-modal>

Botón de cerrar closable

Oculta el botón de cerrar de la cabecera, de modo que el modal solo pueda descartarse desde tus propios controles.

html
<r-modal open heading="Términos" closable="false">
  <p>Debes aceptar los términos para continuar.</p>
  <div slot="footer">
    <r-button type="primary">Aceptar</r-button>
  </div>
</r-modal>

Cerrar con la máscara maskClosable

Por defecto, hacer clic en el fondo cierra el modal. Ponlo a false para exigir una acción explícita.

html
<r-modal open heading="Cambios sin guardar" maskClosable="false">
  <p>Hacer clic fuera no descartará este diálogo.</p>
</r-modal>

Cerrar con Escape closeOnEsc

html
<r-modal open heading="Informe" closeOnEsc="false">
  <p>La tecla Escape está desactivada para este diálogo.</p>
</r-modal>

Bloquear el desplazamiento lockScroll

html
<r-modal open heading="Vista previa" lockScroll="false">
  <p>La página detrás del modal todavía puede desplazarse.</p>
</r-modal>

Foco automático autoFocus

html
<r-modal open heading="Buscar" autoFocus="false">
  <input type="text" placeholder="Escribe para buscar" />
</r-modal>

Modo sin cabecera hideHeader

Suprime por completo la barra de título y su borde, y deja solo un botón de cerrar flotante (arriba a la derecha) cuando hay closable. Va bien para diálogos que son solo contenido, como una caja de luz con una imagen o un diagrama, donde una barra de título no haría más que comerse el contenido. El diálogo conserva un nombre accesible mediante aria-label (derivado de title) aunque el <h3> visible haya desaparecido, así que pon title para dar una etiqueta al lector de pantalla incluso en este modo.

html
<r-modal open hide-header>
  <img src="/diagram.png" alt="Diagrama de arquitectura" style="display: block; max-width: 100%;" />
</r-modal>

Slots

SlotDescripción
(por defecto)Contenido del cuerpo del modal
footerAcciones del pie; la barra solo aparece cuando este slot tiene algo
html
<r-modal open heading="Confirmar">
  <p>El contenido del cuerpo va en el slot por defecto.</p>
  <div slot="footer">
    <r-button onclick="modal.open = false">Cancelar</r-button>
    <r-button type="primary">Confirmar</r-button>
  </div>
</r-modal>

Eventos

Todos los eventos relacionados con el cierre llevan un trigger en event.detail que describe qué lo provocó: 'mask', 'button', 'escape' o 'program'.

EventoCancelabledetailDescripción
beforeopenAntes de abrir; llama a preventDefault() para cancelar
openNoSe dispara cuando el modal se abre
afteropenNoSe dispara al terminar la transición de apertura
beforeclose{ trigger }Antes de cerrar; llama a preventDefault() para cancelar
closeNo{ trigger }Se dispara cuando el modal se cierra
aftercloseNo{ trigger }Se dispara al terminar la transición de cierre
html
<r-modal id="modal" heading="Ejemplo"></r-modal>

<script>
  const modal = document.getElementById('modal');

  modal.addEventListener('beforeclose', (e) => {
    if (!confirm('¿Descartar los cambios?')) e.preventDefault();
  });

  modal.addEventListener('close', (e) => {
    console.log('cerrado por', e.detail.trigger); // 'mask' | 'button' | 'escape' | 'program'
  });
</script>

API programática

La clase Modal expone ayudantes estáticos que crean, montan y resuelven un modal sin marcado. Cada uno devuelve una Promise<{ action, trigger }> donde action es 'confirm', 'cancel' o 'dismiss'.

MétodoDescripción
Modal.open(opts)Abre un modal con un único botón de aceptar
Modal.confirm(opts)Abre un modal con botones de aceptar y cancelar
Modal.info(opts)Modal informativo (el título recurre a Info)
Modal.success(opts)Modal de éxito (el título recurre a Success)
Modal.warning(opts)Modal de advertencia (el título recurre a Warning)
Modal.error(opts)Modal de error (el título recurre a Error)

Opciones (todas opcionales): title, content, okText, cancelText, showCancel, maskClosable, closeOnEsc, lockScroll, autoFocus, closable, onConfirm, onCancel. onConfirm / onCancel pueden devolver false (o una promesa que resuelva a false) para dejar el modal abierto.

js
import { Modal } from 'ranui/modal';

const result = await Modal.confirm({
  title: 'Eliminar proyecto',
  content: 'Esta acción no se puede deshacer.',
  okText: 'Eliminar',
  cancelText: 'Conservar',
  onConfirm: async () => {
    await deleteProject();
  },
});

if (result.action === 'confirm') {
  // eliminado
}

Parts CSS

Da estilo a las piezas internas con ::part().

PartDescripción
rootContenedor exterior de la capa
maskFondo tras el diálogo
dialogLa caja del diálogo
headerBarra de cabecera
titleEncabezado del título
closeBotón de cerrar (x)
bodyRegión del cuerpo, desplazable
footerBarra de acciones del pie
css
r-modal::part(dialog) {
  border-radius: 8px;
}
r-modal::part(mask) {
  background: rgba(0, 0, 0, 0.6);
}

Estilos

<r-modal> expone 23 propiedades personalizadas de CSS propias, además de los tokens semánticos que lee del tema. Define una allí donde se herede: :root, un contenedor o el propio elemento:

css
r-modal {
  --ran-modal-mask-background: var(--ran-color-bg-subtle);
}

Partes: body · close · dialog · footer · header · mask · root · title

La lista completa está en tokens de estilo; cuál usar lo explica el sistema de diseño.

Buenas prácticas

  • Disparador y alternancia: abre con modal.open = true y cierra con modal.open = false, o llama a close().
  • Protege los cierres destructivos: escucha beforeclose y llama a preventDefault() para confirmar antes de descartar trabajo sin guardar.
  • Acciones del pie: pon los botones primario y secundario en slot="footer"; la barra del pie solo aparece cuando el slot tiene contenido.
  • Flujos que no se descartan: pon closable="false" y maskClosable="false" para forzar una elección explícita.
  • Diálogos puntuales: usa Modal.confirm / Modal.info para preguntas rápidas en lugar de escribir marcado.
  • Elevar una página anfitriona por encima del modal mientras está abierto: haz coincidir :has(r-modal[open]), :has(r-modal[closing]), no solo [open]. open desaparece en el instante en que se ejecuta close(), pero la transición de la máscara y el diálogo sigue pintando unos 0,3 s más; retirar la elevación de z-index a mitad del desvanecido deja que la máscara, todavía visible, se repinte por debajo de aquello sobre lo que estaba levantada.

Publicado bajo la licencia MIT.