Чипи тегів і панелі фільтрів: обробка переповнення, обгортка, прокрутка, стани вибору

Було корисно?

Чипи тегів здаються нешкідливими, поки не настане день, коли вони з’їдять ваш макет на маленькому екрані, сховають кнопку «Застосувати» і перетворять аналітичну панель на місце злочину з горизонтальною прокруткою. Потім ви отримуєте тикет: «Фільтри не працюють на мобільних». Це завжди «зламано», ніколи «трохи не оптимально».

Це посібник, орієнтований на продакшен: як обирати між обгорткою та прокруткою, як робити переповнення чесно щодо користувача і як реалізувати стани вибору, що не перетворюються на борг доступності.

Що ми насправді будуємо (чипи, теги, панелі фільтрів)

Чипи тегів — це компактні елементи інтерфейсу, що позначають категорію, атрибут або вибраний фільтр. Зазвичай вони містять мітку, іноді іконку, іноді кнопку видалення «x» і майже завжди мають власну думку щодо відступів.

Панелі фільтрів — це контейнери, що містять чипи (і часто інші контролі) для звуження набору даних. Якщо ви створюєте B2B‑програму, ваша панель фільтрів — це міні‑операційна система: вона має пережити довгі мітки, десятки виборів, локалізацію та користувача, який ставить масштаб браузера на 200%, бо цінує свої очі.

Прихований контракт

Чипи — не просто декорації. Вони укладають контракт:

  • Стан: вибрано чи ні має бути однозначно зрозуміло.
  • Ємність: контейнер має впорядковано обробляти N чипів, де N ніколи не дорівнює тому, що ви тестували.
  • Контроль: користувачі повинні мати змогу додавати/видаляти вибори, не втрачаючи контексту.
  • Стабільність: панель не повинна так сильно переломлювати макет, щоб сторінка «стриботіла» і користувач помилково натискав не те.

Опінійований базовий набір

Якщо ви створюєте панель фільтрів для щільного продукту (аналітика, тикетинг, інвентар, безпека), за замовчуванням обирайте:

  • Обгортка на десктопі, коли панель розташована над контентом і вертикального простору багато.
  • Однорядкова горизонтальна прокрутка на мобільних з видимими підказками і запасним контролем «Більше фільтрів».
  • Згорнутий підсумок при великій кількості виборів («Фільтри (12)») плюс окрема панель для редагування.

Короткі факти та історія, які можна використати в аргументах

  • Факт 1: UI‑шаблони «чип» стали популярні в дизайн‑системах середини 2010‑х як орієнтовані на дотик компактні токени — напівкнопка, напівмітка.
  • Факт 2: Горизонтальні скрол‑інтерфейси існували задовго до мобайлу; їх просто не дуже любили, бо колеса миші та трекпади були непослідовні між платформами.
  • Факт 3: Рання адаптивна верстка тяжіла до обгортки, бо патерни CSS‑переповнення були грубими, а інерційна прокрутка на мобільних була нестабільною скрізь.
  • Факт 4: Зростання липких заголовків та вбудованих веб‑в’ю підштовхнуло панелі фільтрів до «завжди присутніх» поверхонь — добре для UX, небезпечно для макета.
  • Факт 5: «Пілли» (закруглені теги) в UI походять з десктопних тулкітів; форма не нова, а от щільність використання — нововведення.
  • Факт 6: Усікання з трикрапкою стало поширеним частково через змінну ширину шрифтів і локалізацію — фікс‑ширинні мітки міфічні.
  • Факт 7: Патерни доступності для «груп кнопок‑перемикачів» дозріли з еволюцією ARIA; чипи часто поводяться як перемикачі, а не як посилання.
  • Факт 8: Коли продукти перейшли на клієнтський рендер, стан фільтрів прив’язали до URL; чипи стали навігаційними артефактами та візуальними одночасно.
  • Факт 9: Чипи стали подіями аналітики («фільтр застосовано»), тому баг UI‑інтерфейсу може перетворитися на баг даних. Ось так керівники кричать про «здоров’я пайплайна».

Цитата (парафраз ідеї): «Надія — не стратегія.» — часто приписують лідерам з надійності/операцій; вживайте як менталітет, а не буквальну цитату.

Ще один факт, який не помістиш на слайд: кількість чипів корелює з організаційним хаосом. Коли команди не можуть узгодити таксономію, вони додають ще один фільтр. Це UI‑версія додавання дисків, бо хтось забув видалити логи.

Прийміть складні рішення: обгортка vs прокрутка vs згортання

Обробка переповнення — це не лише вибір CSS. Це продуктове рішення з CSS‑реалізацією. Вирішіть, який режим відмови ви віддаєте перевагу, коли кількість чипів зросте.

Варіант A: Обгортка (багаторядкова)

Найкраще, коли: чипи другорядні, вертикального простору багато і ви хочете повну видимість міток без жестів.

Витрати: стрибок контенту, зміна макета та «куди поділася таблиця?» коли користувач додає дев’ятий чип.

Оперативна реальність: обгортка взаємодіє з липкими заголовками. Якщо панель збільшується під час липкості, вона може накривати контент або штовхати його дивними способами. Ви отримаєте баги «не можу клікнути перший рядок». Вони будуть праві.

Варіант B: Однорядкова прокрутка (горизонтальна)

Найкраще, коли: вертикальний простір обмежений (мобільні), чипи часто використовуються і ви можете показати чітку підказку про прокрутку.

Витрати: проблеми з виявленням і складна навігація клавіатурою. Також: легко випадково створити пастку прокрутки.

Правило: якщо обираєте прокрутку, обов’язково забезпечте візуальний натяк, що вміст виходить за межі (градієнт‑маска, частково обрізаний чип, кнопки‑стрілки або меню «Більше»).

Варіант C: Згортання (підсумок + панель)

Найкраще, коли: вибір може вирости великим, фільтри складні і потрібна детермінована висота макета.

Витрати: ще один клік, плюс треба зробити панель так, щоб користувач не відчував приниження.

Правило: згортання — це не «сховати вибори». Показуйте підсумок: кількість і, можливо, перші 1–2 чипи як прев’ю.

Мій бажаний пріоритет

  1. Десктоп: обгортка до 2 рядків, потім згортати в «+N ще» або меню переповнення.
  2. Мобіль: однорядкова прокрутка + кнопка «Фільтри», що відкриває повноекранну панель.
  3. Усюди: ніколи не дозволяйте кнопкам «Застосувати/Очистити» бути викинутими за межі екрану.

Жарт №1: Горизонтальна прокрутка — як борг: інколи необхідна, але завжди потребує плану погашення.

Шаблони обробки переповнення (і що ламається)

Шаблон 1: Обгортка з лімітом рядків для міток

Розгортання чипів по рядках просте, доки мітки не стають надто довгими. Правильний хід зазвичай — усікати мітки всередині чипа, а не чипи самі по собі. Користувачі все ще бачать окремі токени; вони просто не бачать повної мітки, доки не відкриють підказку або не довго не натиснуть.

Режим відмови: довгі локалізовані рядки створюють гігантські чипи, що домінують у рядку і штовхають усе інше на наступний рядок. Це не «адаптивно»; це ваш UI, якого булить контент.

Шаблон 2: Обгортка з максимумом рядків + індикатор переповнення

Дозвольте контейнеру обгортатися, але обмежте його (наприклад) двома рядками. Потім покажіть чип або кнопку «+N ще». Це зберігає стабільність макета при збереженні читабельності.

Режим відмови: реалізація «+N ще» шляхом вимірювання ширини DOM при кожному зміненні розміру або оновленні чипа може трясти макет. Якщо робите так, дебаунсіть і не запускайте це при кожному натисканні клавіші в полі пошуку.

Шаблон 3: Прокручуваний контейнер з інерційною прокруткою

Для мобайлу однорядковий контейнер з overflow-x: auto — стандарт. Але «стандарт» ≠ «безпечний». Потрібно гарантувати:

  • прокрутка не блокує вертикальну прокрутку сторінки (важливий touch-action)
  • фокус клавіатури залишається видимим (scrollIntoView при табуванні)
  • є підказка, що вміст виходить за межі

Режим відмови: вкладений регіон прокрутки в липкому заголовку може відчуватися як зламана сторінка. Користувачі намагаються прокрутити сторінку; рядок чипів краде жест.

Шаблон 4: Меню переповнення («Більше»)

Коли чипи представляють багато опцій, розгляньте контроль «Більше», який відкриває меню або шторку з рештою. Це фактично стратегія віртуалізації для вашого UI: тримайте основну лінію швидкою, виносьте рідкісні випадки з критичного шляху.

Режим відмови: якщо в «Більше» є вибрані чипи, але основна панель цього не показує, ви створюєте невидимий стан. Невидимий стан породжує тикети.

Шаблон 5: Адаптивна зміна поведінки (обгортка → прокрутка)

Перемикання поведінки за брейкпоінтом прийнятне, але уникайте «одна розмітка, різна фізика» сюрпризів. Рядок чипів, що обгортається на десктопі, але прокручується на мобайлі, має зберегти стан вибору, семантику клавіш та порядок фокусу. Інакше він виглядає як два різних компоненти, склеєні разом.

Не покладайтесь на «ймовірно не переповниться»

Припускати, що переповнення не станеться — UI‑еквівалент думки, що диски не заповняться. Ви помилитеся, і людина, що фіксує баг, прикріпить скриншот з 47 чипами «Enterprise – North America – West – Secondary».

Стани вибору: UX, a11y і «чому цей чип синій?»

Визначте тип чипа перед стилізацією

«Чип» — це форма. Поведінка важливіша. Поширені типи поведінки чипів:

  • Чипи‑перемикачі (toggle): вибрано/не вибрано; клік перемикає стан.
  • Чипи‑дії: виконують дію (додають рядок фільтра, відкривають діалог).
  • Чипи‑вхідні: представляють введені користувачем значення (наприклад вибрані контакти) і можуть бути видалені.
  • Навігаційні чипи: поводяться як вкладки/посилання (увага: не плутайте з перемикачами).

Стан вибору має відповідати поведінці. Не стилізуйте навігаційний чип як перемикач. Користувачі вивчають патерни. Потім вони вас карають, коли ви їх порушуєте.

Вибрано ≠ активно ≠ фокусовано

Три стани, які часто плутають:

  • Вибрано: належить до поточного набору фільтрів.
  • Активний/натиснутий: зараз клацають/торкають (транзитний).
  • Фокусовано: клавіатурний фокус (повинен бути видимим навіть коли елемент вибраний).

Не «перевикористовуйте» ту саму візуальну обробку для вибраного та фокусу. Саме так користувачі клавіатур втрачають орієнтацію, і QA реєструє баг «клавіатура зламана», який важко відтворити, якщо ви не користуєтеся клавіатурою. А користуватися клавіатурою варто.

Колір — недостатньо

Стан вибору має бути сприйнятним без покладання лише на колір. Практичні варіанти:

  • іконка галочки (з доступною назвою)
  • зміна ваги шрифту (тонко, але корисно)
  • зміна стилю рамки
  • зміна форми (рідше; може спричинити зсув макета)

А ще: тримайте контраст у межах реальності. «Світло‑блакитний на ще світлішому блакитному» виглядає модно і зазнає фіаско у реальності.

Видалення чипа («x») — окрема ціль

Для чипів‑входів, які можна видаляти, кнопка видалення має бути окремим інтерактивним елементом. Інакше ви створюєте неоднозначну поведінку кліка/тапа: ви вибрали чип чи видалили його? Обидва варіанти — погані сюрпризи.

Жарт №2: Якщо ваш стан вибору залежить від рамки шириною 1px — вітаю, ви створили фільтр Шредінгера.

Мобільні реалії: великі пальці, липкі панелі та безпечні зони

Липкі панелі фільтрів: ставтесь до них як до інфраструктури

Липкі заголовки — це персистентний UI. Персистентний UI має бути нудно правильним. Якщо панель чипів липка:

  • обмежте її максимальну висоту (особливо в режимі обгортки)
  • уникайте динамічного зміщення під час прокрутки
  • резервуйте місце, щоб контент не перекривався

Липка, що росте, панель чипів — як лог без ротації: рано чи пізно вона поглине все.

Цілі по влучності та відступи

Чипи потребують адекватного розміру для торкання. Але не бездумно розширюйте відступи; це підвищує висоту рядка і погіршує переповнення. Краще рішення:

  • зберігайте тіло чипа комфортним, але перемістіть густі взаємодії в панель
  • використовуйте єдиний контроль «Фільтри», що відкриває спеціальний лист для складного вибору
  • обмежте мітки в панелі до необхідного мінімуму; повний опис показуйте в панелі

Безпечні області та вирізи

Якщо панель фільтрів розташована біля низу (поширено на мобайлі), поважайте інсети безпечної області. Інакше «Очистити» житиме під індикатором додому. Користувачі виявлять це переважно матюками.

Коли переходити на панель

Моє правило: якщо користувач може розумно вибрати більше ~8 чипів у типовому робочому процесі, «основна панель» має стати підсумком і воротами до панелі. Нехай чипи в панелі працюють як швидкі переключателі для частих фільтрів; довгий хвіст — у панелі.

Доступність і поведінка клавіатури, яка вас не засмутить

Обирайте семантику свідомо

Не дозволяйте бібліотеці компонентів випадково вирішувати семантику. Типові відповідності:

  • Чипи‑перемикачі: кнопки з aria-pressed="true|false" (або семантика чекбокса, якщо це явно список).
  • Група з одиничним вибором: radiogroup + radio можуть підходити, але лише якщо поведінка справді як у радіо.
  • Навігаційні чипи: реальні посилання або вкладки, залежно від заміни контенту.

Що б ви не обрали, тримайте стилі фокусу видимими і послідовними між вибраними/невибраними станами.

Прокручувані ряди чипів і видимість фокусу

Якщо чипи в горизонтально прокручуваному регіоні, користувачі клавіатури повинні все одно бачити фокусований чип. Це означає, що ваш JS має прокручувати фокусований чип у видиму область, коли фокус рухається.

І так, це продакшенна проблема. Прихований індикатор фокусу — як прихований лог помилок: він є, але не допомагає нікому.

Оповіщення про зміни

Коли вибір чипа змінює результати, користувачам з екранними рідерами корисно отримати оповіщення («Результати оновлено»). Тримайте повідомлення коротким. Не описуйте всю вашу план‑запиту.

Не ловіть прокрутку у пастку

Вкладені області прокрутки можуть ловити і дотики, і клавіатуру. Якщо у вас є рядок чипів, що прокручується всередині прокручуваної сторінки, перевірте:

  • жести дотику все ще можуть прокручувати сторінку
  • стрілки поводяться передбачувано в групі чипів
  • Tab рухається вперед логічно і не застрягає в циклі

Продуктивність і надійність: чипи як поверхня продакшену

Чому SRE має хвилюватися

Панелі фільтрів виглядають як фронтендова прикраса, поки ви не підключите їх до живого бекенду і реальної поведінки користувачів:

  • кожен перемикач може викликати API‑запит.
  • кожен вибраний стан може серіалізуватися в URL.
  • кожна мітка чипа може походити від контенту, створеного користувачем.

Тепер ваша панель фільтрів — частково UI, частково будівник запитів, частково генератор ключів кешу. Якщо вона поводиться неправильно, це не «косметика». Це навантаження, затримка і іноді відмова в обслуговуванні.

Дебаунс і пакетні зміни

Для мультивибору уникайте відправки запиту при кожному перемиканні, якщо користувачі зазвичай вибирають кілька поспіль. Варіанти:

  • явна кнопка «Застосувати»
  • автодозастосування з дебаунсом (але з можливістю скасування)
  • пакетні оновлення (застосувати, коли користувач затихає)

З точки зору опсів: пакетування зменшує шторм запитів і стабілізує поведінку кешу.

Слідкуйте за ключами кешу

Чипи часто відображаються в параметрах запиту. Якщо порядок неузгоджений (наприклад, tag=a&tag=b vs tag=b&tag=a), кеші вважають їх різними. Сортуйте вибрані фільтри перед генерацією URL та тіла запиту.

Віртуалізація — не лише для списків

Якщо у вас сотні можливих чипів (поширено в e‑commerce або пошуку логів), не рендеріть їх усіх у горизонтальному списку. Рендеріть невеликий набір і перемістіть решту в панель з пошуком. Це зменшить розмір DOM і зробить макет передбачуванішим.

Три корпоративні міні‑історії з передової

Міні‑історія 1: Інцидент через хибне припущення

Продуктова команда додала «швидкі фільтри» на сторінку звітів. Гарні чипи зверху: Регіон, Сегмент, Статус. Це швидко вийшло в реліз, бо набір даних у стаджингу був малий і всі тестували англійськими мітками.

Потім великий корпоративний клієнт увімкнув набір кастомних тегів зі своєї внутрішньої таксономії. Раптом список чипів виріс з 8 елементів до «стільки, скільки у їхньому орг‑чарті». Мітки були довгі, локалізовані і містили пунктуацію. Панель розгорнулася на п’ять рядків, штовхнула таблицю вниз, і липкий заголовок почав накривати перші рядки.

Приходили звернення у підтримку з описом «дані відсутні», бо верхні рядки буквально були сховані за розширеною липкою панеллю фільтрів. Користувачі не розуміли, що можна прокрутити; UI виглядав ніби зупинився на заголовку. Це не був аутидж, але це був інцидент довіри. Такі події гірші: ніхто не надсилає Pager, але клієнти запам’ятовують.

Хибне припущення було просте: «фільтри не перевищать одного рядка». Виправлення теж було простим, але вимагало дисципліни: обмежити панель двома рядками, додати «+N ще» і перемістити рідкісні фільтри в панель. Також припинити дозволяти липким контейнерам автоматично рости. Липкий означає стабільний.

Міні‑історія 2: Оптимізація, що підвела

Фронтенд‑команда помітила, що вимірювання макета дорогі при зміні розміру. Вони реалізували хитрий алгоритм переповнення чипів: вимірювати ширини чипів, пакувати стільки, скільки вміщається, і ховати решту за чипом «Більше». На їхніх машинах це працювало чудово.

На слабших пристроях і в деяких вбудованих веб‑в’ю вимірювання запускалося повторно під час прокрутки через перерахунок макета від завантаження шрифтів і динамічних змін viewport. Панель чипів стала маленьким DoS проти головного потоку. Прокрутка затримувалася. Вхідні затримки зросли. Користувачі описували: «сторінка зависає, коли намагаюся фільтрувати.»

В моніторингу продакшену бекенд‑латентність виглядала нормально. Вузьким місцем був UI‑потік. Оптимізація провалилася, бо вважала вимірювання DOM детермінованим і дешевим. Це не так. Особливо під час дивних моментів: поворот екрану, заміна шрифтів, масштаб, відкриття клавіатури.

Кінцеве виправлення було нудним: прибрати вимірювання на кожному кадрі, дозволити простішу схему обгортки з максимальними рядками і обчислювати переповнення лише на явні події (змінився список фільтрів, перетнуто брейкпоінт) з дебаунсом. Додали також ручний пункт «Фільтри» для відкриття панелі. Продуктивність покращилась, компонент спростився. Урок: «хитро» і «надійно» рідко опиняються в одному календарі.

Міні‑історія 3: Нудна, але правильна практика, що врятувала день

Інша команда мала звичку: кожен UI‑компонент, що може переповнитися, постачає «torture mode» в стаджинг. Це просто тумблер, що підкидає абсурдно довгі мітки і 50+ чипів. Ніхто цього не любить, але всі користуються ним перед релізом.

Під час редизайну розробник поміняв контейнер фільтрів з CSS grid на flex і прибрав min-width: 0 з дочірнього елемента, бо «виглядало зайвим». При нормальних даних нічого не зламалося.

Тортурний режим одразу показав провал: довгі мітки відмовлялися стискатися, виходили за межі контейнера і створювали горизонтальний скролбар по всій сторінці. Такий баг дорогий, якщо потрапляє в продакшен, бо зазвичай проявляється лише на певному контенті і розмірах вікна.

Вони відновили відсутнє обмеження, додали регресійний скриншот‑тест на типові брейкпоінти і рухнули далі. Жодного інциденту. Жодної драми. Просто тихе задоволення від того, що система поводиться. Нудне — це добре. В опсах і UI нудне — це фіча.

Практичні завдання: 12+ команд для діагностики вузького місця

Це реальні оперативні завдання, які можна виконати, коли панель чипів «здається зламаною» в продакшені. Мета — не гадати. Мета — локалізувати вузьке місце: CSS/макет, JS‑руттайм, мережа/API або вибух запитів на бекенді.

Завдання 1: Перевірте патерни доступу Nginx для перемикань фільтрів

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/access.log
10.0.12.34 - - [29/Dec/2025:14:01:12 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0"
10.0.12.34 - - [29/Dec/2025:14:01:13 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise HTTP/2.0" 200 8451 "-" "Mozilla/5.0"

Що це означає: кожне перемикання чипа викликає запит. Якщо ви бачите кілька послідовних запитів на одну дію користувача, ваш UI може занадто агресивно застосовувати зміни.

Рішення: додайте пакетування (кнопка Застосувати) або дебаунс; також нормалізуйте/сортуйте параметри тегів для роботи кешу.

Завдання 2: Порахуйте частоту запитів на endpoint, щоб виявити штурми

cr0x@server:~$ sudo awk '{print $7}' /var/log/nginx/access.log | cut -d'?' -f1 | sort | uniq -c | sort -nr | head
  8421 /api/search
  1190 /api/filters
   402 /static/app.js

Що це означає: /api/search домінує. Якщо це стрімко зросло після зміни UI, панель фільтрів, ймовірно, підвищила кількість запитів.

Рішення: перевірте поведінку UI (автозастосування) і впровадьте кешування або обмеження частоти за потреби.

Завдання 3: Проаналізуйте кардинальність рядка запиту (вбивця кешу)

cr0x@server:~$ sudo grep -o 'GET /api/search?[^ ]*' /var/log/nginx/access.log | head -n 5
GET /api/search?tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=status%3Aopen&tag=region%3Ana
GET /api/search?tag=region%3Ana&tag=status%3Aopen&sort=desc
GET /api/search?sort=desc&tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise

Що це означає: порядок параметрів варіюється, створюючи кілька ключів кешу для одного логічного запиту.

Рішення: канонізуйте порядок параметрів на клієнті; за потреби канонізуйте серверні редиректи для GET.

Завдання 4: Перевірте співвідношення кеш‑хітів CDN/кешу (якщо є)

cr0x@server:~$ sudo grep -E 'HIT|MISS' /var/log/nginx/access.log | tail -n 10
10.0.10.9 - - [29/Dec/2025:14:02:11 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS
10.0.10.9 - - [29/Dec/2025:14:02:12 +0000] "GET /api/search?tag=status%3Aopen&tag=region%3Ana HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS

Що це означає: miss’и на логічно однакових запитах зазвичай вказують на неканонічні параметри або малий TTL.

Рішення: канонізуйте, і розгляньте кешування результатів пошуку на короткі проміжки, якщо прийнятно.

Завдання 5: Виміряйте розподіл латентності бекенду (не лише середнє)

cr0x@server:~$ sudo awk '{print $NF}' /var/log/nginx/access.log | tail -n 5
rt=0.091
rt=0.104
rt=1.902
rt=0.088
rt=0.095

Що це означає: у вас є хвостова латентність (1.9s), навіть якщо більшість запитів ~100ms. Чиповий UI сильно помітить хвости, бо користувачі часто «б’ють» по ньому.

Рішення: оптимізуйте найгірші запити і розгляньте оптимістичні оновлення UI або індикатори завантаження, які не трусять макет.

Завдання 6: Визначте повільні запити в Postgres (приклад)

cr0x@server:~$ sudo -u postgres psql -c "select calls, mean_exec_time, query from pg_stat_statements order by mean_exec_time desc limit 5;"
 calls | mean_exec_time |                     query
-------+----------------+------------------------------------------------
   412 |        988.123 | select * from events where tags @> $1 ...
  1022 |        212.044 | select distinct tag from events_tags where ...

Що це означає: чип‑керовані фільтри часто трансформуються у запити за тегами; найповільніші патерни видно тут.

Рішення: додайте індекси (GIN для JSONB/масивів тегів), перепишіть запити або поставте дорогі фільтри за кнопкою «Застосувати».

Завдання 7: Перевірте насичення CPU (симптоми на клієнті можуть бути й на сервері)

cr0x@server:~$ top -b -n 1 | head -n 12
top - 14:05:21 up 12 days,  3:18,  1 user,  load average: 5.21, 4.98, 4.10
Tasks: 212 total,   2 running, 210 sleeping,   0 stopped,   0 zombie
%Cpu(s): 86.2 us,  3.1 sy,  0.0 ni, 10.3 id,  0.0 wa,  0.0 hi,  0.4 si,  0.0 st
MiB Mem :  32123.8 total,   1120.4 free,  18002.3 used,  13001.1 buff/cache

Що це означає: CPU високий; якщо це співпадає з активністю фільтрів, бекенд виконує надто багато роботи за перемик.

Рішення: зменшіть частоту запитів і/або оптимізуйте стратегію індексів/запитів.

Завдання 8: Швидко перевірте p95 за допомогою systemd journal (приклад сервісу)

cr0x@server:~$ sudo journalctl -u search-api --since "30 min ago" | grep "request_time=" | tail -n 5
request_time=0.112 path=/api/search
request_time=0.138 path=/api/search
request_time=1.744 path=/api/search
request_time=0.121 path=/api/search
request_time=0.109 path=/api/search

Що це означає: ви бачите сплески; корелюйте їх з конкретними тегами або комбінаціями.

Рішення: додайте метрики запитів, ключовані нормалізованими наборами фільтрів.

Завдання 9: Переконайтесь у gzip/brotli для великих payload фільтрів

cr0x@server:~$ curl -sI -H "Accept-Encoding: gzip" https://app.example.internal/api/filters | grep -i -E "content-encoding|content-length"
Content-Encoding: gzip
Content-Length: 18234

Що це означає: визначення фільтрів може бути важким (мітки, лічильники, метадані). Стиснення важливе.

Рішення: переконайтесь, що стиснення ввімкнено; зменшіть поля у payload для панелі чипів порівняно з повною панеллю.

Завдання 10: Перевірте, що фронтенд‑бандл не роздувся

cr0x@server:~$ ls -lh /srv/www/static/ | grep app
-rw-r--r-- 1 www-data www-data 1.9M Dec 29 13:40 app.js
-rw-r--r-- 1 www-data www-data 255K Dec 29 13:40 app.css

Що це означає: якщо ваш чип‑компонент підхопив величезну залежність (іконки, бібліотеки вимірювання), розмір бандла може погіршити латентність взаємодій.

Рішення: tree‑shake, code‑split панель фільтрів і тримайте саму панель компактною.

Завдання 11: Виявляйте скарги на зсув макета через логи помилок фронтенду

cr0x@server:~$ sudo tail -n 20 /var/log/frontend-errors.log
2025-12-29T14:03:09Z WARN ui layout_shift chipbar_resize loops=34 route=/reports
2025-12-29T14:03:10Z WARN ui long_task 247ms route=/reports action=toggle_chip

Що це означає: повторювані зміни розміру панелі чипів і довгі задачі вказують на тряску на клієнті.

Рішення: приберіть вимірювання в кожному кадрі; зменшіть рефлови; обмежте висоту; уникайте анімацій ширини/висоти.

Завдання 12: Перевірте, що «Очистити фільтри» завжди доступна (синтетична перевірка)

cr0x@server:~$ node /opt/synthetics/check-filterbar.js
PASS route=/reports viewport=390x844 clear_button_visible=true chips_overflow=true
PASS route=/reports viewport=768x1024 clear_button_visible=true chips_overflow=true
FAIL route=/reports viewport=1280x720 clear_button_visible=false chips_overflow=true

Що це означає: при 1280×720 кнопка очистки прихована. Це реальний баг з реальним болем для користувачів.

Рішення: закріпіть кнопки дій, обмежте ширину контейнера чипів або перемістіть дії в фіксовану зону.

Завдання 13: Перевірте сплески HTTP 429/5xx після релізу UI

cr0x@server:~$ sudo awk '$9 ~ /429|500|502|503/ {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
  184 429 /api/search
   27 503 /api/search

Що це означає: ваш UI фільтрів генерує достатньо трафіку, щоб спрацьовували ліміти або перевантаження.

Рішення: відкатити автозастосування, додати дебаунс/пакетування і оптимізувати дорогі запити.

Завдання 14: Підтвердіть, що розмір payload запиту не вибухає (POST фільтри)

cr0x@server:~$ sudo grep "Content-Length" /var/log/nginx/access.log | tail -n 3
"POST /api/search HTTP/2.0" 200 9123 "-" "Mozilla/5.0" cl=842
"POST /api/search HTTP/2.0" 200 9130 "-" "Mozilla/5.0" cl=9421
"POST /api/search HTTP/2.0" 200 9201 "-" "Mozilla/5.0" cl=18822

Що це означає: користувачі можуть вибирати настільки багато чипів, що тіла запитів стають великими. Це підвищує латентність і може стикатися з обмеженнями.

Рішення: обмежте число виборів або перейдіть від «надсилати всі теги» до серверно‑збережених наборів фільтрів.

План швидкої діагностики

Коли хтось каже «панель фільтрів зламана», не сперечайтесь про естетику. Знайдіть вузьке місце швидко.

Перше: визначте клас відмови

  • Помилка макета: чипи перекриваються, дії зникають, дивні скроллбари.
  • Помилка взаємодії: тап не спрацьовує, кнопка видалення помилково активує інше, фокус губиться.
  • Помилка продуктивності: підлагування UI, стагнація, зависання після перемикання.
  • Помилка даних: вибрані чипи не відповідають результатам, невідповідність URL, неправильні лічильники.

Друге: відтворіть під «стресом»

  • малий viewport + великий текст (зум браузера / масштаб шрифтів ОС)
  • довгі мітки (локалізація, теги від користувачів)
  • багато виборів (20+)
  • повільна мережа (імітуйте, якщо можливо)

Третє: визначте куди йде час

  1. Головний потік клієнта: довгі задачі навколо перемикання? Підозрюйте вимірювання макета, перерендери, важкі оновлення стану.
  2. Мережа: кілька запитів на перемик? Підозрюйте автозастосування, відсутність дебаунсу, відсутність пакетування.
  3. Бекенд: хвостова латентність або сплески CPU? Підозрюйте проблеми з індексами/запитами, промахи кешу.

Що перевірити першим/другим/третім (практичний порядок)

  1. Перше: чи область дій (Застосувати/Очистити) стабільна і завжди видима? Якщо ні — виправте обмеження макета перед усім іншим.
  2. Друге: чи одна взаємодія викликає один запит? Якщо ні — виправте дебаунс/пакетування і канонічну серіалізацію URL.
  3. Третє: чи p95 бекенду прийнятний для інтерактивних переключень? Якщо ні — індексуйте/оптимізуйте і розгляньте кнопку «Застосувати».

Типові помилки: симптоми → корінь → виправлення

1) Сторінка отримує горизонтальний скрол після вибору чипів

Симптоми: сторінка прокручується вбік; контент виглядає «зіпсованим».

Корінь: дочірній елемент flex відмовляється стискатися через значення min-width за замовчуванням або довгі мітки не усікаються.

Виправлення: дозволити стиск (min-width: 0 на flex‑елементі, що містить чипи); усікати мітки чипів; уникати фіксованих ширин у вмісті чипа.

2) «Очистити» або «Застосувати» зникає, коли чипи обгортаються

Симптоми: дії переміщуються на новий ряд або викидаються з виду.

Корінь: дії в тому ж обгортальному контейнері, що й чипи, без закріпленої області.

Виправлення: розділіть області макета: зона чипів (обгортка/прокрутка) + фіксована зона дій. Якщо потрібно, дозволяйте чипам переповнюватися, поки дії лишаються закріпленими.

3) Рядок чипів краде прокрутку і ловить користувачів

Симптоми: користувач намагається прокрутити сторінку; рухаються лише чипи; сторінка здається застряглою.

Корінь: горизонтальний регіон прокрутки в липкому заголовку з агресивною обробкою дотику.

Виправлення: забезпечити, щоб вертикальна прокрутка залишалася домінуючою; налаштувати touch‑action; тримати регіон прокрутки невеликим; забезпечити альтернативну панель «Фільтри».

4) Стан вибору неясний або непослідовний

Симптоми: користувачі не розуміють, що застосовано; звернення у підтримку з «неправильними результатами».

Корінь: стилі покладаються на тонкі зміни кольору; стани фокусу і вибору накладаються; вибрані чипи сховані за переповненням.

Виправлення: додати неколірні підказки (галочка/рамка), тримати кільце фокусу відмінним і забезпечити видимість вибраних чипів у підсумку («Фільтри (N)»).

5) Клік по «x» видалення перемикає вибір натомість

Симптоми: чип переключається, коли користувач намагається видалити; випадкові зміни фільтрів.

Корінь: іконка видалення не окрема кнопка або обробка подій не зупиняє поширення.

Виправлення: зробіть видалення окремою кнопкою з власною підписною назвою; зупиніть поширення події; тримайте цілі великі для торкання.

6) Бекенд «плавиться», коли користувачі граються з фільтрами

Симптоми: сплески 429/5xx; CPU зростає; хвостова латентність погіршується.

Корінь: автозастосування викликає запит на кожне перемикання; погане кешування через неканонічні params; дорогі запити за тегами.

Виправлення: дебаунс/пакетування, канонізуйте серіалізацію фільтрів, додайте індекси і розгляньте серверно‑збережені набори фільтрів.

7) Локалізація ламає макет

Симптоми: німецькі або фінські мітки перетворюють чипи на цеглини; усікання не працює.

Корінь: чипи розміром під англійську; немає max‑width; немає усікання; іконки займають забагато місця.

Виправлення: встановіть max‑width для міток, забезпечте роботу ellipsis і тестуйте з псевдо‑локалізацією та довгими рядками.

Контрольні списки / покроковий план

Чекліст: Оберіть стратегію переповнення (не переосмислюйте, але вирішіть)

  • Панель фільтрів липка? Якщо так, обмежте висоту і віддайте перевагу стабільному макету (обгортка макс 2 рядки або прокрутка).
  • Користувачі часто вибирають багато фільтрів? Якщо так, забезпечте панель і покажіть кількість у підсумку.
  • Вертикальний простір цінний (мобільні)? Якщо так, віддайте перевагу однорядковій прокрутці плюс панель.
  • Мітки довгі або локалізовані? Якщо так, вимагається усікання і максимальна ширина мітки.
  • Потрібно «Застосувати»? Якщо вартість бекенду велика — так. Інакше розгляньте дебаунсоване автозастосування.

Чекліст: Реалізуйте стани вибору/фокусу/натискання чисто

  • Стан вибору містить некольорову підказку (іконка, рамка, вага шрифту).
  • Кільце фокусу завжди видно і відрізняється від стилю вибраного.
  • Стан натискання/активності транзитний і не виглядає як вибраний.
  • Кнопка видалення окрема і доступна з клавіатури, де потрібно.
  • ARIA‑атрибути відповідають поведінці (кнопки‑перемикачі використовують aria-pressed).

Покроково: Відправте в реліз стійку панель чипів

  1. Визначте семантику: toggle vs navigation vs input чипи. Запишіть.
  2. Виберіть режим переповнення на брейкпоінт: обгортка на десктопі (макс рядки) і прокрутка на мобільному, або згортання в підсумок.
  3. Зарезервуйте місце для дій: Застосувати/Очистити мають бути закріплені, а не жертвою обгортки.
  4. Обмежте мітки: max‑width + ellipsis; підказка/довгий натиск для повного тексту.
  5. Опрацюйте велику кількість виборів: показуйте кількість і панель для редагування повного набору.
  6. Нормалізуйте серіалізацію фільтрів: сталий порядок для URL‑параметрів і payload.
  7. Контролюйте частоту запитів: дебаунс або кнопка «Застосувати»; скасовуйте поточні запити при зміні.
  8. Тестуйте стрес‑кейси: довгі мітки, 50 чипів, 200% зум, малий viewport, повільна мережа.
  9. Додайте моніторинг: рахунок перемикань за сесію, сплески запитів, довгі задачі UI і p95 бекенду.
  10. Напишіть план відкату: можна feature‑flag автозастосування і відкотити до «Застосувати», якщо витрати на бекенд виростуть.

Поширені запитання

1) Чи мають чипи обгортатися чи прокручуватись за замовчуванням?

Обгортка на десктопі, якщо вертикальний простір прийнятний. Прокрутка на мобільних, якщо ви можете забезпечити очевидну підказку та уникнути пастки прокрутки. Для сценаріїв з великою кількістю виборів краще згортання в підсумок з панеллю.

2) Скільки чипів — це «занадто багато»?

Більше, ніж уміщається зручного, не ховаючи основні дії. Практично: якщо користувачі часто вибирають більше 8–12, основна панель має стати підсумком і воротами до панелі.

3) «+N ще» краще, ніж горизонтальна прокрутка?

Часто так на десктопі, бо це зберігає стабільність сторінки і уникає прихованих виборів. На мобільних горизонтальна прокрутка прийнятна, але все ж розгляньте «Фільтри (N)» як основний контроль.

4) Як уникнути зсуву макета при додаванні/видаленні чипів?

Обмежте висоту контейнера (макс рядки), уникайте анімації висоти і тримайте дії в окремій фіксованій зоні. Якщо анімація потрібна, віддавайте перевагу opacity/transform над height.

5) Чому довгі мітки чипів ламають усікання в flex‑макетах?

Flex‑елементи можуть відмовлятися стискатися, якщо не дозволити. Класичне виправлення — min-width: 0 на flex‑дочці, що містить текст, плюс max‑width на області мітки.

6) Чи мають вибрані чипи бути видаляємими через «x»?

Лише для input‑типів чипів, що представляють значення, які користувач «володіє» (наприклад обрані отримувачі або застосовані фільтри у списку, які можна видалити). Для чипів‑перемикачів достатньо клікати сам чип для зняття вибору. Якщо додаєте «x», він має бути окремою кнопкою.

7) Автозастосування чи кнопка «Застосувати»?

Якщо бекенд‑запити дешеві і ви можете дебаунсити та скасовувати поточні запити, автозастосування працює. Якщо запити дорогі або користувачі вибирають кілька фільтрів підряд, кнопка «Застосувати» — надійний вибір. Також її легше розрулити в інциденті.

8) Як зробити прокручуваний ряд чипів доступним?

Використовуйте правильну семантику кнопок, зберігайте видиме кільце фокусу і забезпечте, щоб фокусований чип прокручувався у видиму область при табуванні. Також надайте альтернативний доступ (панель «Фільтри»), щоб користувачі не були вимушені робити горизонтальну прокрутку.

9) Чи має вибір чипів відображатись у URL?

Так, для можливості поділитися й відновлення стану, особливо в B2B‑інструментах. Канонізуйте порядок параметрів, щоб уникнути фрагментації кешу і плутанини з кнопкою Назад.

10) Який найнадійніший дизайн для фільтрів корпоративного рівня?

Стабільний заголовок з підсумком («Фільтри (N)»), кілька часто вживаних швидких чипів і спеціальна панель фільтрів з пошуком, групуванням і чіткими діями. Передбачуваність краща за хитрість.

Висновок: наступні кроки, які можна відправити в реліз

Чипи тегів і панелі фільтрів ламаються передбачувано: переповнення ховає контролі, стан вибору стає неоднозначним, а «швидкі переключателі» тихо DDoS‑лять ваш бекенд. Нічого містичного тут немає. Це просто нехтування обмеженнями.

Наступні кроки, що дають миттєвий ефект:

  1. Вирішіть стратегію переповнення по брейкпоінтах (обгортка з макс. рядками, прокрутка з підказкою або згортання в підсумок).
  2. Закріпіть контролі дій, щоб вони ніколи не ставали жертвами обгортки.
  3. Зробіть стани вибору/фокусу/натискання відмінними й доступними без покладання лише на колір.
  4. Канонізуйте серіалізацію фільтрів і пакетування запитів, щоб уникнути штормів і промахів кешу.
  5. Додайте режим стрес‑тестування (довгі мітки, багато чипів, великий текст) і запускайте його перед кожним релізом.

Якщо ви зробите ці п’ять речей, ваша панель чипів перестане бути генератором випадкових інцидентів і стане тим, чим мала бути спочатку: нудною, швидкою та надійною.

© 2025

← Попередня
DMARC-звіти: як їх читати та вчасно виявляти підробки
Наступна →
WireGuard VPN за схемою hub-and-spoke для 3 офісів із доступом за ролями

Залишити коментар