Реалізація Mega Menu (мега-меню) на сайті
Мега-меню — навігаційний компонент, що розкриває багатоколоночну панель з групуванням посилань, зображеннями, описами й додатковими елементами управління. Застосовується на сайтах з глибокою структурою розділів: інтернет-магазини, портали, корпоративні сайти з кількома продуктовими лініями. Стандартний dropdown-список стає вузьким місцем, коли розділів більше 20 й користувачу потрібен контекст разом з навігацією.
Коли стандартне меню перестає справляться
Звичайний выпадаючий список (<ul> з position: absolute) має жорсткі обмеження:
- одна колонка — неможливо групувати по категоріях
- немає місця для допоміжного контенту (зображення, описи, промо-блоки)
- на touch-пристроях hover-логіка ломається
- управління з клавіатури або відсутнє, або реалізовано наспіх
Мега-меню вирішує ці проблеми через іншу модель розкриття й розмітку.
Архітектура компонента
Типова структура мега-меню складається з трьох шарів:
Тригери верхнього рівня — горизонтальна навігаційна полоса. Кожен пункт — це або посилання, або кнопка, що розкриває панель. З точки зору семантики: <button aria-expanded="false" aria-controls="menu-catalog">.
Панель — <div role="dialog"> або просто <div> з aria-labelledby, абсолютно або фіксовано позиціонована, займаюча всю ширину контейнера (або viewport). Всередину — CSS Grid або Flexbox з кількома колонками.
Контент всередину панелі — групи посилань з заголовками, featured-блоки, зображення, CTA-кнопки. Структуруються через <nav> + <ul> всередину іменованих секцій.
<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-проектах мега-меню зазвичай управляється через контекст або локальний state з useReducer. Анімації відкриття/закриття — через Framer Motion або CSS-трансформації.
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);
}, []);
// Закриття по Escape
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 | глобальний обробник або через FocusTrap |
| Фокус при закритті | повернення на тригер через triggerRef.current?.focus() |
| Приховання від screen reader | hidden або aria-hidden="true" на закритій панелі |
Бібліотека @radix-ui/react-navigation-menu реалізує більшість цих вимог з коробки й є кращою основою для кастомного мега-меню в React-стеку. Вона використовує паттерн NavigationMenu.Root + NavigationMenu.List + NavigationMenu.Item + NavigationMenu.Trigger + NavigationMenu.Content.
Мобільна версія
Мега-меню на мобільних пристроях трансформується в аккордеон або drawer-панель. Це інший компонент, не просто адаптований десктопний. 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-вимірювання висоти.
Позиціонування панелі
Два основних підходи:
Full-width — панель розтягується на всю ширину сторінки, прив'язується до нижної границі навігаційної полоси. Використовується в більшості e-commerce сайтів. Реалізується через position: fixed з top: <navbar-height> або через position: absolute на обгортці з overflow: visible.
Flyout — панель позиціонується відносно конкретного тригера. Підходить для меню з невеликою кількістю груп. Вимагає розрахунку позиції через getBoundingClientRect() для коректної поведінки у куті екрана (flip-логіка, як у Floating UI / Popper.js).
Продуктивність
Панелі мега-меню містять багато DOM-вузлів. Якщо контент завантажується з API (наприклад, динамічні категорії каталогу), важливо:
- рендерити панелі ліниво — лише після першого відкриття (
mountedPanels: Set<string>) - використовувати
content-visibility: autoдля скритих секцій - обмежити кількість одночасно монтованих панелей
const [mounted, setMounted] = useState(false);
const handleOpen = () => {
if (!mounted) setMounted(true);
setIsOpen(true);
};
return (
<div>
<button onClick={handleOpen}>Каталог</button>
{mounted && (
<div hidden={!isOpen} className="mega-panel">
<CatalogPanelContent />
</div>
)}
</div>
);
Типові терміни реалізації
- Статичне мега-меню (фіксовані посилання, без API) з мобільним аккордеоном — 3–5 днів
- Динамічне меню з завантаженням категорій з CMS/API + full accessibility + анімації — 7–10 днів
- Інтеграція у існуючий дизайн-системний компонент з unit/a11y тестами — додати 2–3 дні







