Библиотека Aria-query предназначена для работы с ARIA-атрибутами и ролями в веб-разработке, предоставляя средства для валидации и анализа доступности интерфейсов. Один из ключевых аспектов работы с ARIA — выявление конфликтов атрибутов, когда определённые комбинации ролей и свойств могут привести к некорректной интерпретации страницы вспомогательными технологиями.
Основные структуры библиотеки включают:
aria-checked, aria-expanded.Пример базовой структуры роли:
import { roles } from 'aria-query';
const buttonRole = roles.get('button');
console.log(buttonRole.props); // Все допустимые ARIA-свойства для кнопки
Каждая роль содержит:
Конфликт атрибутов возникает, когда:
Примеры:
role="button" с aria-checked="true"
создаёт конфликт, так как кнопка не поддерживает состояние
checked.role="checkbox" и aria-pressed
может вызвать неоднозначность для скринридеров.Для обнаружения конфликтов используются методы проверки свойств на совместимость с ролями.
import { roles, aria } from 'aria-query';
function checkAttributeConflicts(roleName, attributes) {
const role = roles.get(roleName);
const conflicts = [];
for (const attr in attributes) {
if (!role.props.has(attr)) {
conflicts.push({
attribute: attr,
reason: 'Не поддерживается ролью'
});
}
}
return conflicts;
}
const conflicts = checkAttributeConflicts('button', { 'aria-checked': true });
console.log(conflicts);
// [{ attribute: 'aria-checked', reason: 'Не поддерживается ролью' }]
Ключевой момент — каждое свойство должно проверяться
относительно допустимых значений и типов данных, а не только
наличия в списке props.
Aria-query поддерживает иерархию ролей, что позволяет учитывать наследуемые свойства:
const menuItemRole = roles.get('menuitem');
console.log(menuItemRole.superClass); // ['command', 'widget']
Если свойство допустимо для родительской роли, оно считается корректным для дочерней. Проверка конфликтов должна учитывать все уровни иерархии.
Конфликты могут возникать при комбинации нескольких атрибутов, даже если каждый по отдельности допустим:
const attributes = {
'aria-expanded': true,
'aria-checked': true
};
function checkCombinedAttributes(roleName, attributes) {
const role = roles.get(roleName);
const conflicts = [];
for (const attr in attributes) {
if (!role.props.has(attr)) {
conflicts.push(attr);
}
}
// Дополнительная логика для комбинаций
if ('aria-expanded' in attributes && 'aria-checked' in attributes) {
conflicts.push('Несовместимая комбинация aria-expanded и aria-checked');
}
return conflicts;
}
console.log(checkCombinedAttributes('button', attributes));
// ['aria-checked', 'Несовместимая комбинация aria-expanded и aria-checked']
Такой подход позволяет выявлять сложные логические конфликты, которые не видны при проверке отдельных атрибутов.
Aria-query часто применяется в инструментах линтинга и статической проверки кода:
Пример интеграции с линтером:
import { roles } from 'aria-query';
import fs from 'fs';
import { parse } from 'node-html-parser';
const html = fs.readFileSync('index.html', 'utf-8');
const root = parse(html);
root.querySelectorAll('[role]').forEach(el => {
const roleName = el.getAttribute('role');
const attributes = Object.fromEntries(
Array.from(el.attributes).filter(([name]) => name.startsWith('aria-'))
);
const conflicts = checkAttributeConflicts(roleName, attributes);
if (conflicts.length) {
console.warn(`Элемент <${el.tagName}> с ролью ${roleName} содержит конфликты:`, conflicts);
}
});
aria-checked и aria-pressed
одновременно на одном элементе.Aria-query обеспечивает мощный инструментальный базис для выявления и предотвращения конфликтов ARIA-атрибутов, что критично для создания доступных веб-интерфейсов и соответствия стандартам WAI-ARIA.