Tucano v0.37.2

Teclado y accesibilidad

Todo se puede usar sin ratón, y las teclas son las del elemento nativo y las del ARIA APG — quien ya sabe usar un <select> no tiene que aprender nada. Esta página reúne en un solo lugar lo que cada componente hace con el teclado y lo que anuncia a quien usa lector de pantalla. Las etiquetas accesibles que la biblioteca escribe por su cuenta están en portugués por defecto, y Tucano.setTexts() las reemplaza; abajo aparecen entre comillas con su significado.

Tab no abre paneles

Llegar a un campo con Tab nunca abre calendario, lista ni selector de color. Abrir siempre es explícito: ↓, clic, y en el select también Enter y Espacio.

Abrir al recibir el foco parecía cómodo y era el origen de dos defectos. Los paneles se amontonaban al tabular — quien recorría un formulario hasta el botón de guardar se encontraba un calendario en la cara en cada campo, con el panel tapando el campo siguiente. Y en el campo de color el panel tapaba el propio campo de quien quería escribir el hex. Es la regla del <select> nativo, y es la del ARIA APG.

ComponenteSe abre con
Date picker↓, clic; Espacio con el campo vacío
Select↓, ↑, Enter, Espacio, clic
Color picker↓ en el campo o en la muestra; Enter y Espacio en la muestra, que es un <button>
Menú desplegable↓, ↑, Enter, Espacio o clic en el disparador

En el campo de fecha, el Enter es del formulario

El date picker es un campo de texto dentro de un <form>, y Enter en un campo de texto envía el formulario — es lo que cualquiera espera después de escribir la fecha. Secuestrar la tecla rompería el envío en silencio en todos los formularios existentes, así que ahí quien abre es el Espacio. Y solo con el campo vacío: en modo con hora se escribe 07/09/2026 14:30, y el espacio forma parte de lo escrito. Con el panel abierto, el Enter confirma lo que se escribió.

Cualquier panel que se abrió se cierra cuando el foco sale de él, así que tabular por la página nunca deja menús abiertos atrás.

Date picker

TeclaDóndeAcción
↓Campo, cerradoAbre el calendario
EspacioCampo vacío, cerradoAbre el calendario
EnterCampo, abiertoConfirma la fecha escrita y cierra; cerrado, envía el formulario
← →DíaDía anterior y siguiente
↑ ↓DíaMisma fecha en la semana anterior y en la siguiente
PageUp PageDownDíaMes anterior y siguiente; con Shift, año
Home EndDíaInicio y fin de la semana, respetando el primer día del locale
Enter EspacioDíaElige el día
↑ ↓Columna de horaValor anterior y siguiente de la columna
Home EndColumna de horaPrimer y último valor de la columna
Enter EspacioColumna de horaElige el valor
TabPanelCircula dentro del panel, sin escaparse a la página; cada columna de hora es una sola parada
EscPanelCierra, descarta lo que no se confirmó y devuelve el foco al campo

El campo es un combobox con aria-haspopup="dialog" y aria-expanded, y recibe aria-controls apuntando al panel mientras está abierto. El panel es role="dialog" con la etiqueta "Selecionar data" (seleccionar fecha) o "Selecionar período" (seleccionar período); cada mes es un role="grid" con el nombre del mes, cada semana un row y cada día un gridcell con aria-selected y la etiqueta completa — "sábado, 12 de septiembre de 2026". Un día fuera de los límites lleva aria-disabled: recibe el foco, pero no se elige. El cambio de mes se anuncia en una región aria-live="polite" que queda fija en el panel. Cada columna de hora, minuto y segundo es un grupo de botones con una sola parada de Tab, y el valor elegido lleva aria-pressed. Cerrar con un clic fuera no devuelve el foco al campo, para no quitárselo al lugar donde la persona hizo clic.

Select

TeclaAcción
↓ ↑ Enter EspacioCerrado, abre el panel
↑ ↓Abierto, recorre las opciones, saltando las desactivadas y dando la vuelta
Home EndPrimera y última opción, saltando las desactivadas
EnterElige la opción activa; en el múltiple, la marca o la desmarca
LetrasCon la búsqueda activada, filtran la lista ignorando tildes. La búsqueda se activa sola a partir de 6 opciones, o con data-search="true"
BackspaceEn el múltiple, con la búsqueda vacía, quita la última etiqueta
Backspace DeleteEn el simple, con la búsqueda vacía, limpia el valor, como la X — sin la X (clearable: false), no limpia
EscCierra y devuelve el foco al campo

Con el <select> nativo disabled, el campo sale del recorrido del Tab, queda atenuado, no abre, no acepta texto y oculta la X de limpiar y la de las etiquetas. Una opción dentro de <optgroup disabled> también cuenta como desactivada.

El control es un combobox con aria-haspopup="listbox", aria-expanded y aria-controls apuntando a la lista mientras está abierta — cerrada, la lista sale del DOM y el atributo se va con ella. La lista es listbox, con aria-multiselectable en el múltiple; cada opción es option con aria-selected y aria-disabled. La opción activa llega al lector de pantalla por aria-activedescendant, sin quitar el foco del campo de búsqueda. La X de cada etiqueta se llama "Remover" (quitar) seguido del nombre de la opción. El <select> nativo sigue en el formulario con aria-hidden, para que no se lea dos veces.

Color picker

TeclaDóndeAcción
↓Campo o muestraAbre el panel con el foco en el área de color
Enter EspacioMuestraAbre el panel con el foco en el área de color, y lo cierra
← →Área de colorSaturación, de 2 en 2%; con Shift, de 10 en 10
↑ ↓Área de colorBrillo, con el mismo paso
← ↓ / → ↑Barras de tono y opacidadDisminuye y aumenta de 1 en 1; con Shift, de 10 en 10
Home EndBarras de tono y opacidadMínimo y máximo
EscPanelCierra y devuelve el foco a la muestra

La muestra es un <button> "Escolher cor" (elegir color) con aria-haspopup="dialog" y aria-expanded; el panel es role="dialog" "Seletor de cor" (selector de color). Las barras son role="slider" con aria-valuenow, y cada color de la paleta es un botón con su propio valor como etiqueta. El campo de texto sigue siendo el <input> donde se escribe el hex.

TeclaDóndeAcción
↓DisparadorAbre y entra en el primer elemento
↑DisparadorAbre y entra en el último elemento
Enter EspacioDisparadorAbre y cierra
↑ ↓MenúElemento anterior y siguiente, dando la vuelta y saltando los desactivados
Home EndMenúPrimer y último elemento
EnterElementoLo acciona; un elemento sin href es <button> y acepta también Espacio
EscMenúCierra y devuelve el foco al disparador
TabMenúCierra y sigue al siguiente elemento de la página

El disparador recibe aria-haspopup="menu" y aria-expanded; el panel es role="menu", cada elemento menuitem, el separador separator y el elemento desactivado aria-disabled. Los elementos tienen tabindex="-1" a propósito: dentro de un menú quien navega es la flecha, y unos elementos tabulables harían que el Tab saliera del menú elemento a elemento.

Pestañas

TeclaAcción
TabEntra en la pestaña abierta y sale de la lista directo al panel — la lista es una sola parada
← →Pestaña anterior y siguiente, dando la vuelta y saltando las desactivadas
Home EndPrimera y última pestaña
Enter EspacioCon data-manual="true", cambia el panel; sin él, la flecha ya lo cambia

El script pone tablist, tab y tabpanel, une cada pestaña a su panel con aria-controls y aria-labelledby, y mantiene al día aria-selected y el tabindex. El modo manual existe para paneles que cargan por HTMX: con activación automática, recorrer cuatro pestañas con la flecha dispararía cuatro peticiones.

Modal y panel lateral

TeclaAcción
Tab Shift+TabCirculan dentro del diálogo, sin escaparse a la página
EscCierra con animación y devuelve el foco a quien lo abrió — salvo con closable: false o data-closable="false"

Los dos son un <dialog> abierto con showModal(), y de ahí viene la accesibilidad: la página de detrás queda inerte, el foco queda atrapado y vuelve al disparador al cerrar, todo por el navegador y no por JavaScript nuestro. El Escape nativo cerraría al instante, sin animación; la biblioteca intercepta el cancel para cerrar por el mismo camino que la X. Con título, el diálogo queda etiquetado por él vía aria-labelledby, y la X se llama "Fechar" (cerrar). En Tucano.confirm(), cerrar con Esc es un rechazo: la promesa se resuelve con false.

Tooltip

Tecla o gestoAcción
Tab hasta el elementoMuestra la pista al instante, sin el retraso del puntero
Tab hacia fueraOculta la pista
EscLa oculta, incluso con el puntero quieto encima
ToqueEn pantalla táctil, tocar abre y volver a tocar cierra

El globo es role="tooltip" y el elemento apunta a él con aria-describedby, así que el lector de pantalla lee la pista junto con el nombre del botón. Un elemento que no recibe foco — un <span>, por ejemplo — recibe tabindex="0"; si no, quien usa teclado nunca vería la pista. El Esc con el puntero encima es lo que pide la WCAG 1.4.13, para quien usa ampliación de pantalla y necesita apartar una pista que tapa el contenido.

Tabla

TeclaAcción
TabPasa por las cabeceras ordenables, por las casillas de selección y por los botones de las filas
EnterEn la cabecera, ordena — en el modo servidor, sigue el enlace
EspacioMarca la casilla; en el modo cliente, también ordena con el botón de la cabecera

La cabecera ordenada tiene aria-sort="ascending" o "descending" y las demás "none". En el modo servidor el disparador es un <a>, anunciado como enlace; en el cliente, un <button>. La casilla de la cabecera es "Selecionar todas as linhas desta página" (seleccionar todas las filas de esta página), la de cada fila "Selecionar linha" (seleccionar fila), y el estado mixto es el indeterminate nativo.

Los demás componentes

Estos no tienen teclado propio: lo que responde es el elemento nativo de debajo. Aun así, cada uno tiene algo que decirle al lector de pantalla.

ComponenteTecladoLo que anuncia
AcordeónEnter y Espacio en el <summary>, del navegadorAbierto y cerrado, por el <details>; la flecha es aria-hidden
UploadEnter y Espacio en la zona abren la ventana de archivosLa zona es role="button" descrita por la pista de tipos y tamaño; un archivo rechazado aparece en un aviso role="alert"
Máscara con ojoEnter y Espacio en el botón del ojo"Mostrar" y "Ocultar", con aria-pressed; con data-validate, el campo que no pasa recibe aria-invalid
Editor de textoCtrl/⌘ + B, I, U, K; Tab en una tabla avanza de celda y, en la última, crea una filaEl área es role="textbox" multilínea; las barras son role="toolbar" y los botones de formato tienen aria-pressed
ToastEl temporizador se pausa cuando algo dentro de él recibe el focoDos regiones en vivo creadas antes del primer mensaje: polite para lo normal y assertive para errores; cada toast es status o alert
PaginaciónEnlaces nativos<nav> "Paginação" (paginación), aria-current="page" en la actual; el extremo desactivado es un <span>, fuera del camino del Tab
Casilla, opción e interruptorLas del <input> nativoLas del navegador; el interruptor necesita role="switch" en la plantilla, porque el CSS no pone roles
Grupo de botonesLas del <input> nativo: el grupo de radios es una sola parada del Tab, y las flechas lo recorrenEl grupo necesita nombre — role="group" con aria-label, o <fieldset> con <legend>; el <input> queda invisible, pero enfocable y anunciado

Lo que vale para todos

Las decisiones que se repiten de componente en componente.

ReglaPor qué
El elemento nativo sigue en el formularioNombre, obligatoriedad, estado mixto y el anuncio del lector de pantalla siguen siendo los del navegador
Un campo con error es aria-invalid="true", y no una claseEs el atributo que anuncia el lector de pantalla, y Django 5 lo escribe solo. En los componentes que cambian el campo por un control propio, el atributo se queda en el nativo y el CSS alcanza el control
El panel que se abrió se cierra cuando sale el focoSin esto, tabular por la página dejaba un rastro de paneles abiertos
Esc cierra y devuelve el foco a quien abrióCerrar soltando el foco al principio de la página obligaría a quien usa teclado a empezar desde arriba
Los dibujos van con aria-hiddenFlechas, iconos y puntos suspensivos de la paginación no se leen en voz alta
prefers-reduced-motion desactiva las animacionesVale para paneles, diálogos, toasts, pestañas, acordeón, tabla y botones