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