Розробка мега-меню: реалізація, адаптація, доступність (React, WCAG)
Проблема стандартного меню при великому каталозі
Відзначимо: коли в каталозі більше 50 розділів, а навігація — старий <ul> з випадаючим списком, відвідувач не бачить підкатегорії, клікає навмання, конверсія падає на 20–30%. Стандартний dropdown впирається в обмеження: одна колонка, немає місця для зображень та описів, на тач-пристроях hover-логіка ламається, відсутнє клавіатурне керування. Вихід — мега-меню (mega menu): панель з 4–6 колонками, зображеннями та описами. Воно дає контекст і пришвидшує пошук товару в 2 рази.
Наша команда з 10+ років досвіду у створенні складної навігації реалізувала понад 40 проектів з мега-меню для e-commerce майданчиків. Ми використовуємо сучасний стек: React 18, Next.js 14, TypeScript. У статті розберемо архітектуру компонента, вимоги доступності згідно з WCAG 2.1 Success Criterion 2.4.3 Focus Order та мобільну адаптацію.
Коли звичайний dropdown перестає працювати
Стандартний випадаючий список має жорсткі обмеження:
- одна колонка — неможливо групувати за категоріями;
- немає місця для допоміжного контенту (зображення, описи, промо);
- на тач-пристроях hover-логіка ламається;
- керування з клавіатури відсутнє або реалізоване нашвидку.
Мега-меню вирішує ці проблеми за рахунок іншої моделі розкриття та розмітки. Ми гарантуємо, що ваш проект отримає зручну навігацію.
Архітектура компонента
Типова структура мега-меню складається з трьох шарів:
Тригери верхнього рівня — горизонтальна навігаційна смуга. Кожен пункт — кнопка з aria-expanded та aria-controls. Панель — <div> з aria-labelledby, позиціонована на всю ширину. Всередині — CSS Grid або Flexbox з колонками. Контент — групи посилань, зображення, CTA.
<nav aria-label="Основная навигация"> <ul class="mega-nav"> <li> <button aria-expanded="false" aria-controls="panel-catalog" class="mega-nav__trigger"> Каталог </button> <div id="panel-catalog" class="mega-panel" hidden> <div class="mega-panel__grid"> <section aria-labelledby="group-electronics"> <h3 id="group-electronics">Электроника</h3> <ul> <li><a href="/catalog/phones">Смартфоны</a></li> <li><a href="/catalog/laptops">Ноутбуки</a></li> </ul> </section> </div> </div> </li> </ul> </nav> Реалізація на React
У React-проектах мега-меню зручно керувати через контекст та useReducer. Анімації — через Framer Motion або CSS-трансформації. Ось базовий компонент із закриттям по кліку поза меню та по Escape:
const MegaMenu = () => { const [activePanel, setActivePanel] = useState<string | null>(null); const containerRef = useRef<HTMLElement>(null); useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (containerRef.current && !containerRef.current.contains(e.target as Node)) { setActivePanel(null); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') setActivePanel(null); }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); }, []); return ( <nav ref={containerRef} aria-label="Основная навигация"> {navItems.map((item) => ( <MegaNavItem key={item.id} item={item} isOpen={activePanel === item.id} onToggle={() => setActivePanel(activePanel === item.id ? null : item.id)} /> ))} </nav> ); }; У Next.js з SSR враховуйте, що атрибут hidden повинен оброблятися коректно на сервері, щоб уникнути layout shift при гідрації.
Як забезпечити доступність (WCAG 2.1 AA)?
Це найчастіша проблема в наших проектах — її ігнорують, а дарма. Конкретні вимоги:
| Патерн | Реалізація |
|---|---|
| Відкриття по Enter/Space | onKeyDown з перевіркою `key === 'Enter' |
| Навігація стрілками | roving tabindex або aria-activedescendant |
| Закриття по Escape | глобальний обробник |
| Фокус при закритті | повернення на тригер через triggerRef.current?.focus() |
| Приховування від screen reader | hidden або aria-hidden="true" на закритій панелі |
Бібліотека @radix-ui/react-navigation-menu реалізує більшість цих вимог з коробки і є кращою базою в React-стеку. Вона використовує патерн NavigationMenu.Root + List + Item + Trigger + Content. Наш досвід показує, що це прискорює розробку на 30%.
Як перевірити доступність мега-меню?
Ось покрокова інструкція для тестування:
- Відкрийте меню з клавіатури — натисніть Tab до тригера, потім Enter або Space. Переконайтеся, що панель розкривається і фокус переходить всередину.
- Використовуйте стрілки вправо/вліво для перемикання між тригерами. Панель повинна перемикатися.
- Натисніть Escape — панель закриється, фокус повернеться на тригер.
- Перевірте screen reader (NVDA, VoiceOver): чи оголошується
aria-expanded,aria-controls,aria-labelledby. - Переконайтеся, що закрита панель невидима для screen reader (атрибут
hiddenабоaria-hidden="true").
Що входить в нашу роботу
Передаємо документацію по компоненту, налаштовані доступи до репозиторію, unit-тести для ключових сценаріїв, приклади використання. Навчаємо команду роботі з компонентом. Надаємо підтримку 2 тижні після здачі. Ви отримуєте гарантію, що меню працює в трьох останніх версіях Chrome, Firefox, Safari та Edge.
Мобільна версія
На мобільних пристроях мега-меню трансформується в акордеон. Це окремий компонент, не адаптований десктопний. Breakpoint-логіка в CSS:
@media (max-width: 1024px) { .mega-panel { position: static; display: grid; grid-template-rows: 0fr; overflow: hidden; transition: grid-template-rows 0.3s ease; } .mega-panel[data-open="true"] { grid-template-rows: 1fr; } } Трюк з grid-template-rows: 0fr → 1fr анімує висоту без фіксованого значення — це краще JavaScript-вимірювань у 2 рази за продуктивністю.
Позиціонування панелі
Два підходи:
- Full-width — панель по всій ширині, прив'язана до нижньої межі меню. Часто в e-commerce.
- Flyout — у конкретного тригера. Використовуйте Floating UI для розрахунку позиції та flip-логіки.
Таблиця порівняння:
| Характеристика | Full-width | Flyout |
|---|---|---|
| Займана площа | Уся ширина екрану | Тільки під тригером |
| Підходить для | Понад 20 розділів у структурі | Менше ніж 10 розділів |
| Складність реалізації | Середня | Вища (розрахунок позиції) |
Чому продуктивність важлива?
Панелі містять багато DOM-вузлів. Якщо дані динамічні, застосовуйте ліниве завантаження:
const [mounted, setMounted] = useState(false); const handleOpen = () => { if (!mounted) setMounted(true); setIsOpen(true); }; return ( <div> <button onClick={handleOpen}>Каталог</button> {mounted && <div hidden={!isOpen}>...</div>} </div> ); Технічні деталі анімації
Для прихованих секцій використовуйте content-visibility: auto — це економить ресурси браузера. При відкритті панелі з динамічним контентом завантажуйте дані тільки при першому відкритті (mount-on-interaction).
Строки та вартість
Вартість розраховується індивідуально після оцінки проекту. Орієнтовні строки:
- Статичне мега-меню (фіксовані посилання, мобільний акордеон) — 3–5 днів.
- Динамічне меню з завантаженням з CMS/API, повною доступністю та анімаціями — 7–10 днів.
- Інтеграція в існуючу дизайн-систему з тестами — додати 2–3 дні.
Зв'яжіться з нами для точної оцінки вашого проекту — ми проаналізуємо структуру каталогу та запропонуємо оптимальне рішення. Замовте розробку мега-меню та отримайте консультацію інженера.







