fix(a11y): roles ARIA que contradecían la semántica del elemento
Una auditoría marcó `<ul role="img">` en el riel de clases de fuego. El rol contradice la semántica implícita de lista, así que la combinación es inválida y el árbol de accesibilidad queda mal formado. La intención original era correcta: el riel debe anunciarse como una sola unidad con su resumen, no como "lista de cinco elementos: A, B, C, D, K", que pierde justo lo que importa, cuáles están encendidas. Lo que estaba mal era el elemento, no el rol. El riel pasa a un contenedor genérico y conserva role="img" con su etiqueta. Al revisar apareció el mismo error en dos gráficas del panel, que además duplicaban el anuncio: leían el resumen completo y después la tabla accesible entera. Ahí la representación visual se oculta al árbol y la tabla equivalente queda como única fuente. También se corrigió el botón del selector de clases, que se anunciaba "A Clase A" por tener la letra visible junto a un texto oculto con el nombre completo. Se agrega una comprobación de regresión que valida el rol contra la semántica de ul, ol, li, table, nav, main, header y footer, para que no vuelva a colarse. Suite completa en verde. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
15d31b5d6b
commit
ab9f3ef6d3
@@ -78,6 +78,33 @@ for (const ruta of RUTAS_MUESTRA) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- roles que contradicen la semántica del elemento ---
|
||||
//
|
||||
// Una auditoría marcó `<ul role="img">` en el riel de clases de fuego:
|
||||
// el rol contradice la semántica implícita de lista. El mismo error
|
||||
// estaba en dos gráficas del panel. Los elementos de abajo tienen
|
||||
// semántica fuerte y no deben recibir un rol arbitrario; lo único
|
||||
// razonable es quitarles la semántica con presentation/none, o repetir
|
||||
// la que ya tienen.
|
||||
const PERMITIDOS: Record<string, string[]> = {
|
||||
ul: ["list", "presentation", "none", "menu", "menubar", "listbox", "group", "tablist"],
|
||||
ol: ["list", "presentation", "none", "menu", "menubar", "listbox", "group", "tablist"],
|
||||
li: ["listitem", "presentation", "none", "menuitem", "option", "tab", "treeitem"],
|
||||
table: ["table", "grid", "treegrid", "presentation", "none"],
|
||||
nav: ["navigation", "presentation", "none"],
|
||||
main: ["main", "presentation", "none"],
|
||||
header: ["banner", "group", "presentation", "none"],
|
||||
footer: ["contentinfo", "group", "presentation", "none"],
|
||||
};
|
||||
for (const [etiqueta, roles] of Object.entries(PERMITIDOS)) {
|
||||
for (const el of Array.from(document.querySelectorAll(etiqueta))) {
|
||||
const rol = el.getAttribute("role");
|
||||
if (rol && !roles.includes(rol)) {
|
||||
fallas.push(`rol inapropiado para el elemento: <${etiqueta} role="${rol}">`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- listas mal formadas ---
|
||||
for (const ul of Array.from(document.querySelectorAll("ul, ol"))) {
|
||||
for (const hijo of Array.from(ul.children)) {
|
||||
|
||||
Reference in New Issue
Block a user