Обробка помилок та UX-пояснення
1) Навіщо це важливо
Помилка - це не «червоний текст», а продовження сценарію. Хороший UX помилок:- пояснює що сталося і що робити далі,
- зберігає введені дані та запобігає втраті прогресу,
- дає безпечний повтор або альтернативний шлях,
- залишається доступним (SR/клавіатура) і не розкриває зайвого.
2) Типологія помилок (для інтерфейсу)
1. Валідація даних (4xx client): порожні/невірні поля, формат, довжина, конфлікт правил.
2. Бізнес-правила: ліміти, гео-обмеження, KYC/KYB, дублі, недоступні слоти.
3. Права/пермісії: роль, доступ до ресурсу, вікові обмеження.
4. Мережа/сервер: таймаут, оффлайн, 5xx, перевантаження, rate limit.
5. Конфлікти/стан: 409/412 (дані змінилися), гонки, блокування.
6. Відсутність ресурсу: 404/410, видалено/перенесено.
7. Платіжні та ризикові: відхилення банком/PSP, антифрод, ліміти відповідальної гри.
3) Канали і рівень відображення
Підбираємо «гучність» під контекст:Правило: не ховайте критичне в тости/hover. Там, де користувач дивиться, там і повідомлення.
4) Копірайтинг помилок
Структура: причина → наслідок → дія.
Тон: чесний, нейтральний, без провини.
Конкретика: вкажіть поле/умову, уникайте кодів і стеків.
Кнопка-дія: «Повторити», «Змінити карту», «Скинути фільтри», «Відкрити чат».
Чутливі дані: не показувати (маскування PAN, особисті атрибути).
Приклади
Добре: "Платіж не пройшов: банк відхилив операцію. Спробуйте інший спосіб або повторіть пізніше".
Погано: "Помилка 500. Щось пішло не так".
Добре: "Ліміт денних витрат досягнуто. Встановіть новий ліміт або спробуйте завтра".
Добре: "Файл занадто великий (макс. 25 МБ). Спаліть або завантажте кілька файлів".
5) Поведінка і фокус (A11y)
Помилка виводиться у фокусний контекст: перекладаємо фокус до першого помилкового поля.
Живі регіони: 'role = «status»'( polite) для інфо,'role = «alert»'( assertive) - для критичних.
Видиме':focus-visible', контраст ≥ AA, альтернативи кольору (іконка/текст).
Повідомлення прив'язуємо до поля через'aria-describedby'.
html
<label for = "pwd "> Password </label>
<input id="pwd" name="password" aria-describedby="pwd-err" aria-invalid="true">
<p id = "pwd-err" role = "alert"> Minimum 8 characters </p>
6) Ретраї, backoff і ідемпотентність
Повтор пропонується, якщо шанс успіху є (мережеві збої, 5xx, rate limit).
Експоненціальний backoff 1-2-4-8 с, ліміт спроб, зрозуміла кнопка «Повторити».
Критичні операції (ставки/платежі): обов'язковий Idempotency-Key → виключаємо дублі.
Відкати оптимістичних оновлень - чітке візуальне повернення + пояснення.
js async function retry(fn, attempts=3){
let wait=1000; for(let i=0; i<attempts; i++){
try{ return await fn(); }catch(e){ if(i===attempts-1) throw e; await new Promise(r=>setTimeout(r,wait)); wait=2; }
}
}
7) Оффлайн, таймаути і частковий контент
Оффлайн: показуємо банер «Немає підключення», доступ до кешу (read-only), чергу синхронізації.
Таймаути: UI-таймаут (3-5 с) → стан «Очікуємо підтвердження»... з безпечним повтором/скасуванням.
Частковий успіх: зберігаємо те, що вдалося; маркуємо «не синхронізовано».
8) Конфлікти і конкурентність
409/412: дані застаріли. Запропонувати «Оновити» і показати дифф (що змінилося).
Блокування: інформуємо, хто тримає блок, і скільки часу, кнопка «Запросити доступ».
9) Зразки UI-шаблонів
Банер сторінки:html
<div class="banner banner--error" role="alert">
<strong> Connection failed. </strong> Shows cached data.
<button class =" btn btn--ghost" id = "retry "> Retry </button>
</div>
Модалка критичної помилки:
html
<div role="alertdialog" aria-labelledby="err-title" aria-describedby="err-desc">
<h2 id = "err-title "> Session expired </h2>
<p id = "err-desc "> Sign in again to continue. </p>
<button class = "btn "> Sign in </button>
<button class =" btn btn--ghost"> Home </button>
</div>
React ErrorBoundary (з ID кореляції):
tsx function Fallback({ id, onRetry }: { id: string; onRetry: ()=>void }) {
return (
<div role="alert" className="banner banner--error">
<strong> We couldn't load the page. </strong>
<div> Try again. Код: <code>{id}</code> <button onClick={()=>navigator. clipboard. writeText (id)}> Copy </button> </div>
<button onClick = {onRetry}> Retry </button>
</div>
);
}
10) Токени помилок (дизайн-система)
json
{
"error": {
"tones": { "danger": "#", "warning": "#", "info": "#" },
"aria": { "polite": true, "assertive": true },
"timing": { "toastMs": 3500, "retryBackoffMs": [1000,2000,4000] },
"layout": { "fieldGap": 8, "bannerIcon": 20 }
}
}
CSS-пресети:
css
.banner--error { background: var(--bg-danger); color: var(--on-danger); padding: 12px 16px; border-radius: 12px; }
.field-error { color: var(--role-danger); margin-top: 6px; font-size:.875rem; }
11) Безпека і приватність
Не виводимо стек-трейси, внутрішні ID, шляхи БД.
Маскуємо чутливі значення (карти, документи).
Повідомлення не повинні підказувати зловмиснику (наприклад, що аккаунт існує).
Для підтримки - ID кореляції замість деталей.
json
{"level":"error","event":"payment_fail","correlation_id":"c-8f1...","user_id":"u-","route":"/pay","psp_code":"DO_NOT_EXPOSE_TO_USER"}
12) Метрики та контроль
INP і частка Long Tasks в момент помилки (помилка не повинна «вішати» UI).
Retry success rate, помилки на 1000 дій, час до відновлення.
CTR на «Допомога/Чат», відсоток кинутих форм.
Теплові карти: де найчастіше виникають field-errors.
13) QA-чек-лист
Доступність
- Фокус на першому помилковому полі;'aria-describedby '/' aria-invalid'виставлені.
- Критичні повідомлення -'role = «alert»'; контраст ≥ AA.
Поведінка
- Дані форми не втрачаються при помилці.
- Є зрозумілий'Retry'і коректний backoff.
- Оффлайн-режим/кеш працюють; банер бачимо.
Копірайтинг
- Причина → дія; без технічного жаргону і звинувачень.
- Тексти локалізуються і не ламають сітку.
Безпека
- Немає витоку PII/секретів; показуємо тільки безпечні коди/ID.
- Ідемпотентність включена для критичних операцій.
14) Специфіка iGaming
Ставка:- UI відразу фіксує'busy'; при затримці> 3 с - «Очікуємо підтвердження»....
- При fail: чесний статус («ринок закрився», «коефіцієнт змінився») + безпечний'Retry'.
- Ідемпотентний ключ, щоб виключити подвійну ставку.
- Розрізняємо «відмова банку/PSP» vs «збій сервера». Для першого - «Виберіть інший спосіб», для другого - «Retry».
- Прозорі кроки KYC/AML; посилання "Чому це потрібно? ».
- Тон турботливий, без тиску. «Ліміт досягнуто - зробіть паузу або оновіть ліміт».
- Без спалахів/неону; контраст AAA, доступність при SR.
- Чітко поясніть обмеження і запропонуйте «Ознайомитися з правилами/підтримка».
15) Анти-патерни
«Щось пішло не так» без дій і контексту.
Скидання форми після помилки.
Ховати критичне в тост на 3 секунди.
Тільки колір без тексту/іконки.
Нескінченні ретраї без можливості скасування.
Показувати внутрішні коди/стек-трейси.
16) Документація в дизайн-системі
Компоненти: `FieldError`, `FormError`, `PageBanner`, `AlertDialog`, `ErrorBoundary`.
Токени тонів/контрасту/таймінгів, пресети a11y і приклади ARIA.
Карта типових сценаріїв (валідація, мережа, права, платежі) з текстовими шаблонами.
«Do/Don’t»: реальні екрани до/після з метриками відмов/успіхів.
Коротке резюме
Робіть помилки зрозумілими і керованими: говоріть людською мовою, зберігайте введені дані, пропонуйте безпечний повтор і альтернативи, поважайте доступність і приватність. Тоді навіть позаштатні ситуації зберігають довіру і не переривають шлях користувача - особливо в критичних сценаріях ставок і платежів.