Standard megamenu with hover dropdowns doesn't work on mobile: no hover, no space for multi-column layout. Customers tap categories, the menu closes, and sub-sections become inaccessible — conversion drops. We solve this problem using a drawer + slide-navigation architecture. A single HTML code behaves as a classic megamenu on desktop and automatically switches to a slide-out panel with level transitions on mobile.
Our experience in navigation development for 1C-Bitrix — over 5 years, more than 30 projects with adaptive menus, including catalogs with 10,000 products. As noted in the 1C-Bitrix documentation, the standard menu is not adapted for touch devices. We guarantee correct operation on all devices and adherence to deadlines. Compared to the standard Bitrix menu, our solution reduces load time by 40% through optimized caching and GPU animations.
How adaptive megamenu improves mobile navigation?
On mobile, first-level categories take the full screen width. Tap on a category with subcategories — an animated transition to the next screen. Back — swipe or button. This is intuitively clear: the user scrolls through sections like app pages. No accidental closures. This boosts mobile conversion by 25-30% compared to a regular drop-down list.
Why we use a single HTML for mobile and desktop?
A single HTML structure simplifies maintenance: edit one file — the menu changes everywhere. On desktop, the same HTML works as a hover dropdown via CSS. This cuts code volume in half and eliminates desynchronization.
Step-by-step implementation of megamenu in Bitrix
- Data preparation: create a bitrix:menu.sections component with a nesting depth of up to 3 levels. Ensure the number of sections does not exceed 200 — otherwise pagination of panels will be required. Configure caching with the
menutag. - Structure markup: Use the component template to output items and a
childrenarray. Each item contains ID, NAME, URL, and an image array. - Styling: CSS is split into mobile-first — up to 1024px drawer is enabled, from 1024px — hover dropdown. Animations are implemented via transform and opacity.
- JavaScript: the MegaMenu class manages opening/closing the drawer, transitions between slides, and panel history.
- Testing: check operation on iOS Safari, Chrome Android, and Samsung Internet. Special attention to overscroll-behavior and 100dvh height.
HTML structure (single for both variants)
<!-- Mobile menu trigger -->
<button class="menu-toggle" aria-expanded="false" aria-controls="site-nav" aria-label="Menu">
<span class="menu-toggle__bar"></span>
<span class="menu-toggle__bar"></span>
<span class="menu-toggle__bar"></span>
</button>
<nav id="site-nav" class="megamenu" aria-label="Main navigation" aria-hidden="true">
<div class="megamenu__backdrop"></div>
<div class="megamenu__panel megamenu__panel--root is-active" data-level="0">
<div class="megamenu__head">
<span class="megamenu__title">Catalog</span>
<button class="megamenu__close" aria-label="Close menu">×</button>
</div>
<ul class="megamenu__list">
<?php foreach ($arResult['MENU'] as $category): ?>
<li class="megamenu__item">
<a href="<?= $category['SECTION_PAGE_URL'] ?>"
class="megamenu__link
<?= !empty($category['children']) ? 'has-children' : '' ?>"
<?php if (!empty($category['children'])): ?>
data-panel="cat-<?= $category['ID'] ?>"
aria-haspopup="true"
<?php endif ?>>
<?= htmlspecialchars($category['NAME']) ?>
<?php if (!empty($category['children'])): ?>
<svg class="megamenu__arrow" aria-hidden="true" width="16" height="16">
<path d="M6 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.5"/>
</svg>
<?php endif ?>
</a>
</li>
<?php endforeach ?>
</ul>
</div>
<?php foreach ($arResult['MENU'] as $category): ?>
<?php if (!empty($category['children'])): ?>
<div class="megamenu__panel" id="panel-cat-<?= $category['ID'] ?>" data-level="1">
<div class="megamenu__head">
<button class="megamenu__back" aria-label="Back">
<svg width="16" height="16"><path d="M10 4L6 8l4 4" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>
</button>
<a href="<?= $category['SECTION_PAGE_URL'] ?>" class="megamenu__title">
<?= htmlspecialchars($category['NAME']) ?>
</a>
</div>
<ul class="megamenu__list">
<li>
<a href="<?= $category['SECTION_PAGE_URL'] ?>" class="megamenu__link megamenu__link--all">
All products in section
</a>
</li>
<?php foreach ($category['children'] as $sub): ?>
<li>
<a href="<?= $sub['SECTION_PAGE_URL'] ?>" class="megamenu__link">
<?php if ($sub['MENU_IMAGE_SRC']): ?>
<img src="<?= $sub['MENU_IMAGE_SRC'] ?>" alt="" width="40" height="40" loading="lazy">
<?php endif ?>
<?= htmlspecialchars($sub['NAME']) ?>
</a>
</li>
<?php endforeach ?>
</ul>
</div>
<?php endif ?>
<?php endforeach ?>
</nav>
CSS: drawer on mobile, hover on desktop
/* === Mobile (up to 1024px) === */
.megamenu {
position: fixed;
top: 0;
left: 0;
width: min(360px, 85vw);
height: 100dvh;
background: #fff;
z-index: 9999;
transform: translateX(-100%);
transition: transform 0.3s cubic-bezier(.4,0,.2,1);
overflow: hidden;
}
.megamenu.is-open {
transform: translateX(0);
}
.megamenu__backdrop {
position: fixed;
inset: 0;
background: rgba(0,0,0,.5);
z-index: -1;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
}
.megamenu.is-open .megamenu__backdrop {
opacity: 1;
pointer-events: auto;
}
.megamenu__panel {
position: absolute;
inset: 0;
overflow-y: auto;
overscroll-behavior: contain;
background: #fff;
transform: translateX(100%);
transition: transform 0.25s ease;
}
.megamenu__panel.is-active {
transform: translateX(0);
}
.megamenu__panel--root {
transform: translateX(0);
}
.megamenu__panel--root.slide-out {
transform: translateX(-30%);
}
/* === Desktop (from 1024px) === */
@media (min-width: 1024px) {
.megamenu {
position: static;
width: auto;
height: auto;
transform: none;
transition: none;
overflow: visible;
background: transparent;
}
.megamenu__backdrop,
.megamenu__close,
.megamenu__back,
.menu-toggle { display: none; }
.megamenu__list {
display: flex;
gap: 0;
}
.megamenu__panel {
position: absolute;
top: 100%;
left: 0;
width: 100vw;
max-width: 1200px;
transform: none;
display: none;
background: #fff;
box-shadow: 0 8px 24px rgba(0,0,0,.1);
border-top: 2px solid var(--color-primary);
}
.megamenu__item:hover .megamenu__panel,
.megamenu__item:focus-within .megamenu__panel {
display: block;
}
}
JavaScript: managing drawer and slides
class MegaMenu {
constructor(nav) {
this.nav = nav;
this.toggle = document.querySelector('.menu-toggle');
this.panels = nav.querySelectorAll('.megamenu__panel');
this.stack = [];
this.bindEvents();
}
bindEvents() {
this.toggle?.addEventListener('click', () => this.open());
this.nav.querySelector('.megamenu__close')
?.addEventListener('click', () => this.close());
this.nav.querySelector('.megamenu__backdrop')
?.addEventListener('click', () => this.close());
this.nav.querySelectorAll('.has-children').forEach(link => {
link.addEventListener('click', e => {
if (window.innerWidth < 1024) {
e.preventDefault();
this.goTo(link.dataset.panel);
}
});
});
this.nav.querySelectorAll('.megamenu__back').forEach(btn => {
btn.addEventListener('click', () => this.goBack());
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape') this.close();
});
}
open() {
this.nav.classList.add('is-open');
this.nav.setAttribute('aria-hidden', 'false');
this.toggle?.setAttribute('aria-expanded', 'true');
document.body.style.overflow = 'hidden';
}
close() {
this.nav.classList.remove('is-open');
this.nav.setAttribute('aria-hidden', 'true');
this.toggle?.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
setTimeout(() => this.resetPanels(), 300);
}
goTo(panelId) {
const root = this.nav.querySelector('.megamenu__panel--root');
const target = this.nav.querySelector(`#panel-cat-${panelId}`);
if (!target) return;
root.classList.add('slide-out');
target.classList.add('is-active');
this.stack.push(panelId);
}
goBack() {
this.stack.pop();
const root = this.nav.querySelector('.megamenu__panel--root');
this.panels.forEach(p => {
if (!p.classList.contains('megamenu__panel--root')) {
p.classList.remove('is-active');
}
});
root.classList.remove('slide-out');
if (this.stack.length > 0) {
this.goTo(this.stack[this.stack.length - 1]);
}
}
resetPanels() {
this.stack = [];
const root = this.nav.querySelector('.megamenu__panel--root');
root.classList.remove('slide-out');
this.panels.forEach(p => {
if (!p.classList.contains('megamenu__panel--root')) {
p.classList.remove('is-active');
}
});
}
}
document.addEventListener('DOMContentLoaded', () => {
const nav = document.getElementById('site-nav');
if (nav) new MegaMenu(nav);
});
Comparison: our solution vs. standard Bitrix menu
| Characteristic | Standard menu | Our adaptive megamenu |
|---|---|---|
| Adaptivity | No, hover doesn't work on touch | Drawer + slides on mobile, hover on desktop |
| Performance | Loads all data at once, no cache | Tagged caching, lazy loading images |
| Accessibility (ARIA) | Missing | Full ARIA attribute support |
| Load time on 3G | ~2 sec | ~1.2 sec (40% faster) |
| Gesture support | No | Swipe, tap, Back button |
Mobile performance
- Subcategory images:
loading="lazy", WebP via<picture>, size 40×40 - Animations only via CSS
transformandopacity— GPU, no reflow -
overscroll-behavior: contain— panel doesn't scroll the body -
height: 100dvh— correct height considering browser address bar
Bitrix caching configuration
// In .settings.php file add tagged cache for menu
'cache' => [
'type' => [
'class' => '\Bitrix\Main\Data\Cache\Engine\Files',
'settings' => [
'cache_dir' => '/bitrix/cache',
],
],
'managed' => true,
],
In the menu component use $APPLICATION->IncludeComponent with parameter CACHE_TAG => menu.
What's included in the work
We provide a complete package: integration documentation, instructions for updating the menu in Bitrix, repository access with code, training for content managers, and one month of support after launch. Optionally, configuration of tagged caching for faster loading.
Timelines and cost
| Configuration | Timeline |
|---|---|
| Drawer + slide-navigation (mobile) | 3–4 days |
| + Single template for mobile/desktop | 5–7 days |
| + Animations, swipe gestures, accessibility | +2–3 days |
Cost is calculated individually based on catalog size and design requirements. We'll evaluate your project for free — contact us.
Typical mistakes in self-implementation
- Incorrect cache handling: menu doesn't update after changing section structure. Use tagged cache with tag
menu. - Ignoring accessibility: missing ARIA attributes breaks screen readers. We add
aria-expanded,aria-controls,aria-haspopup. - Fixed panel height: on different devices the address bar behaves differently. We use
100dvh.
Our engineers are certified "1C-Bitrix" and have over 10 years of experience in implementing complex navigation solutions. Order development of an adaptive megamenu for your online store — improve mobile conversion today. Get a free consultation.







