Svelte actions

Библиотека ally.js предоставляет мощные инструменты для управления доступностью (accessibility, a11y) веб-приложений. Она позволяет программно определять, какие элементы доступны для фокусировки, управлять фокусом и обходить ограничения, накладываемые браузером или структурой DOM. В сочетании с Svelte actions ally.js становится удобным инструментом для создания динамических и доступных интерфейсов.


Подключение ally.js и интеграция с Svelte

Установка ally.js производится через npm:

npm install ally.js

Импорт в компоненте Svelte:

import ally from 'ally.js';

Svelte actions — это функции, которые автоматически применяются к DOM-элементам при их монтировании. Ally.js удобно использовать внутри actions для управления фокусом и видимостью элементов:

function focusTrap(node, options = {}) {
    const trap = ally.maintain.focus({
        context: node,
        ...options
    });

    return {
        destroy() {
            trap.disengage();
        }
    };
}

Применение action к элементу:

<div use:focusTrap>
    <button>Кнопка 1</button>
    <button>Кнопка 2</button>
</div>

Здесь maintain.focus создаёт контекст, внутри которого фокус остаётся ограниченным выбранным контейнером, предотвращая случайное перемещение фокуса за пределы элемента.


Навигация и управление фокусом

Ally.js позволяет:

  • Определять, какие элементы доступны для фокуса.
  • Программно управлять фокусом.
  • Обеспечивать логический порядок обхода элементов с клавиатуры.

Пример поиска доступного элемента и установки фокуса:

import ally from 'ally.js';

function autoFocus(node) {
    const firstFocusable = ally.query.firstTabbable(node);
    if (firstFocusable) {
        firstFocusable.focus();
    }
    
    return {
        destroy() {
            // при необходимости дополнительные действия при удалении элемента
        }
    };
}

Svelte action autoFocus позволяет автоматически фокусировать первый tabbable-элемент при монтировании контейнера.


Поддержка динамического контента

Для динамически создаваемых элементов ally.js предоставляет методы, которые учитывают изменения DOM в реальном времени. Например, можно использовать observe для отслеживания доступных элементов:

function observeFocus(node) {
    const observer = ally.query.tabbable(node, { context: node });

    // пример реагирования на доступные элементы
    console.log('Доступные элементы для фокуса:', observer);

    return {
        destroy() {
            // Очистка наблюдателя при размонтировании
        }
    };
}

В комбинации с Svelte reactive statements ($:) это позволяет создавать динамически доступные интерфейсы без потери контроля над фокусом.


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

Ally.js особенно полезен при реализации модальных окон, всплывающих подсказок и других overlay-компонентов. Основная задача — trap focus, чтобы пользователь не мог покинуть модальное окно клавиатурой.

Пример action для модального окна:

function modalFocus(node) {
    const trap = ally.maintain.focus({
        context: node,
        ignoreCaret: true
    });

    const restoreFocus = ally.focus.restore();

    return {
        destroy() {
            trap.disengage();
            restoreFocus();
        }
    };
}
  • ignoreCaret: true исключает курсор из ограничений фокусировки.
  • ally.focus.restore() возвращает фокус к элементу, который был активен до открытия модального окна.

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

<div use:modalFocus>
    <button>Закрыть</button>
    <input type="text" placeholder="Введите текст" />
</div>

Совмещение с анимациями и переходами

Svelte активно использует transitions и анимации. Для корректной работы фокуса при появлении/исчезновении элементов необходимо учитывать задержки анимации. Ally.js предоставляет методы для отложенной установки фокуса:

function delayedFocus(node, delay = 300) {
    const timeout = setTimeout(() => {
        const first = ally.query.firstTabbable(node);
        if (first) first.focus();
    }, delay);

    return {
        destroy() {
            clearTimeout(timeout);
        }
    };
}

Такой подход предотвращает попытки установить фокус до завершения визуального перехода.


Важные методы ally.js для Svelte actions

  • ally.query.firstTabbable(container) — возвращает первый доступный для фокуса элемент.
  • ally.query.lastTabbable(container) — возвращает последний tabbable-элемент.
  • ally.query.tabbable(container) — массив всех доступных элементов для табуляции.
  • ally.maintain.focus({context: node}) — ограничивает фокус внутри контейнера.
  • ally.focus.restore() — возвращает фокус к предыдущему элементу.
  • ally.query.focusable(container) — список всех focusable элементов, включая элементы с tabindex="-1".

Использование в сложных интерфейсах

Ally.js позволяет строить интерфейсы с несколькими вложенными фокусными контекстами, такими как:

  • Аккордеоны с внутренними tabbable-элементами.
  • Меню с вложенными подменю.
  • Сложные формы с динамическими секциями.

Пример комбинирования focus trap и auto-focus:

function panelFocus(node) {
    const trap = ally.maintain.focus({ context: node });
    const first = ally.query.firstTabbable(node);
    if (first) first.focus();

    return {
        destroy() {
            trap.disengage();
        }
    };
}

Такой паттерн обеспечивает, что пользователь всегда остаётся в контексте панели и сразу попадает на первый интерактивный элемент.


Выводы по применению

Ally.js в сочетании с Svelte actions позволяет:

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

Правильное использование ally.js снижает количество ручного кода для доступности и обеспечивает соответствие современным стандартам a11y.