Accessibility форм

Atomico — это современная библиотека для создания веб-компонентов на JavaScript с реактивным подходом. Одной из ключевых задач при разработке компонентов является обеспечение доступности (Accessibility, сокращённо a11y), особенно для форм, где взаимодействие пользователя с элементами управления должно быть максимально удобным и предсказуемым.

Фокус на доступности включает поддержку экранных читалок, клавиатурной навигации и корректной семантики HTML. Atomico предоставляет инструменты для интеграции этих практик без излишней сложности.


Основы доступности форм

Формы должны использовать семантически корректные элементы:

  • <form> для контейнера формы.
  • <label> для описания полей ввода.
  • <input>, <textarea>, <select> для ввода данных.
  • Атрибуты aria-* для дополнительных подсказок экранным читалкам.

Atomico позволяет работать с этими элементами внутри веб-компонентов, сохраняя реактивность и интеграцию с состоянием.

Пример базовой формы в Atomico:

import { c, useState } from "atomico";

function MyForm() {
    const [name, setName] = useState("");

    return c("form", { 
        onSubmit: (event) => {
            event.preventDefault();
            console.log("Submitted:", name);
        }
    },
        c("label", { for: "name" }, "Имя"),
        c("input", {
            id: "name",
            type: "text",
            value: name,
            onInput: (e) => setName(e.target.value),
            required: true,
            "aria-required": "true"
        }),
        c("button", { type: "submit" }, "Отправить")
    );
}

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

  • Используется семантический <label> с атрибутом for, связанный с input.
  • Применён атрибут required и aria-required для поддержки экранных читалок.
  • Обработчик onSubmit предотвращает стандартное поведение браузера, сохраняя контроль над отправкой формы.

Реактивное управление состоянием формы

Atomico позволяет синхронизировать состояние полей формы с реактивными переменными через useState. Это даёт возможность:

  • Валидации полей в реальном времени.
  • Управления ошибками через визуальные подсказки и атрибуты aria-invalid.
  • Динамической генерации подсказок через aria-describedby.

Пример с валидацией:

function ValidatedForm() {
    const [email, setEmail] = useState("");
    const [error, setError] = useState("");

    function validateEmail(value) {
        return /\S+@\S+\.\S+/.test(value);
    }

    return c("form", { onSubmit: e => e.preventDefault() },
        c("label", { for: "email" }, "Email"),
        c("input", {
            id: "email",
            type: "email",
            value: email,
            onInput: (e) => {
                const val = e.target.value;
                setEmail(val);
                setError(validateEmail(val) ? "" : "Некорректный email");
            },
            "aria-invalid": !!error,
            "aria-describedby": error ? "email-error" : undefined,
            required: true
        }),
        error && c("span", { id: "email-error", role: "alert" }, error),
        c("button", { type: "submit", disabled: !!error }, "Отправить")
    );
}

Особенности:

  • aria-invalid информирует пользователя о неверном вводе.
  • aria-describedby указывает на элемент с текстом ошибки.
  • role="alert" обеспечивает мгновенное озвучивание ошибки экранной читалкой.

Навигация с клавиатуры

Для доступности критически важно поддерживать навигацию с клавиатуры:

  • Поля формы должны быть фокусируемыми (tabindex обычно не нужен для стандартных элементов, но полезен для кастомных компонентов).
  • События onKeyDown или onKeyUp позволяют реализовать кастомные комбинации клавиш.
  • Кнопки должны быть интерактивными с помощью клавиши Enter или Space.

Пример кастомного select:

function CustomSelect({ options }) {
    const [open, setOpen] = useState(false);
    const [selected, setSelected] = useState(null);

    return c("div", { 
        role: "listbox",
        tabindex: 0,
        "aria-expanded": open,
        onKeyDown: (e) => {
            if(e.key === "Enter" || e.key === " ") setOpen(!open);
        },
        onClick: () => setOpen(!open)
    },
        selected ?? "Выберите значение",
        open && c("ul", {},
            options.map(opt =>
                c("li", {
                    role: "option",
                    "aria-selected": selected === opt,
                    onClick: () => setSelected(opt)
                }, opt)
            )
        )
    );
}

Здесь:

  • Используются ARIA-атрибуты для кастомного списка.
  • Поддерживается клавиатурная навигация через Enter и Space.
  • aria-selected указывает на текущий выбор для экранных читалок.

Итоги подходов

Atomico сочетает реактивность и семантическую разметку, что позволяет строить доступные формы без дублирования логики. Основные принципы:

  • Использование нативных элементов формы при возможности.
  • Поддержка ARIA-атрибутов для кастомных компонентов.
  • Реактивное состояние для динамической валидации и сообщений об ошибках.
  • Навигация с клавиатуры и корректное управление фокусом.

Правильная реализация этих практик делает веб-компоненты на Atomico доступными для всех пользователей, включая людей с ограничениями.