0Apoyar

Playground de Popover API

Crea contenido no modal en la capa superior con el atributo popover. Compara controles declarativos y JavaScript, prueba el cierre ligero y copia HTML, CSS y JS reutilizable.

Leer la guía

Preguntar a la IA

Playground de Popover API

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.
HTML
<button type="button" popovertarget="feature-popover" popovertargetaction="toggle">
  Abrir popover
</button>

<div
  class="popover-card"
  id="feature-popover"
  popover
>
  <h2>Panel de acciones</h2>
  <p>Este contenido vive en la capa superior sin crear un modal.</p>
  <button type="button" popovertarget="feature-popover" popovertargetaction="hide">
    Cerrar
  </button>
</div>
CSS
.popover-card {
  max-inline-size: min(24rem, calc(100vw - 2rem));
  padding: 1.25rem;
  border: 1px solid light-dark(#b8c3ee, #496096);
  border-radius: 1rem;
  background: light-dark(#fffdf7, #10182c);
  color: light-dark(#17203a, #f4f7ff);
  box-shadow: 0 1.25rem 3rem rgb(8 15 35 / 0.28);
  opacity: 0;
  transform: translateY(0.5rem) scale(0.98);
  transition:
    opacity 180ms ease,
    transform 180ms ease,
    display 180ms allow-discrete,
    overlay 180ms allow-discrete;
}

.popover-card:popover-open {
  opacity: 1;
  transform: translateY(0) scale(1);
}

@starting-style {
  .popover-card:popover-open {
    opacity: 0;
    transform: translateY(0.5rem) scale(0.98);
  }
}

@media (prefers-reduced-motion: reduce) {
  .popover-card {
    transition: none;
  }
}

.popover-card::backdrop {
  background: rgb(8 15 35 / 0.28);
  backdrop-filter: blur(3px);
}
JS

Guía educativa de Popover API

Contenido sobre la página, sin bloquearla

Aprende a asociar botones y paneles con HTML, elegir cómo se cierran y conservar una interacción accesible. Después ajusta el comportamiento y el código en el playground.

Volver al playground

Modelo mental

Popover es un comportamiento de presentación. No es un elemento HTML nuevo ni una semántica de menú o modal.

Del documento a la capa superiorEsquema estático: explica la relación entre botón, documento y panel. La prueba nativa está en los ejemplos siguientes.
  1. 1 · DisparadorEl botón referencia el ID del panel mediante popovertarget.
  2. 2 · DocumentoEl contenido y el botón conservan su lugar en el DOM; el resto de la página sigue disponible.
  3. 3 · Capa superiorEl panel abierto se pinta por encima del contenido normal, fuera de sus contextos de apilamiento.

La top layer la gestiona el navegador. Al abrirse, el panel sale del flujo visual normal y no queda recortado por el overflow de sus ancestros. No necesitas competir con z-index, y su lugar en el DOM no cambia. Dentro de la capa superior también importa el orden de las capas.

Un popover es no modal: no vuelve inert el resto del documento ni atrapa el foco. Puede mostrar ayuda, acciones o contenido complementario. Si el usuario debe completar o cancelar una tarea antes de seguir, elige un dialog modal.

Invocación declarativa

Un ID y dos atributos de botón bastan para abrir y cerrar el contenido sin listeners de JavaScript.

El valor de popovertarget es el ID, sin #. popovertargetaction acepta toggle, show o hide; al omitirlo se usa toggle. Usa type="button" para que el control no envíe un formulario. popover sin valor equivale a auto.

Ayuda complementaria con cierre ligero

Abre la ayuda, pulsa Tab para entrar en ella y prueba Escape, el botón de cierre o un clic fuera. En un navegador sin Popover el contenido queda inline y los botones de invocación se ocultan.
Ayuda complementaria con cierre ligero
<button id="popover-guide-auto-opener" type="button" class="popover-guide-auto-control" popovertarget="popover-guide-auto" popovertargetaction="toggle">
  Consultar consejos de lectura
</button>
<aside id="popover-guide-auto" popover="auto" aria-labelledby="popover-guide-auto-heading">
  <h5 id="popover-guide-auto-heading">Antes de copiar un ejemplo</h5>
  <p>Lee el HTML y el CSS juntos. Comprueba los textos, el tamaño de letra y el comportamiento con teclado antes de incorporar la receta a tu interfaz.</p>
  <button type="button" class="popover-guide-auto-control" popovertarget="popover-guide-auto" popovertargetaction="hide">
    Cerrar panel
  </button>
</aside>
:root {
  color-scheme: light dark;
}

.popover-guide-auto-control {
  display: none;
  min-block-size: 2.75rem;
  max-inline-size: 100%;
  padding: 0.5rem 1rem;
  border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  border-radius: 16px;
  corner-shape: squircle;
  background: light-dark(color-mix(in oklch, oklch(0.95 0.008 95) 98%, white), color-mix(in oklch, oklch(0.22 0.024 260) 86%, black));
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: inherit;
  overflow-wrap: anywhere;
  cursor: pointer;

  &:focus-visible {
    outline: 3px dashed light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }
}

#popover-guide-auto {
  box-sizing: border-box;
  max-inline-size: 100%;
  padding: min(1rem, 4vi);
  border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  border-radius: 16px;
  corner-shape: squircle;
  background: light-dark(oklch(0.98 0.005 95), oklch(0.16 0.02 260));
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font-size: 1rem;
  line-height: 1.65;
  overflow-wrap: anywhere;

  > h5 {
    margin: 0;
    font-size: 1.25rem;
  }

  > p {
    margin-block: 1rem;
  }

  &::backdrop {
    background: transparent;
  }

  &:popover-open {
    box-shadow: light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260)) 0px 1px 4px;
  }
}

@supports selector(:popover-open) {
  .popover-guide-auto-control {
    display: inline-block;
  }

  #popover-guide-auto {
    position: fixed;
    inset: 0;
    margin: auto;
    inline-size: min(28rem, calc(100vi - 2rem));
    max-block-size: 70dvb;
    overflow: auto;
  }
}

La receta aplica el posicionamiento y muestra los botones dentro de @supports selector(:popover-open). Cuando el navegador ignora el atributo, el aside permanece legible en el flujo normal. El fallback es contenido inline, sin simular cierre ligero ni capa superior. La apertura y el cierre son nativos; solo la referencia manual añade JavaScript para restaurar el foco.

commandfor y command también pueden invocar popovers mediante toggle-popover, show-popover y hide-popover. Son otra API con su propio soporte. La receta usa popovertarget para enseñar la asociación específica de Popover sin depender de esos comandos.

Modos y reglas de cierre

Elige el modo por la duración del contenido y la salida que debe ofrecer, no por su apariencia.

popover="auto"

Cierre ligero y solicitudes de cierre, como Escape. Abrir otro auto independiente suele cerrar el anterior; los popovers anidados pueden permanecer abiertos juntos. Úsalo para un panel que debe desaparecer cuando el usuario continúa fuera.

popover="hint"

Ayuda subordinada a auto. Mostrar un hint no cierra el auto abierto; reemplaza otros hint independientes y admite cierre ligero y solicitudes de cierre. Su disponibilidad se comprueba por separado.

popover="manual"

Cierre explícito mediante botón o API. Un clic fuera y Escape no lo cierran de forma nativa. Pueden coexistir varios manual; cada uno necesita una salida clara y no debe cubrir contenido esencial.

hint no abre por hover automáticamente. El modo define las reglas de convivencia y cierre; la invocación se programa o utiliza otra API compatible. Además, un clic en un disparador situado fuera de un auto abierto puede cerrarlo por cierre ligero, aunque el destino sea hint. No conviertas información necesaria en ayuda disponible solo al pasar el puntero.

Referencia que permanece abierta

Abre la referencia y prueba Escape o un clic fuera: permanece abierta. El cierre explícito recibe foco con autofocus. Al cerrarlo, el JavaScript local devuelve el foco al disparador; Tab puede continuar por el resto de la página.
Referencia que permanece abierta
<button id="popover-guide-manual-opener" type="button" class="popover-guide-manual-control" popovertarget="popover-guide-manual" popovertargetaction="show">
  Abrir referencia persistente
</button>
<aside id="popover-guide-manual" popover="manual" aria-labelledby="popover-guide-manual-heading">
  <h5 id="popover-guide-manual-heading">Lista de revisión</h5>
  <p>Comprueba labels, foco visible, salida explícita y reflow. Esta referencia permanece abierta hasta que uses el botón de cierre; no impide seguir usando la página.</p>
  <button type="button" class="popover-guide-manual-control" popovertarget="popover-guide-manual" popovertargetaction="hide" autofocus>
    Cerrar panel
  </button>
</aside>
:root {
  color-scheme: light dark;
}

.popover-guide-manual-control {
  display: none;
  min-block-size: 2.75rem;
  max-inline-size: 100%;
  padding: 0.5rem 1rem;
  border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  border-radius: 16px;
  corner-shape: squircle;
  background: light-dark(color-mix(in oklch, oklch(0.95 0.008 95) 98%, white), color-mix(in oklch, oklch(0.22 0.024 260) 86%, black));
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font: inherit;
  overflow-wrap: anywhere;
  cursor: pointer;

  &:focus-visible {
    outline: 3px dashed light-dark(oklch(0.46 0.14 261), oklch(0.82 0.18 261));
    outline-offset: 3px;
  }
}

#popover-guide-manual {
  box-sizing: border-box;
  max-inline-size: 100%;
  padding: min(1rem, 4vi);
  border: 1px solid light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260));
  border-radius: 16px;
  corner-shape: squircle;
  background: light-dark(oklch(0.98 0.005 95), oklch(0.16 0.02 260));
  color: light-dark(oklch(0.22 0.01 95), oklch(0.95 0.01 95));
  font-size: 1rem;
  line-height: 1.65;
  overflow-wrap: anywhere;

  > h5 {
    margin: 0;
    font-size: 1.25rem;
  }

  > p {
    margin-block: 1rem;
  }

  &::backdrop {
    background: transparent;
  }

  &:popover-open {
    box-shadow: light-dark(oklch(0.84 0.01 95), oklch(0.36 0.024 260)) 0px 1px 4px;
  }
}

@supports selector(:popover-open) {
  .popover-guide-manual-control {
    display: inline-block;
  }

  #popover-guide-manual {
    position: fixed;
    inset: 0;
    margin: auto;
    inline-size: min(28rem, calc(100vi - 2rem));
    max-block-size: 70dvb;
    overflow: auto;
  }
}
(() => {
  const panel = document.getElementById('popover-guide-manual');
  const opener = document.getElementById('popover-guide-manual-opener');

  if (!panel || !opener || panel.dataset.focusBound) return;
  panel.dataset.focusBound = 'true';

  panel.addEventListener('toggle', (event) => {
    if (event.newState === 'closed') opener.focus();
  });
})();

showPopover(), hidePopover() y togglePopover() permiten coordinar el panel con lógica de aplicación. beforetoggle precede al cambio y toggle lo informa mediante oldState/newState. Los eventos toggle pueden agruparse durante cambios rápidos; no los trates como un registro de cada llamada. El método no aporta por sí solo la relación ARIA implícita del botón declarativo.

Referencia de la API JavaScript

Fragmento para el panel auto anterior: define operaciones para llamarlas desde tu lógica y observa el estado final. No se ejecuta en esta guía ni añade listeners a los botones nativos.

Referencia de la API JavaScript
JavaScript
const panel = document.getElementById('popover-guide-auto');

if (panel && 'popover' in HTMLElement.prototype) {
  const help = {
    show: () => panel.showPopover(),
    hide: () => panel.hidePopover(),
    toggle: () => panel.togglePopover(),
  };

  panel.addEventListener('toggle', (event) => {
    panel.dataset.state = event.newState;
  });

}

Foco y elección del patrón

La plataforma resuelve parte de la interacción; la estructura y el propósito del contenido siguen siendo responsabilidad de la interfaz.

Con popovertarget el navegador establece una relación implícita de aria-expanded/aria-details y coloca el contenido abierto a continuación del disparador en el orden de Tab. No es una trampa de foco. En auto, cerrar con Escape desde el interior permite regresar al disparador; un clic fuera puede llevar el foco al destino pulsado. manual no garantiza esa restauración: el ejemplo añade un listener local de toggle para devolverlo al botón al cerrar.

Sin un destino autofocus, abrir un popover no implica mover el foco al primer control. La ayuda auto deja el foco en el disparador; la referencia manual lo lleva deliberadamente al cierre. Elige un foco inicial útil y prueba la lectura del contenido y el regreso, evitando saltos innecesarios.

popover no convierte un div en menú ni en tooltip. Aquí usamos un aside con título asociado para contenido complementario y botones con nombre visible. Un menú con role="menu" necesita el patrón completo de teclado y roles; un tooltip necesita su propia relación descriptiva y no debe contener controles interactivos.

Popover

Contenido temporal superpuesto que permite seguir usando la página: ayuda complementaria, opciones o acciones. El modo define cuándo desaparece.

dialog

Un dialog representa una ventana de diálogo. showModal() abre en capa superior y vuelve inert el resto del documento; show() abre uno no modal. Elige la variante que corresponda a la tarea.

details

Contenido que se revela en el flujo con un summary nativo: explicación adicional o preguntas frecuentes. El contenido abierto participa en el layout, sin necesitar una capa superpuesta.

Estilos y posición

Primero conserva apertura, lectura y cierre. Después ajusta la presentación sin cambiar el significado del patrón.

El navegador oculta el popover cerrado con display:none. No fuerces display:block o grid en el estado cerrado. Usa :popover-open para estilos del estado abierto; si necesitas un layout interior, aplica grid o flex a un wrapper dentro del panel.

La posición predeterminada suele estar centrada: popover no coloca por sí solo el panel junto al botón. La receta conserva un centro limitado al viewport, altura máxima y scroll interno. Para acercarlo a su disparador, evalúa CSS anchor positioning y sus alternativas de borde con soporte independiente; conserva una posición usable como fallback.

::backdrop representa la capa decorativa inmediatamente detrás del popover. La receta la deja transparente. Oscurecerla no hace modal el panel; puede sugerir bloqueo donde no existe. Comprueba contraste, controles exteriores y lectura del contenido que siga siendo relevante.

Los ejemplos no añaden animación. Si animas apertura y salida, revisa @starting-style, display/overlay y transition-behavior:allow-discrete como piezas independientes. Mantén una alternativa con prefers-reduced-motion y no retrases el acceso al cierre por un efecto visual.

Errores frecuentes

Relaciona el síntoma con el contrato que falla antes de añadir JavaScript.

El botón no abre el panel

Resultado esperado
El botón asociado alterna o muestra su popover.
Causa
El destino lleva #, no coincide con el ID, está duplicado o carece del atributo popover.
Solución
Usa un ID único y popovertarget con ese valor exacto, sin #. Dentro de formularios indica type="button" y comprueba soporte.

El panel aparece aunque está cerrado

Resultado esperado
El contenido solo se superpone cuando el estado nativo está abierto.
Causa
Una regla de autor sobrescribe display:none, o se interpreta la presencia del atributo como estado abierto.
Solución
Conserva el display del navegador y aplica el layout a un wrapper. Comprueba el estado mediante :popover-open, no por [popover] ni un atributo open.

Escape no cierra la referencia

Resultado esperado
El modo elegido ofrece la salida que necesita el usuario.
Causa
manual no admite las solicitudes de cierre ni el cierre ligero de auto. Un hint no reconocido también se interpreta como manual en un navegador con Popover.
Solución
Ofrece un botón hide accesible. Usa auto cuando quieras cierre ligero y comprueba la compatibilidad de hint antes de confiar en sus reglas.

Un menú visual no funciona con teclado

Resultado esperado
La interacción y el nombre accesible corresponden al contenido.
Causa
Se añadió role="menu" suponiendo que popover implementaba flechas, items y gestión de foco.
Solución
Conserva enlaces o botones nativos para una lista sencilla de acciones, o implementa el patrón completo de menú cuando lo requiera el producto.

El panel cubre el cierre o queda lejos del botón

Resultado esperado
El contenido y su salida siguen visibles en una pantalla estrecha.
Causa
Se confunde top layer con posicionamiento automático o se fija una altura que no admite texto ampliado.
Solución
Limita el tamaño al viewport, permite scroll interior y comprueba el cierre a 320 px y con texto al 200%. Trata anchor positioning como una mejora separada.

Soporte y compatibilidad

Comprueba el núcleo y las extensiones que usa tu receta; aceptar un atributo no valida toda la interacción.

Web Platform DX clasifica Popover como Newly Available desde el 27 de enero de 2025. La fecha del conjunto no es la fecha de cada método o selector; el lab y los ejemplos no certifican todos los motores ni tecnologías de asistencia.

popover="hint" figura como Limited availability en la consulta del 8 de octubre de 2026. Cuando existe Popover pero hint no se reconoce, el valor inválido cae en manual: perderás cierre ligero y convivencia de hint. Conserva una salida explícita o elige auto si esas reglas son necesarias.

Para detectar el núcleo comprueba 'popover' in HTMLElement.prototype. Para hint, crea un elemento de prueba, asigna su propiedad popover a 'hint' y comprueba que devuelve 'hint', no 'manual'. La detección no sustituye pruebas de comportamiento; command/commandfor, source, interest invokers, anclaje y transiciones tienen soporte propio.

Si la superposición es solo una comodidad, conserva el contenido accesible en el flujo, como aquí, o usa details cuando encaje con el propósito. Si es esencial, decide entre un fallback probado y una implementación compatible. Un panel fixed artesanal no reproduce por sí solo la top layer ni sus reglas de foco y cierre.