ARIA-атрибуты

ARIA (Accessible Rich Internet Applications) — набор атрибутов, обеспечивающих доступность динамических компонентов для вспомогательных технологий, таких как скринридеры. В контексте FAST Element, эти атрибуты интегрируются напрямую в шаблоны компонентов, позволяя управлять поведением и состоянием элементов через декларативный синтаксис.

Атрибут role

Атрибут role определяет семантику элемента. В FAST Element он задается как обычный HTML-атрибут в шаблоне:

import { FASTElement, html, css } from "@microsoft/fast-element";

class MyButton extends FASTElement {
  static template = html`
    <button role="button" @click="${x => x.handleClick()}">
      Нажми меня
    </button>
  `;
}

Использование role критично для компонентов, которые визуально напоминают стандартные элементы, но реализованы через <div> или <span>.

Атрибуты состояния: aria-*

ARIA предоставляет множество атрибутов состояния и свойств. В FAST Element их можно связывать с внутренними свойствами компонента через двустороннюю привязку.

Пример с aria-pressed для кнопки:

class ToggleButton extends FASTElement {
  static template = html`
    <button
      role="button"
      aria-pressed="${x => x.pressed}"
      @click="${x => x.toggle()}"
    >
      ${x => x.pressed ? 'Включено' : 'Выключено'}
    </button>
  `;

  pressed = false;

  toggle() {
    this.pressed = !this.pressed;
  }
}

Ключевой момент: значение атрибута автоматически обновляется при изменении свойства pressed, что делает компонент полностью доступным для вспомогательных технологий.

ARIA и динамическое обновление контента

FAST Element позволяет использовать реактивные свойства для обновления ARIA-атрибутов в реальном времени. Например, компонент аккордеона:

class AccordionPanel extends FASTElement {
  static template = html`
    <div role="region" aria-labelledby="header-${x => x.id}" ?hidden="${x => !x.open}">
      <slot></slot>
    </div>
    <button id="header-${x => x.id}" @click="${x => x.toggle()}">
      ${x => x.title}
    </button>
  `;

  id = Math.random().toString(36).substring(2, 10);
  open = false;

  toggle() {
    this.open = !this.open;
  }
}

Здесь используется:

  • aria-labelledby для связи заголовка с панелью.
  • ?hidden для управления видимостью контента, что синхронизируется с ARIA.

Декларативная привязка ARIA

FAST Element поддерживает синтаксис условных и вычисляемых атрибутов:

  • aria-checked="${x => x.checked}" — значение динамически обновляется.
  • ?disabled="${x => x.isDisabled}" — короткая форма булевых атрибутов.
  • Использование id и связывание с aria-labelledby или aria-describedby позволяет строить сложные компоненты с корректной семантикой.

Особенности интеграции с шаблонами

  • Слотинг: при использовании <slot> необходимо учитывать, что ARIA-атрибуты не наследуются автоматически. Следует явно задавать role или aria-* на контейнере.
  • События: при изменении состояния компонента важно синхронизировать ARIA-атрибуты с событиями, чтобы скринридеры получали актуальную информацию.
  • Композиция компонентов: при создании составных виджетов, таких как табы или аккордеоны, рекомендуется создавать реактивные свойства, отражающие состояния ARIA (selected, expanded, hidden) и использовать их в шаблоне.

Рекомендации по использованию

  • Каждому интерактивному элементу присваивать role, соответствующий его поведению.
  • Динамические состояния компонентов связывать с соответствующими aria-* атрибутами.
  • Избегать использования ARIA для элементов, которые имеют нативную семантику, так как это может привести к конфликтам.
  • Использовать реактивные свойства FAST Element для управления ARIA-атрибутами в реальном времени.

Пример комплексного компонента

class Tabs extends FASTElement {
  static template = html`
    <div role="tablist">
      ${x => x.tabs.map(tab => html`
        <button
          role="tab"
          aria-selected="${tab.selected}"
          aria-controls="panel-${tab.id}"
          @click="${() => x.selectTab(tab.id)}"
        >
          ${tab.label}
        </button>
      `)}
    </div>
    ${x => x.tabs.map(tab => html`
      <div
        role="tabpanel"
        id="panel-${tab.id}"
        ?hidden="${!tab.selected}"
        aria-labelledby="tab-${tab.id}"
      >
        ${tab.content}
      </div>
    `)}
  `;

  tabs = [
    { id: 1, label: 'Вкладка 1', content: 'Контент 1', selected: true },
    { id: 2, label: 'Вкладка 2', content: 'Контент 2', selected: false },
  ];

  selectTab(id) {
    this.tabs.forEach(tab => tab.selected = tab.id === id);
  }
}

В этом примере:

  • Каждая вкладка имеет role="tab" и атрибут aria-selected.
  • Панель связана с вкладкой через aria-labelledby и aria-controls.
  • Видимость панели управляется через реактивное свойство selected и булев атрибут ?hidden.

ARIA-атрибуты в FAST Element обеспечивают полную интеграцию с реактивной моделью и шаблонизацией, позволяя создавать доступные и динамичные веб-компоненты без лишнего кода.