Atomico — это библиотека для создания веб-компонентов на базе Web Components с реактивной моделью, схожей с React. Одной из ключевых задач при разработке компонентов является обеспечение их доступности для пользователей с особыми потребностями, включая людей, использующих экранные читалки.
Экранные читалки ориентируются на ARIA-атрибуты (Accessible Rich Internet Applications) и стандартные HTML-элементы для определения структуры страницы и интерактивных элементов. Atomico позволяет добавлять эти атрибуты напрямую через JSX-подобный синтаксис.
import { c, html } from "atomico";
function Button({ label, disabled }) {
return html`
<button
role="button"
aria-label=${label}
?disabled=${disabled}
>
${label}
</button>
`;
}
Button.props = {
label: String,
disabled: Boolean
};
export const button = c(Button);
Ключевые моменты:
? перед атрибутом (?disabled)
автоматически применяет атрибут только если значение истинно.Для пользователей экранных читалок критично правильное управление
фокусом. Atomico обеспечивает реактивное отслеживание состояния и
позволяет управлять атрибутами tabindex и событиями
клавиатуры:
function Menu({ items }) {
let activeIndex = 0;
function onKeyDown(event) {
if (event.key === "ArrowDown") activeIndex = (activeIndex + 1) % items.length;
if (event.key === "ArrowUp") activeIndex = (activeIndex - 1 + items.length) % items.length;
}
return html`
<ul role="menu" @keydown=${onKeyDown}>
${items.map((item, index) => html`
<li
role="menuitem"
tabindex=${index === activeIndex ? 0 : -1}
>
${item}
</li>
`)}
</ul>
`;
}
Особенности:
tabindex="0" делает элемент доступным для фокуса.tabindex="-1" доступны только программно
через скрипт.@keydown) позволяют
перемещать фокус по элементам меню.Экранные читалки отслеживают изменения DOM, но иногда динамически обновляемый контент нужно явно озвучивать. Для этого применяются ARIA-live регионы:
function Notifications({ messages }) {
return html`
<div aria-live="polite">
${messages.map(msg => html`<p>${msg}</p>`)}
</div>
`;
}
Для сложных виджетов, таких как табы или аккордеоны, важно поддерживать правильную семантику и фокус:
function Tabs({ tabs }) {
let activeTab = 0;
return html`
<div role="tablist">
${tabs.map((tab, index) => html`
<button
role="tab"
aria-selected=${index === activeTab}
tabindex=${index === activeTab ? 0 : -1}
@click=${() => activeTab = index}
>
${tab.label}
</button>
`)}
</div>
<div role="tabpanel">
${tabs[activeTab].content}
</div>
`;
}
<button>, <input>,
<ul>).Atomico позволяет сочетать реактивность и гибкость Web Components с полным соблюдением правил доступности, что обеспечивает совместимость с экранными читалками и улучшает UX для всех пользователей.