La espera terminó: el elemento DIALOG alcanza pleno soporte
Tags: html, a11y, webdev, javascript
Published: 2022-04-19 14:12:10

Primero sería Firefox 98, en marzo de este año, quien se sumaría a darle soporte[^1], y Safari haría lo propio al poco tiempo con su más reciente versión, la 15.4[^2], pasando así el elemento `<dialog>` de HTML5 a ser soportado por todos los navegadores modernos[^3].

![Tabla de compatibilidad de caniuse.com](https://i.imgur.com/Gpea71M.png)

## Especificaciones principales

- **Categoría a la que pertenece**: Contenido de flujo[^4], seccionado raíz[^5].

- **Contenido permitido**: Contenido de flujo.

- **Interfaz de DOM**: `HTMLDialogElement`.

## Contexto de uso
Se usa para representar un cuadro de diálogo o ventana modal, ya sea para informar algo importante al usuario, como para involucrarlo en alguna acción o toma de decisiones. Están inspirados en los que vemos comúnmente en todos los sistemas operativos.

## Problemas que resuelve

Las soluciones mágicas que nos trae, además de tener un impacto positivo del lado del usuario, mejoran también la experiencia de desarrollo. Veamos algunas.

### Accesibilidad y experiencia de usuario
El diálogo se cierra al presionar la tecla <kbd>Esc</kbd>, interacción que cualquier usuario, tenga una discapacidad o no, agradecerá siempre.

Por defecto, el foco se coloca automáticamente sobre el primer elemento [enfocable](https://html.spec.whatwg.org/multipage/interaction.html#focusable) que exista dentro, a no ser que se indique otro explícitamente usando el atributo `autofocus`.

Al cerrarse el diálogo, el foco vuelve a donde se encontraba antes (típicamente el botón o elemento que disparó su apertura).

Por otro lado, para que las tecnologías de apoyo, como los lectores de pantalla, comuniquen al usuario el propósito del diálogo, es suficiente con usar `aria-labelledby` en el elemento, apuntando al `id` del título del mismo, o sumistrar el texto directamente mediante `aria-label`. Si este no fuese lo suficientemente descriptivo, se puede recurrir a `aria-describedby`[^6].
```html
<dialog aria-labelledby="dialog-title">
    <h1 id="dialog-title">Agregar a la billetera</h1>
	...
</dialog>
```
```html
<dialog aria-label="Agregar a la billetera">
	...
</dialog>
```
De esta forma, al abrirse el diálogo, se anunciará su título seguido de la palabra "diálogo" (si no se provee, se anuncia solamente "diálogo"). Además, huelga decir que ya nos podemos olvidar del uso de `role="dialog"`, ya que, al ser una etiqueta semántica, su rol es implícito, pudiendo únicamente cambiarse por el más específico `role="alertdialog"`[^7] [^8].

### Interacción y estilos
Invocando el método `show()` del elemento, se muestra el diálogo sin impedir la navegación por el resto del documento, mientras que `showModal()` confina al usuario, realizando [focus trap](https://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-trapping.html), hasta tanto se ejecute el método `close()`; al accionarse un botón, por ejemplo, o si se pulsa <kbd>Esc</kbd>. Además, `showModal()` agrega una capa de overlay para ocultar el contenido subyacente, la cual puede ser accedida por CSS para su personalización, mediante el pseudoelemento [::backdrop](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop)[^9].

Si se busca conocer el estado (abierto o cerrado) del diálogo, se puede consultar el atributo (implícito) `open` del elemento, el cual retorna un _booleano_. Una advertencia: ¡nunca se debería modificar el valor de este atributo o removerlo! En primer lugar, porque el navegador perdería por completo el rastro del estado, produciendo varios efectos no deseados[^10]; y en segundo lugar, ¿por qué querrías hacerlo, si al final se controla "automágicamente"?

## Conclusiones
A estas alturas, y si eres alguien con ciertos conocimientos de accesibilidad, te resultarán más que evidentes las ventajas que trae de serie este elemento. Hasta ahora, debíamos tener una serie de consideraciones que, si se descuidaban, fácilmente nuestras modales podían volverse una pesadilla para la accesibilidad. Otra opción era buscar una librería que proporcione diálogos accesibles, a cambio de ceder el control sobre nuestro markup, o terminar invirtiendo en personalización el tiempo que se pretendió ahorrar en un principio.
En cualquier caso, había que cerciorarse de que se cumpla con lo siguiente:

- **Indicar el rol**: para que las tecnologías de apoyo puedan anunciar que se trata de un diálogo.
- **Atrapar el foco** -aplica para ventanas modales-: Se trata, hoy por hoy, de una labor prácticamente artesanal, y consiste en hacer que, al avanzar el foco con la tecla tabuladora (<kbd>Tab</kbd>), tras haber alcanzado el último elemento interactivo del diálogo, este vuelva a posarse sobre el primero; y a la inversa, al retroceder con <kbd>Shift</kbd> + <kbd>Tab</kbd>, impidiendo que el usuario navegue a otras partes del documento por fuera de la ventana modal.
- **Bloquear el resto del documento**: por la misma razón que en el caso anterior: en una ventana modal, queremos que el usuario se concentre en una acción específica. Para esto, la práctica más común es volver el resto del contenido invisible para los usuarios de tecnologías de asistencia cuando la modal está visible, añadiendo `aria-hidden="true"` mediante JS a un contenedor principal, colocando por fuera al elemento que hace las veces de diálogo; además, conviene agregar a este último el atributo `aria-modal="true"`, de esa forma los productos de apoyo avisarán que el resto del contenido está inactivo. Luego, hay que recordar cambiar a `aria-hidden="false"` o remover el atributo por completo del nodo cuando la modal se cierre.
- **Controlar el estado abierto/cerrado**: para impedir la creación de  diálogos duplicados o permitir volver a abrir un diálogo cerrado; para lo mencionado en el punto anterior sobre el bloqueo del documento.
- **Conservar la referencia del elemento que abrió el diálogo**: para devolver el foco a su sitio original al cerrarlo. 

Probablemente nos hayamos encontrado realizando estas tareas de manera repetitiva en el pasado. Bueno, se podría decir que ya no tendremos que preocuparnos más por ellas.

Sin embargo, como sucede con toda nueva característica, conviene que sea usada con prudencia y proporcionando algún tipo de fallback para agentes de usuario más antiguos: versiones de navegadores aun vivas o tecnologías de apoyo cuyo ciclo de actualización es más lento. Una buena opción, bastante robusta, puede ser [a11y-dialog](https://a11y-dialog.netlify.app/). También existe un [polyfill](https://github.com/GoogleChrome/dialog-polyfill) publicado por el equipo de Chrome. No nos olvidemos que la retrocompatibilidad también forma parte de la accesibilidad.

## Donar

- [Buy Me a Coffee](https://buymeacoffee.com/fena)
- [Invitame un café en cafecito.app](https://cafecito.app/fena)

[^1]: [Firefox 98 release notes](https://www.mozilla.org/en-US/firefox/98.0/releasenotes/)
[^2]: [New WebKit Features in Safari 15.4](https://webkit.org/blog/12445/new-webkit-features-in-safari-15-4/)
[^3]: [Dialog element | Can I use... Support tables for HTML5, CSS3, etc](https://caniuse.com/dialog)
[^4]: [Content categories - Developer guides | MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#flow_content)
[^5]: [HTML Standard](https://html.spec.whatwg.org/multipage/sections.html#sectioning-root)
[^6]: [aria-describedby - Accessibility | MDN](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-describedby)
[^7]: [MDN: The Dialog element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
[^8]: [Accessible Rich Internet Applications (WAI-ARIA) 1.3](https://w3c.github.io/aria/#alertdialog)
[^9]: [Introducing the Dialog Element | WebKit](https://webkit.org/blog/12209/introducing-the-dialog-element/)
[^10]: [HTML Standard](https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element)