Mostrar información sobre la herramienta cuando se coloca el cursor sobre ella

Los usuarios esperan ver información relacionada adicional sin cambiar por completo su contexto. Mostrar un cuadro de información cuando un usuario se interesa en obtener más información puede ser útil para proporcionar definiciones de un término, aclarar la acción que realizará un botón que solo tiene un ícono o proporcionar orientación adicional sobre los campos de un formulario.

Crea la información sobre la herramienta

Puedes crear un cuadro emergente con el comportamiento requerido agregando el atributo popover="hint" a un elemento <div> o a otro elemento semánticamente apropiado. Cuando el usuario abre la información sobre la herramienta, se ocultan otras informaciones sobre la herramienta de popover="hint", pero no se ocultan las de auto ni las de manual. También controla el descarte de información sobre la herramienta anidada.

También proporciona un comportamiento de descarte ligero, por lo que, cuando un usuario hace clic o enfoca fuera del cuadro emergente, este se descarta.

El elemento de información sobre la herramienta debe tener un atributo id con un valor único:

<!-- MANDATORY: The tooltip container `<div>` must have a `popover` attribute.
     the value of `"hint"` ensures it can be "light dismissed". -->
<div popover="hint" id="tooltip">Tooltip content</div>

Un usuario expresa interés en la información adicional colocando el cursor o enfocándose en un elemento <a> o <button>. El elemento debe tener un atributo interestfor que coincida con el atributo id de la información sobre la herramienta.

<!-- The `interestfor` attribute can be applied to a `<button>` element: -->
<button interestfor="tooltip">Tooltip trigger</button>

<!-- The `interestfor` attribute can also be applied to an `<a>` element: -->
<a interestfor="tooltip" href="">Tooltip trigger</a>

El activador debe tener un indicador visual que muestre que hay información adicional disponible cuando se interactúa con él.

Accesibilidad integrada en interestfor

Cuando usas interestfor, el navegador controla el cableado de la tecnología de accesibilidad por ti:

  • Semántica implícita: Un destino con popover="hint" obtiene un rol mínimo implícito de tooltip. NO configures role="tooltip" por tu cuenta.
  • Asociación implícita: El navegador asocia de forma implícita el elemento de origen con el destino a través de aria-describedby cuando el destino es texto sin formato o a través de aria-details cuando el destino contiene contenido interactivo. NO agregues aria-describedby ni aria-details al activador.
  • Contenido interactivo: Debido a que la asociación cambia a aria-details cuando es necesario, el destino SÍ puede contener contenido interactivo (p.ej., un vínculo dentro de una "tarjeta de interés").

Restricciones de accesibilidad (WCAG 1.4.13)

Incluso con interestfor controlando la semántica anterior, tu implementación DEBE cumplir con la WCAG 1.4.13 (Contenido al colocar el cursor sobre un elemento o al enfocarlo):

  • Descartable: Los usuarios deben poder descartar la información sobre la herramienta sin mover el puntero ni el enfoque del teclado (p.ej., presionando la tecla Escape). El atributo nativo popover administra esta vinculación automáticamente.
  • Se puede colocar el cursor sobre el elemento: El puntero debe poder moverse sobre el contenido del cuadro de información sin que este desaparezca. Esto permite que los usuarios con herramientas de ampliación lean el texto de la información sobre la herramienta de forma segura.
  • Persistente: La información sobre la herramienta debe permanecer visible hasta que se quite el activador de desplazamiento o enfoque, el usuario la descarte de forma explícita o su contenido ya no sea válido.

Posiciona el cuadro de información

La información sobre la herramienta se posiciona con el posicionamiento de anclaje de CSS. Cuando se abre la información sobre la herramienta con interestfor, el elemento activador se convierte automáticamente en un anclaje implícito para el cuadro emergente.

En los navegadores actualizados que admiten el posicionamiento de anclaje nativo, no necesitas definir un anchor-name en el activador ni un position-anchor en la información sobre la herramienta. Puedes posicionar la información sobre la herramienta directamente en relación con su ancla implícita usando position-area y controlar el cambio de posición por colisión con position-try-fallbacks:

[popover] {
  /* Implicit anchor from interestfor: no anchor-name or position-anchor needed */
  position-area: top;
  position-try-fallbacks: flip-block;
  margin: unset;
}

Estrategias de resguardo

Resguardos y compatibilidad con navegadores para los invocadores de interés

Los activadores de interés deben tener un polyfill condicional con el paquete de polyfill interestfor de NPM. Se prefiere agrupar el polyfill en lugar de usar la CDN.

OBLIGATORIO: Detecta la función verificando la propiedad interestForElement en HTMLButtonElement.prototype y carga el polyfill solo cuando no haya compatibilidad nativa.

<script type="module">
  if (!HTMLButtonElement.prototype.hasOwnProperty("interestForElement")) {
    // CDN link only used for example, prefer bundling.
    await import("https://unpkg.com/interestfor@latest");
  }
</script>

Advertencias sobre el polyfill de CSS

Cuando se usa el polyfill, la API de CSS cambia un poco para compensar la falta de compatibilidad con las pseudoclases y propiedades nativas:

  • Pseudoclases: El polyfill no puede definir pseudoclases :interest-source y :interest-target reales, por lo que aplica las clases .interest-source y .interest-target en su lugar. OBLIGATORIO: Combínalas con :is() para admitir los estados nativos y con polyfill.
/* Styles to apply when the effect is being previewed */
:is(:interest-source, .interest-source) { /* ... */ }
:is(:interest-target, .interest-target) { /* ... */ }
  • Propiedades CSS: Para interest-delay, interest-delay-start y interest-delay-end, el polyfill requiere el uso de variables CSS, ya que no puede leer las propiedades personalizadas directamente en todos los navegadores.
/* Adjust the start and end delay for interest invokers */
[interestfor] {
  --interest-delay-start: 0.2s;
  interest-delay-start: var(--interest-delay-start);
  --interest-delay-end: 0.1s;
  interest-delay-end: var(--interest-delay-end);
}

Resguardos y compatibilidad de navegadores para Popover

La API de Popover es, en su mayor parte, una mejora progresiva, pero sus comportamientos definitorios (promoción de la capa superior, cierre rápido y la invocación de popovertarget) no tienen un equivalente solo de CSS. Los navegadores más antiguos necesitan un polyfill o un resguardo manual si prefieres no enviar uno.

Polyfill: Para admitir el atributo popover en navegadores más antiguos, carga @oddbird/popover-polyfill de forma condicional. OBLIGATORIO: Detecta la función verificando la propiedad popover en HTMLElement.prototype y carga el polyfill solo cuando no haya compatibilidad nativa. NO lo cargues de forma incondicional.

Con un empaquetador o un mapa de importación:

// MANDATORY: Feature detect 'popover' on HTMLElement.prototype.
if (!("popover" in HTMLElement.prototype)) {
  import("@oddbird/popover-polyfill");
}

Sin un empaquetador, importa desde una CDN dentro de un <script type="module">:

<script type="module">
  if (!("popover" in HTMLElement.prototype)) {
    import("https://unpkg.com/@oddbird/popover-polyfill@latest/dist/popover.min.js");
  }
</script>

Advertencia de diseño: El polyfill no puede definir la pseudoclase :popover-open real, por lo que aplica una clase .\:popover-open en su lugar. OBLIGATORIO: Combina las dos con :is() o :where(); de lo contrario, los navegadores que no tengan :popover-open descartarán toda la regla:

[popover]:is(:popover-open, .\:popover-open) {
  display: block;
}

Como alternativa, para un resguardo heredado sin un polyfill, usa position: fixed y calcula manualmente las coordenadas a través de getBoundingClientRect() o confía en el posicionamiento predeterminado con inset: auto si eso es aceptable para el caso de uso.

El popover-polyfill no aplica polyfill al comportamiento de la sugerencia en los navegadores que admiten popover, pero no popover="hint". En esos navegadores, un cuadro de información que se abre con el enfoque puede permanecer abierto cuando se abre un segundo cuadro de información con el desplazamiento.

Para admitir navegadores sin posicionamiento de fijación, puedes usar el polyfill @oddbird/css-anchor-positioning. Como alternativa, puedes usar @supports para especificar una posición de resguardo.

Cuando uses el polyfill @oddbird/css-anchor-positioning para la compatibilidad con navegadores heredados, ten en cuenta que el polyfill no admite anclajes implícitos ni position-area en ventanas emergentes. Establece de forma explícita un anchor-name en el activador, establece position-anchor en el cuadro emergente y posiciónalo con las funciones de inserción anchor() en lugar de position-area:

<!-- MANDATORY: Conditionally install the anchor positioning polyfill -->
<script type="module">
  if (!("anchorName" in document.documentElement.style)) {
    await import("https://unpkg.com/@oddbird/css-anchor-positioning");
  }
</script>
button[interestfor="tooltip-attrs"] {
  /* MANDATORY: Each trigger and popover pair must have a unique anchor name, referenced by `anchor-name` on the trigger and `position-anchor` on the popover. */
  anchor-name: --tooltip-attrs;
}
#tooltip-attrs {
  position-anchor: --tooltip-attrs;
  /* If using the anchor positioning polyfill with a popover, DO use `anchor()` functions, and not `position-area. */
  top: anchor(bottom);
  left: anchor(right);
  margin: unset;
}