Худшие UX-антипаттерны и как их исправить
Худшие UX-антипаттерны, которые до сих пор живут в продакшене: тёмные паттерны, сломанные переключатели и доступность, с исправлениями в коде.

На прошлой неделе я пытался настроить cookie на сайте одной крупной авиакомпании. Кнопка «Принять все» была ярко-синей, её невозможно было не заметить. Ссылка «Управление настройками»? Серый текст, шрифт 11px, спрятанный под абзацем юридической тарабарщины. Когда я наконец её нашёл, открылась страница с 47 отдельными переключателями, все включены по умолчанию, и ни одной кнопки «Отклонить все». Минуту я сидел и кликал переключатели, прежде чем сдался и просто вручную почистил cookie.
Это не единичный случай. Это норма. Плохой UX не просто раздражает, он враждебен. А самое обидное: большинство этих антипаттернов решаются простыми способами, которые требуют меньше времени на реализацию, чем темные паттерны, которые они заменяют. Я больше десяти лет делаю веб-приложения, и мне искренне непонятно, почему мы продолжаем выпускать эту гадость. Так что давайте поговорим о главных злодеях и о том, как с ними покончить.
Тёмные паттерны, которые относятся к пользователям как к простофилям
Тёмные паттерны появляются не случайно. Кто-то сидел на совещании, смотрел на метрики конверсии и решил, что обманывать пользователей — приемлемая бизнес-стратегия. Именно это и бесит: они сделаны намеренно.
Confirmshaming: вина как элемент интерфейса
Вы их наверняка видели. Модальное окно просит подписаться на рассылку. Кнопка согласия говорит «Да, я хочу экономить!», а кнопка отказа: «Нет, спасибо, я предпочитаю платить полную цену». Это и есть confirmshaming, и его полно везде: в интернет-магазинах, онбординге SaaS-сервисов, даже в некоторых инструментах для разработчиков. Работает он на крошечном проценте пользователей, а всех остальных отталкивает.
Исправление до смешного простое: используйте нейтральные формулировки для обоих вариантов.
<!-- BEFORE: Manipulative confirmshaming -->
<div class="modal">
<p>Join 50,000 smart developers!</p>
<button class="btn-primary">Yes, sign me up!</button>
<button class="btn-link text-sm">
No thanks, I don't care about my career
</button>
</div>
<!-- AFTER: Respectful, neutral options -->
<div class="modal">
<p>Get weekly frontend tips — no spam, unsubscribe anytime.</p>
<button class="btn-primary">Subscribe</button>
<button class="btn-secondary">No thanks</button>
</div>
Обратите внимание, что версия «после» также убирает раздутое социальное доказательство и добавляет понятное обещание не слать спам. Уважение вызывает больше доверия, чем манипуляции. Ваша долгосрочная конверсия скажет вам спасибо.
Roach Motel: регистрация в один клик, отмена за семь шагов
Регистрация занимает 30 секунд. Чтобы отменить, нужно пройти путь Настройки > Аккаунт > Подписка > Управление тарифом > Отменить тариф > Расскажите, почему > Вы уверены > Поговорить с удержанием > Всё-таки отменить. Некоторые сервисы до сих пор требуют телефонного звонка. В 2026 году. Чтобы отменить подписку, которую вы оформили одним кликом.
Меня не волнуют ваши метрики удержания. Пользователи, которых держат в ловушке, это не лояльные клиенты, а заложники, которые генерируют чарджбэки и однозвёздочные отзывы. Сделайте отмену ровно такой же простой, как регистрацию.
- Разместите опцию отмены в настройках аккаунта, там, где её ищут люди
- Максимум два шага: «Вы уверены?», затем «Готово, аккаунт отменён»
- Не прячьте кнопку отмены за ссылкой «Связаться с поддержкой»
- Скидка для удержания — пожалуйста, но не делайте её обязательным шагом в процессе
- Отправьте письмо с подтверждением и ссылкой на восстановление, вместо того чтобы отмена ощущалась необратимой
Сломанные переключатели и запутанные элементы управления
Небольшой эксперимент: зайдите в настройки телефона и посчитайте, сколько переключателей вы не можете сразу понять, включены они или выключены. Я подожду.
Неоднозначный переключатель один из самых частых сбоев UI, которые я встречаю на код-ревью. Переключатель должен передавать ровно одно: текущее состояние. Это включено или выключено? И всё. Но разработчики продолжают выпускать переключатели, где оба состояния выглядят почти одинаково, или где цветовая индикация неоднозначна, или где переключатель показывает, что произойдёт дальше, а не что происходит сейчас.
/* BEFORE: Both states look nearly identical */
.toggle {
width: 48px;
height: 24px;
background: #d1d5db;
border-radius: 12px;
cursor: pointer;
}
.toggle.active {
background: #9ca3af; /* barely different from inactive */
}
/* AFTER: Unmistakable state difference + accessible */
.toggle {
width: 48px;
height: 24px;
background: #d1d5db;
border-radius: 12px;
cursor: pointer;
position: relative;
}
.toggle[aria-checked="true"] {
background: #2563eb; /* strong contrast from inactive */
}
.toggle .label {
font-size: 10px;
font-weight: 600;
color: white;
}
.toggle[aria-checked="true"] .label::after { content: "ON"; }
.toggle[aria-checked="false"] .label::after { content: "OFF"; }
Три правила для переключателей, которые не путают людей: разные цвета для каждого состояния (с достаточным контрастом, чтобы работать для пользователей с дальтонизмом), текстовая метка внутри переключателя или рядом с ним и корректные атрибуты aria-checked, чтобы скринридеры могли озвучить состояние. Если вы полагаетесь только на цвет, вы одним движением провалили и UX, и доступность.
Хватит изобретать нативные элементы форм заново
Я просматриваю много PR с фронтендом, и один паттерн выводит меня из себя: самописные выпадающие списки, которые ломают навигацию с клавиатуры. Разработчик тратит два дня, собирая навороченное меню select с нуля из кучи div и обработчиков кликов. В демо всё выглядит отлично. Потом пользователь пытается пройтись по форме клавишей Tab, доходит до кастомного списка, и ничего не происходит. Нет поддержки стрелок. Нет поиска по первым буквам. Не работает с менеджерами паролей. Ломается на мобильных.
Тем временем нативный элемент <select> или хорошо протестированная библиотека вроде Radix UI или Headless UI делают всё это бесплатно. HTML popover API и элемент <selectlist> сейчас имеют хорошую поддержку в браузерах. Используйте их. Пользователям всё равно, что у вашего выпадающего списка есть кастомная анимация. Им важно, чтобы он работал.
Антипаттерны доступности, которые отсекают реальных пользователей
Доступность не приятное дополнение. Это не фича, которую добавляют в какой-нибудь будущий спринт. По всему миру более миллиарда человек живут с той или иной формой инвалидности, а отчёт WebAIM Million неизменно показывает, что у 96% и более из миллиона самых популярных сайтов есть обнаруживаемые нарушения WCAG. Это не экзотические крайние случаи, а сломанные кнопки, отсутствующие подписи и изображения без alt-текста.
Вот что я вижу чаще всего:
- Изображения без alt-текста: скринридер просто говорит «изображение» и идёт дальше
- Коэффициент контрастности ниже 4.5:1: текст становится нечитаемым для людей с плохим зрением
- Нет навигации с клавиатуры: если нельзя пройтись по приложению клавишей Tab, пользователи с клавиатурой и переключающими устройствами оказываются заблокированы
- Ловушки фокуса в модальных окнах: пользователь открывает диалог и не может из него выйти без мыши
- Отсутствуют подписи полей форм: скринридер не понимает, для чего нужно поле ввода
- Автовоспроизводимое видео без кнопки паузы: дезориентирует пользователей с вестибулярными расстройствами
Самая быстрая победа добавить автоматические проверки доступности в ваш CI-пайплайн. Они поймают не всё: автоматические инструменты находят, наверное, 30–40% проблем, но отловят очевидные вещи до выпуска.
// BEFORE: No accessibility testing at all
test('homepage loads', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
});
// AFTER: Accessibility violations fail the build
import AxeBuilder from '@axe-core/playwright';
test('homepage loads and is accessible', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toBeVisible();
const a11yResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(a11yResults.violations).toEqual([]);
});
Пять минут настройки. Запускается на каждом PR. Автоматически находит отсутствующий alt-текст, сломанные роли ARIA, недостаточный контраст и десятки других ошибок. Оправданий, почему этого не сделать, нет.
Когнитивная перегрузка: страница настроек из ада
Рабочая память человека удерживает около четырёх–семи элементов одновременно. Это не рекомендация, а жёсткое когнитивное ограничение. Когда интерфейс вываливает 200 опций на одну страницу без иерархии, вы даёте пользователям не контроль, а паралич выбора.
Однажды я пересчитал настройки в системе управления проектами, которой мы пользовались на работе. 347 опций. Одна страница. Без поиска. Группировка не шла дальше расплывчатых категорий «Общие» и «Дополнительно». Половина команды ни разу не поменяла ни одной настройки, потому что страница была настолько подавляющей, что её сразу закрывали.
Решение прогрессивное раскрытие. Показывайте пять настроек, которые люди реально меняют. Всё остальное спрячьте в чётко подписанные раскрывающиеся секции. Добавьте строку поиска. И ради всего святого, задавайте разумные значения по умолчанию, чтобы большинству пользователей вообще не нужна была страница настроек.
Если для вашей страницы настроек нужна собственная документация, проблема именно в ней.
Перегрузка уведомлениями приучает пользователей игнорировать всё
Когда каждое событие порождает уведомление (коллега присоединился к каналу, кто-то поставил эмодзи-реакцию, деплой завершился, через 30 минут начинается событие в календаре), пользователи учатся игнорировать всё подряд. Ваша система уведомлений превращается в белый шум. Единственное критичное оповещение, которое действительно важно? Оно погребено под 47 непрочитанными уведомлениями.
Ответ консервативные значения по умолчанию. Новым пользователям стоит присылать только критичные уведомления. Некритичное собирайте в ежедневную сводку. И всегда, всегда давайте пользователям отключать или настраивать уведомления прямо из самого уведомления, а не с какой-нибудь спрятанной страницы настроек в трёх кликах от него.
Медленный UI это сломанный UI: производительность как проблема UX
Кнопка, которая реагирует две секунды, это не медленная кнопка. Она сломана. Пользователи не думают «ага, сервер обрабатывает запрос». Они думают «а мой клик вообще засчитался?» и кликают снова. Теперь у вас дублирующиеся отправки, запутанное состояние и пользователь, который не доверяет вашему приложению.
Всё, что дольше 100 мс, ощущается как лаги. Всё, что дольше секунды, разрушает ход мысли пользователя. Тем не менее мы продолжаем выпускать многомегабайтные JavaScript-бандлы, блокирующие рендер сторонние скрипты и сдвиги макета, из-за которых люди кликают не по тому элементу. Решение не сложное, нужно просто об этом заботиться.
// BEFORE: User clicks, waits 2 seconds, nothing happens
async function handleLike(postId: string) {
const result = await fetch(`/api/posts/${postId}/like`, {
method: 'POST',
});
if (result.ok) {
setLikeCount(prev => prev + 1); // UI updates after network round-trip
}
}
// AFTER: Optimistic update — UI responds instantly
function handleLike(postId: string) {
// Update UI immediately
setLikeCount(prev => prev + 1);
// Sync with server in background
startTransition(async () => {
const result = await fetch(`/api/posts/${postId}/like`, {
method: 'POST',
});
if (!result.ok) {
// Rollback only on failure
setLikeCount(prev => prev - 1);
toast.error('Something went wrong. Try again.');
}
});
}
Оптимистичные обновления, скелетоны вместо спиннеров, ленивая загрузка некритичного JavaScript и мониторинг Core Web Vitals как реальных метрик, а не запоздалой мысли. Это не продвинутые техники. Это базовые ожидания.
Мобильные UX-антипаттерны, которые никак не умрут
У мобильных устройств есть своя особая категория UX-грехов, в основном потому, что разработчики тестируют всё на новеньком iPhone и на этом успокаиваются.
Зоны касания, требующие хирургической точности
Рекомендации Apple говорят о минимуме 44×44 CSS-пикселя для зон касания. WCAG 2.2 с этим согласен. Тем не менее я всё ещё вижу кнопки закрытия 20 пикселей в модальных окнах, мелкие ссылки, слепленные вместе в подвалах, и иконки, в которые на телефоне практически невозможно попасть. Для людей с нарушениями моторики это ещё хуже.
/* BEFORE: Tiny, frustrating touch targets */
.close-btn {
width: 20px;
height: 20px;
font-size: 12px;
}
.footer-links a {
padding: 2px 4px;
font-size: 11px;
}
/* AFTER: Actually tappable elements */
.close-btn {
min-width: 44px;
min-height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
.footer-links a {
padding: 12px 8px;
font-size: 14px;
/* spacing between adjacent targets */
margin: 4px 0;
}
Полноэкранные интерстициалы, которые блокируют контент
Вы тапаете по результату поиска. Страница загружается. Прежде чем вы успеваете прочитать хоть слово, полноэкранное окно требует ваш email. Вы даже не видели контента. С чего бы вам подписываться?
Google с 2017 года понижает в выдаче навязчивые интерстициалы. Их по-прежнему полно, потому что кто-то где-то смотрит на дашборд, где конверсия в подписку 2%, и считает это победой, игнорируя при этом 40% отказов, которые это создаёт. Используйте встроенные баннеры или нижние шторки. Подождите, пока пользователь действительно взаимодействовал с контентом, прежде чем что-то просить. Читатель, который прочитал три статьи, подпишется добровольно. Читатель, которого прерывали три раза, уйдёт и больше не вернётся.
Как построить культуру качества UX (а не просто чинить отдельные баги)
Исправлять антипаттерны по одному необходимо, но недостаточно. Если ваш процесс снова и снова их порождает, нужен лучший процесс. Вот что реально работает:
- Добавьте axe-core в CI-пайплайн: автоматические проверки a11y на каждом PR
- Сделайте UX-ревью частью код-ревью, а не отдельным процессом только для дизайнеров
- Тестируйте на 5 реальных пользователях перед выпуском крупных фич: 5 пользователей выявляют около 85% проблем юзабилити
- Отслеживайте процент завершения задач и частоту ошибок, а не только просмотры страниц и время на сайте
- Используйте дизайн-систему с готовыми доступными компонентами, чтобы разработчики не решали одни и те же задачи взаимодействия с нуля
- Сами пользуйтесь своим продуктом постоянно: когда разработчик, который написал страницу настроек, пользуется ею каждый день, её быстро починят
Самое полезное улучшение UX в моей практике началось с того, что я попытался пройти наш собственный процесс оформления заказа на телефоне и в 23:00 написал в продуктовый канал гневное сообщение.
У каждого антипаттерна из этой статьи есть прямолинейное решение. Ни для одного из них не нужны передовые технологии или масштабные редизайны. Нужно просто начать заботиться. Сделайте одно исправление сегодня. На этой неделе проведите один аудит доступности. В этом месяце протестируйте с одним реальным пользователем. Хороший UX это не грандиозные жесты, а последовательный выбор уважения к пользователям вместо краткосрочных метрик. Разрыв между софтом, который люди терпят, и софтом, который они любят, меньше, чем вы думаете. Это всего лишь тысяча мелких решений, принятых правильно.


