Нижняя навигация — обязательный элемент многих мобильных веб-приложений. Она должна всегда оставаться на виду, не перекрывать контент и корректно работать на экранах разных размеров. На практике есть несколько способов её реализации, и каждый из них имеет свои особенности.
Пример Bottom Navigation:
В этой статье мы разберём три популярных подхода:
- Grid-вёрстка с фиксированным header и footer.
- Fixed-позиционирование с отступом для контента.
- Sticky-позиционирование с использованием
order.
Рассмотрим их сильные и слабые стороны, а также случаи, когда тот или иной метод может подвести. Статья акцентируется на каркасе самой страницы, поэтому вёрстку самого Bottom Navigation не включаю – вряд ли с этим будут какие-то проблемы.
Вариант 1: Grid-вёрстка
<body class="body-layout">
<header></header>
<div class="overflow-y-auto">
<main class="container"></main>
</div>
<nav class="xl:hidden"></nav>
</body>
.body-layout {
height: 100dvh;
display: grid;
grid-template-rows: auto 1fr auto;
align-content: start;
@media (width >= 80rem) {
height: auto;
min-height: 100dvh;
grid-template-rows: none;
}
}
Экран разбивается на три ряда: шапка, контент, подвал. Прокручивается только средняя часть, а <header> и <nav> остаются на месте.
Плюсы:
- Нет проблем с z-index.
- Чёткая структура.
Минусы:
- Нужно создавать внешний контейнер с
overflow-y-auto, иначе могут появиться горизонтальные скроллы, если есть вложенные элементы с отрицательным margin. - При добавлении новых элементов в
<body>нужно следить за разметкой – это хрупкое решение. - Если у корневого
<div>есть отступы, то scrollbar будет находиться не у края экрана, а у края этого контейнера. - Ломает работу библиотек, завязанных на
window.scrollY(например, анимации при скролле вроде GSAP ScrollTrigger или Scroll-driven animations в CSS), так как скроллится внутренний контейнер, а не окно браузера.
Вариант 2: Fixed-позиционирование
<body class="body-layout">
<header class="sticky top-0 z-20"></header>
<main class="container"></main>
<nav class="bottom-nav"></nav>
<footer class="hidden xl:block"></footer>
</body>
:root {
--bottom-nav-height: 5rem;
}
.body-layout {
min-height: 100dvh;
display: flex;
flex-direction: column;
}
header {
flex-shrink: 0;
}
main {
padding-bottom: var(--bottom-nav-height);
flex-grow: 1;
@media (width >= 80rem /* 1280px */) {
padding-bottom: 0;
}
}
.bottom-nav {
position: fixed;
bottom: 0;
z-index: 20;
height: var(--bottom-nav-height);
@media (width >= 80rem /* 1280px */) {
display: none;
}
}
Плюсы:
- Более предсказуемое поведение, элемент всегда прибит к низу экрана.
- Скроллится вся страница, а не отдельный
<div>.
Минусы:
- Требуется явный отступ снизу у
<main>или последнего элемента, иначе нельзя будет промотать экран до самого последнего элемента.
Вариант 3: Sticky
<body class="body-layout">
<header class="sticky top-0 z-20 shrink-0"></header>
<main class="container"></main>
<nav class="bottom-nav"></nav>
<footer class="hidden xl:block"></footer>
</body>
.body-layout {
min-height: 100dvh;
display: flex;
flex-direction: column;
}
header {
flex-shrink: 0;
}
main {
flex-grow: 1;
}
.bottom-nav {
position: sticky;
bottom: 0;
z-index: 20;
order: 9999;
@media (width >= 80rem /* 1280px */) {
display: none;
}
}
<body> нужно задать минимальную высоту 100vh. А центральному элементу flex-grow: 1; для того, чтобы он занял свободную высоту экрана, если страница имеет недостаточно контента для полной высоты.
Плюсы:
- Скроллится вся страница, а не отдельный
<div>.
Минусы:
- Элемент должен быть последним на странице. Нужно следить за разметкой, иначе
<footer>может оказаться ниже<nav>. - На всех страницах нужно центральному элементу ставить
flex-grow: 1;
Заключение
Каждый из трёх подходов решает задачу фиксации нижней навигации, но с разными компромиссами.
- Grid — хорош для строгих макетов, но сложен в поддержке и может давать побочные эффекты с прокруткой.
- Fixed — самый надёжный способ «приклеить» элемент, но требует ручного управления отступами.
- Sticky — элегантное решение, если вы контролируете разметку и готовы гарантировать порядок элементов.
Мне больше нравится вариант с Sticky, так как для центрального элемента всё равно обычно приходится задавать flex: 1; или flex-grow: 1; (для того, чтобы footer был прибит к низу страницы).