JavaScript перетворив статичні документи на програмне забезпечення. Односторінкові додатки здаються миттєвими. Жодних повних перезавантажень сторінок, жодного мерехтіння білих екранів. Але за цю швидкість доводиться платити ціну, яку багато команд ігнорують: базові механізми вебу починають руйнуватися. Навігація стає крихкою. Пошукові системи важко відстежувати шляхи. Програми екранного доступу губляться. А користувачі опиняються в пастці інтерфейсів, які виглядають як вебсайти, але поводяться як зламані десктопні додатки.
Винуватцем зазвичай є div з обробником onClick.
Припиніть використовувати div як посилання
div не несе жодного семантичного значення. Це просто контейнер. Коли ви прикріплюєте до нього обробник кліку і використовуєте його для перенаправлення користувачів на новий вигляд, ви просите браузер ставитися до картонної коробки як до дверей. Браузер відмовляється. Як і будь-який інший інструмент, побудований навколо браузера.
Програми екранного доступу не оголошують div як посилання чи кнопку. Вони пропускають його або читають як звичайний текст. Користувач, що навігує за допомогою голосу, не зможе на нього націлитися. Пошуковий робот, скануючи вашу сторінку на наявність доступних URL-адрес, не побачить нічого, за чим можна було б піти. Вашого маршруту для нього просто не існує.
Гірше того, ви втрачаєте поведінку, до якої користувачі вже звикли. Справжнє посилання дозволяє натиснути праву кнопку миші, щоб відкрити його в новій вкладці, додати адресу в закладки або скопіювати її для поширення. Користувачі, які працюють з клавіатури, очікують натиснути Tab, щоб дістатися посилання, і Enter, щоб відкрити його. div не пропонує нічого з цього. Навіть якщо ви прикріпите tabIndex, role="link" та слухачі подій клавіатури, ви будете погано відтворювати те, що браузер надає вам безкоштовно. І ви обов'язково забудете про якийсь граничний випадок. Ви завжди про них забуваєте.
Використовуйте анкори для переходів, кнопки — для дій
HTML уже вирішив цю проблему. Плутанина виникає тому, що обидва елементи виглядають клікабельними, тому розробники сприймають їх як взаємозамінні. Але це не так.
Використовуйте тег <a>, коли хочете перевести користувача на нову URL-адресу. Не імітовану зміну вигляду чи зміну стану, а реальну локацію. Атрибут href має містити справжню адресу:
<a href="/docs">Documentation</a>
Ось і все. Якщо користувач кудись переходить — використовуйте посилання.
Використовуйте <button>, коли на поточній сторінці щось відбувається. Кнопки призначені для таких дій, як:
- Відкриття модального вікна
- Відправлення форми
- Збереження налаштувань
- Перемикання меню
Посилання — для місць призначення. Кнопки — для дій. Змішування цих двох типів заплутує інтерфейс і порушує очікування користувачів.
Дозвольте браузеру робити свою роботу
Сучасні браузери є результатом десятиліть еволюції та стандартизації. Вони обробляють безпеку, історію, попереднє завантаження (prefetching) та доступність краще, ніж будь-який ваш самописний JavaScript.
Справжній тег анкора автоматично наповнює стек історії браузера. Він працює з нативним контекстним меню. Він бере участь у вбудованих алгоритмах попереднього завантаження браузера, коли користувач наводить на нього курсор або фокусується на ньому, що робить ваш додаток швидшим без написання жодного рядка коду. Він поважає налаштування користувача щодо відкриття посилань. Він співпрацює з менеджерами паролів, інструментами перекладу та режимами читання.
Коли ви замінюєте це функцією навігації на JavaScript, ви відмовляєтеся від усього цього. Ви не просто втрачаєте можливості; ви змушуєте користувачів відмовлятися від звичок, які вони виробили на кожному іншому сайті в інтернеті. Це не технічне рішення. Це вороже ставлення до користувацького досвіду.
Перевірте, що насправді рендерить ваш фреймворк
React Router, Vue Router, компоненти Link у Next.js, SvelteKit. Ці інструменти роблять клієнтську маршрутизацію легкою. Але абстракція породжує помилки.
Перевірте свій DOM. Відкрийте інструменти розробника браузера та подивіться на елементи, які генерує ваш фреймворк. Компонент <Link> має рендеритися як справжній тег <a> з валідним атрибутом href у фінальному HTML. Якщо він рендериться як span, div або будь-що інше без належного href, ваша абстракція вас підвела. Виправте компонент. Перевизначте стандартну поведінку. Використовуйте проп passHref або його еквівалент у вашому фреймворку. Не довіряйте фреймворку на слово без перевірки.
Це також важливо для уникнення помилок гідратації (hydration mismatches). Якщо сервер рендерить посилання, а клієнт гідрує його в не-посилання, ви створюєте помилки доступності, які важко відстежити, оскільки в коді джерела HTML виглядає правильно, а в живому DOM — ні.
Забаньте фейкові місця призначення
Існує патерн, який ніяк не вмирає: href="javascript:void(0)". Розробники використовують його, коли хочуть щось, що виглядає як посилання, але поводиться як кнопка — зазвичай тому, що не хочуть стилізувати кнопку або тому, що цього вимагає застарілий код.
Зупиніться. Це не URL-адреса. Вона не дає браузеру жодного пункту призначення. Вона засмічує стек історії некорисними станами. Вона порушує історію браузера та доступність. Це пастка. Якщо вам потрібна поведінка кліку без навігації, вам потрібна кнопка <button>. Стилізуйте її так, як забажаєте. CSS байдуже, чи є елемент кнопкою, чи посиланням. А вашим користувачам — ні.
Пишіть текст, який пояснює, куди переходить користувач
Слова всередині вашого посилання мають значення. Користувачі програм зчитування екрана часто викликають список усіх посилань на сторінці для швидкого перегляду. Якщо всі ваші посилання містять «Читати далі» або «Натисніть тут», цей список перетворюється на марний шум.
Будьте конкретними. Порівняйте ці варіанти:
- Погано:
<a href="/security/api-guide">Читати далі</a> - Добре:
<a href="/security/api-guide">Прочитайте посібник із безпеки API</a>
Другий варіант точно каже користувачеві, що він знайде. Він надає пошуковим системам контекст про сторінку призначення. Він робить ваш список посилань зручним для навігації. Описовий текст посилання — це одна з найпростіших перемог у забезпеченні доступності, яких ви можете досягти.
Тестуйте всерйоз
Архітектура нічого не варта, якщо ви її не перевіряєте.
По-перше, протестуйте навігацію з клавіатури. Від’єднайте мишу. Пройдіть клавішею Tab через кожен інтерактивний елемент на вашому сайті. Кожне справжнє посилання повинно мати видиму рамку фокусу — не ледь помітне сяйво, що зникає на фоні, а чітке кільце, яке помітить навіть втомлене око. Натисніть Enter. Посилання має активуватися. Якщо Tab пропускає елемент або Enter нічого не робить — у вас баг.
По-друге, протестуйте ваші маршрути на рівні сервера. Клієнтський роутинг — це лише тонка оболонка. Якщо користувач додасть /dashboard/reports у закладки і повернеться завтра або натисне оновити сторінку, ваш сервер повинен знати, як віддати цю сторінку. Налаштуйте свій зворотний проксі або серверний фреймворк так, щоб у разі невідомих шляхів він перенаправляв запит на оболонку вашого додатка (application shell) або безпосередньо віддавав правильний HTML. Помилка 404 при оновленні сторінки — це не дрібний баг. Це порушена обіцянка.
JavaScript — це потужний шар
