Программная генерация ARIA-разметки

Современные веб-приложения активно используют динамическое изменение DOM: элементы создаются программно, интерфейс перестраивается без перезагрузки страницы, компоненты управляются состоянием приложения. В таких условиях обычной HTML-семантики часто недостаточно для корректного взаимодействия со вспомогательными технологиями — экранными дикторами, программами управления голосом и другими инструментами доступности.

Спецификация WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) вводит систему ролей, состояний и свойств, которые описывают семантику интерфейса независимо от его визуальной структуры. Однако ручное добавление ARIA-атрибутов в динамических интерфейсах становится трудоёмким и подверженным ошибкам.

Библиотека aria-query решает эту проблему, предоставляя программный доступ к спецификации ARIA. Она содержит структурированные данные о ролях, их допустимых свойствах, поддерживаемых HTML-элементах и связях между ними. Это позволяет автоматически формировать корректную ARIA-разметку и проверять её валидность на этапе разработки.


Структура библиотеки aria-query

Внутри библиотеки реализован набор коллекций данных, описывающих спецификацию ARIA:

  • roles
  • elementRoles
  • roleElements
  • aria
  • dom

Каждая коллекция представляет собой структуру Map, содержащую метаданные о доступности.

Пример импорта:

import { roles, elementRoles, roleElements } from "aria-query";

Эти структуры используются для программного анализа HTML-элементов и генерации корректной ARIA-семантики.


Объект roles

Коллекция roles содержит описание всех ARIA-ролей, включая:

  • допустимые свойства (props)
  • родительские роли
  • обязательные контексты
  • поддерживаемые элементы
  • уровень абстракции роли

Пример получения информации о роли:

import { roles } from "aria-query";

const buttonRole = roles.get("button");

console.log(buttonRole);

Объект роли содержит подробные метаданные:

{
  abstract: false,
  accessibleNameRequired: true,
  childrenPresentational: true,
  props: {
    "aria-expanded": null,
    "aria-pressed": null
  },
  superClass: [
    ["roletype", "widget", "command"]
  ]
}

Ключевые характеристики роли

abstract Показывает, можно ли использовать роль напрямую в DOM.

props Набор допустимых ARIA-атрибутов.

superClass Иерархия наследования ролей.

Использование этих данных позволяет автоматически добавлять поддерживаемые свойства при генерации компонентов интерфейса.


Соответствие HTML-элементов и ролей

Не каждый HTML-элемент требует ручного указания роли. Многие элементы уже имеют встроенную семантику.

Коллекция elementRoles описывает соответствие HTML-элементов и ролей.

Пример:

import { elementRoles } from "aria-query";

for (const [element, roles] of elementRoles) {
  console.log(element, roles);
}

Структура записи:

{
  name: "button"
}
→ Set { "button" }

Это означает, что элемент <button> уже имеет роль button.

Использование данных для генерации ARIA

При создании интерфейса программно можно проверять, требуется ли добавление роли.

Пример функции:

import { elementRoles } from "aria-query";

function getImplicitRoles(tagName) {
  for (const [element, roles] of elementRoles) {
    if (element.name === tagName) {
      return [...roles];
    }
  }

  return [];
}

Использование:

getImplicitRoles("button");
// ["button"]

Если элемент уже имеет встроенную роль, добавление role в DOM не требуется.


Коллекция roleElements

Структура roleElements выполняет обратную задачу — показывает, какие HTML-элементы поддерживают конкретную роль.

Пример:

import { roleElements } from "aria-query";

const elements = roleElements.get("button");

console.log(elements);

Результат:

Set {
  { name: "button" },
  { name: "input", attributes: [{ name: "type", value: "button" }] }
}

Такая информация полезна при генерации HTML-структуры на основе роли.

Генерация элемента по роли

function getPreferredElement(role) {
  const elements = roleElements.get(role);

  if (!elements) return null;

  return [...elements][0];
}

Пример использования:

getPreferredElement("checkbox");

Результат:

{ name: "input", attributes: [{ name: "type", value: "checkbox" }] }

Это позволяет автоматически выбирать семантически правильный HTML-элемент.


Генерация ARIA-атрибутов

Коллекция aria содержит список всех ARIA-атрибутов и их типов.

Импорт:

import { aria } from "aria-query";

Получение данных:

const expanded = aria.get("aria-expanded");

console.log(expanded);

Пример структуры:

{
  type: "boolean",
  allowUndefined: true
}

Эта информация используется для проверки корректности значений.

Автоматическая установка атрибутов

Пример генерации:

function applyAriaProps(element, roleName) {
  const role = roles.get(roleName);

  if (!role) return;

  for (const prop in role.props) {
    if (!element.hasAttribute(prop)) {
      element.setAttribute(prop, "false");
    }
  }
}

Использование:

applyAriaProps(node, "button");

Функция добавляет поддерживаемые свойства, если они отсутствуют.


Проверка допустимости ARIA-атрибутов

Некоторые ARIA-атрибуты разрешены только для определённых ролей.

Библиотека позволяет проверить их допустимость.

Пример:

function isAriaPropAllowed(roleName, propName) {
  const role = roles.get(roleName);

  if (!role) return false;

  return propName in role.props;
}

Использование:

isAriaPropAllowed("button", "aria-expanded");

Результат:

true

Автоматическое определение роли

Иногда интерфейс генерируется на основе абстрактных компонентов. В этом случае необходимо определить подходящую ARIA-роль.

Использование elementRoles:

function detectRole(element) {
  const tagName = element.tagName.toLowerCase();

  const roles = getImplicitRoles(tagName);

  if (roles.length > 0) {
    return roles[0];
  }

  return "generic";
}

Это позволяет создавать доступную разметку даже при динамическом рендеринге компонентов.


Генерация ARIA-структуры для компонента

Пример генерации доступного переключателя:

function createSwitch(label) {
  const button = document.createElement("button");

  button.setAttribute("role", "switch");
  button.setAttribute("aria-checked", "false");

  button.textContent = label;

  button.addEventListener("click", () => {
    const checked = button.getAttribute("aria-checked") === "true";
    button.setAttribute("aria-checked", !checked);
  });

  return button;
}

Однако можно использовать aria-query для автоматического анализа роли.

function generateComponent(roleName) {
  const element = getPreferredElement(roleName);

  if (!element) return null;

  const node = document.createElement(element.name);

  if (element.attributes) {
    element.attributes.forEach(attr => {
      node.setAttribute(attr.name, attr.value);
    });
  }

  node.setAttribute("role", roleName);

  applyAriaProps(node, roleName);

  return node;
}

Использование:

const checkbox = generateComponent("checkbox");
document.body.appendChild(checkbox);

Использование aria-query в UI-фреймворках

Библиотека активно применяется в инструментах проверки доступности:

  • ESLint-плагинах
  • тестовых утилитах
  • библиотеках рендеринга компонентов

Например, eslint-plugin-jsx-a11y использует aria-query для определения корректности ARIA-атрибутов в JSX.

В React-приложениях библиотека может использоваться для генерации семантики компонентов.

Пример:

function withAria(roleName, props = {}) {
  const role = roles.get(roleName);

  if (!role) return props;

  const ariaProps = {};

  Object.keys(role.props).forEach(prop => {
    ariaProps[prop] = props[prop] ?? null;
  });

  return {
    role: roleName,
    ...ariaProps,
    ...props
  };
}

Использование:

const buttonProps = withAria("button", {
  "aria-pressed": false
});

Программная нормализация доступности

Одно из ключевых преимуществ aria-query — возможность автоматизировать правила доступности:

  • проверка допустимых ролей
  • контроль поддерживаемых атрибутов
  • генерация семантически корректных элементов
  • устранение дублирования ролей

Такая автоматизация особенно важна в больших приложениях, где интерфейс создаётся компонентными системами.


Генерация декларативных схем ARIA

На основе данных aria-query можно формировать схемы описания доступности компонентов.

Пример структуры:

function createAriaSchema(roleName) {
  const role = roles.get(roleName);

  if (!role) return null;

  return {
    role: roleName,
    props: Object.keys(role.props),
    abstract: role.abstract
  };
}

Использование:

createAriaSchema("dialog");

Результат:

{
  role: "dialog",
  props: ["aria-labelledby", "aria-describedby"],
  abstract: false
}

Такие схемы применяются в системах дизайн-компонентов для стандартизации доступности.


Динамическая адаптация интерфейса

При изменении состояния интерфейса ARIA-атрибуты должны обновляться синхронно с DOM.

Использование aria-query позволяет определить, какие атрибуты связаны с конкретной ролью.

Пример:

function updateAriaState(element, roleName, state) {
  const role = roles.get(roleName);

  if (!role) return;

  Object.entries(state).forEach(([prop, value]) => {
    if (prop in role.props) {
      element.setAttribute(prop, value);
    }
  });
}

Пример использования:

updateAriaState(button, "button", {
  "aria-pressed": "true"
});

Интеграция с системами тестирования

Данные aria-query используются для автоматических тестов доступности.

Пример проверки:

function validateRole(element) {
  const role = element.getAttribute("role");

  if (!role) return true;

  return roles.has(role);
}

Расширенный вариант может проверять:

  • поддерживаемые ARIA-атрибуты
  • допустимость ролей
  • соответствие HTML-элементу

Архитектурные преимущества программной генерации ARIA

Программный подход к генерации ARIA-разметки обеспечивает:

консистентность интерфейса единые правила доступности во всех компонентах

автоматическую проверку спецификации данные библиотеки синхронизированы с ARIA-стандартом

снижение числа ошибок исключается ручное добавление некорректных атрибутов

масштабируемость подходит для крупных интерфейсных систем

Библиотека aria-query превращает спецификацию доступности из статического документа в программно используемую модель, позволяя генерировать корректную ARIA-семантику автоматически и системно.