Простое и понятное меню навигации — важнейшая часть удобного интерфейса. Отзывчивость меню тоже критична, ведь сейчас более половины всего интернет-трафика приходится на мобильные устройства.
В подходе mobile-first разработка начинается с самого маленького экрана — мобильного, и по мере увеличения ширины добавляются новые стили и элементы. В итоге страница автоматически подстраивается под размер окна браузера.
Хотя адаптивное меню — ключевой элемент хорошего UX, для его реализации не обязательно использовать JavaScript. В этом руководстве мы покажем, как сделать отзывчивое mobile-first меню только с помощью HTML и CSS.
Отзывчивое меню на чистом CSS
Существует много способов реализации мобильного меню, и один из самых популярных — с использованием только CSS, без JavaScript. В основе — обычный HTML-список ссылок, стилизованный под размер экрана.
В этом уроке мы с помощью CSS создадим меню навигации, которое работает на мобильных устройствах, планшетах и десктопах:



На небольших экранах меню будет скрыто за иконкой. А на больших — его пункты сразу будут видны в панели навигации.
Весь функционал будет реализован только на HTML и CSS, включая иконку меню. Наш результат будет выглядеть так:

Начнем с подготовки файлов
Создайте в одной папке два файла в любом удобном редакторе:
index.html
— для HTML-кодаstyle.css
— для CSS-кода
Затем откройте файл index.html
в браузере, чтобы видеть изменения.
Начинаем с HTML-кода
Добавьте в файл index.html
следующий код:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- App title -->
<title>Responsive Pure CSS Menu</title>
<!-- Link CSS file -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Navigation bar -->
<header class="header">
<!-- Logo -->
<a href="#" class="logo">LR</a>
<!-- Hamburger icon -->
<input class="side-menu" type="checkbox" id="side-menu"/>
<label class="hamb" for="side-menu"><span class="hamb-line"></span></label>
<!-- Menu -->
<nav class="nav">
<ul class="menu">
<li><a href="#">Gallery</a></li>
<li><a href="#">Blog</a> </li>
<li><a href="#">About</a></li>
</ul>
</nav>
</header>
<!-- Main content -->
<main>
<article>
<h1>
Some content
</h1>
<p>
More Content
</p>
</article>
</main>
</body>
</html>
Этот код задаёт структуру и содержимое веб-страницы, подключает таблицу стилей CSS и делит панель навигации и основной контент с помощью семантических элементов header
и main
. Также добавляется логотип с помощью элемента привязки <a>
.
Для иконки меню мы используем элемент checkbox
— с его помощью можно менять стиль в зависимости от состояния флажка.
Мы используем элемент label
, чтобы обозначить иконку меню, а input
с классом side-menu
будет определять, отображается меню или нет. Пункты меню добавляются как элементы <li>
внутри неупорядоченного списка ul
, который, в свою очередь, находится в контейнере nav
.
Вот результат после добавления HTML-кода:

Добавляем CSS-оформление
Теперь с помощью CSS мы стилизуем содержимое страницы и все элементы интерфейса.
Стилизация фона и основного содержимого
Добавим в файл style.css
следующий код:
/* Theming */
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap"); /* import font */
:root{
--white: #f9f9f9;
--black: #36383F;
--gray: #85888C;
} /* variables*/
/* Reset */
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
body{
background-color: var(--white);
font-family: "Poppins", sans-serif;
}
a{
text-decoration: none;
}
ul{
list-style: none;
}
Этот код вызывает загрузку шрифта Poppins Google и задаёт переменные CSS для используемых цветов. Затем мы добавляем CSS, чтобы сделать «сброс» стандартных настроек браузера для margin
, padding
, box-sizing
, text-decoration
и list-style
.
Также мы задаём белый фон страницы (background-color
) и шрифт (font-family
) Poppins для всего текста. Вот результат, отображающий стилизованное содержимое и фон:

Стилизация шапки и логотипа
Код ниже задаёт шапке чёрный фон (background-color
), серую тень (box-shadow
) и нулевое смещение сверху (top
). Мы фиксируем её вверху экрана с помощью липкого позиционирования (position
) и делаем так, чтобы она растянулась по всей ширине устройства (width
):
/* Header */
.header{
background-color: var(--black);
box-shadow: 1px 1px 5px 0px var(--gray);
position: sticky;
top: 0;
width: 100%;
}
/* Logo */
.logo{
display: inline-block;
color: var(--white);
font-size: 60px;
margin-left: 10px;
}
Для стилизации логотипа указыаем color
, font-size
, and left-margin
. Важно: margin
— это расстояние от логотипа до других элементов, не путайте его с padding
.

Стилизация навигационного меню
Для элемента nav
мы устанавливаем свойства width
и height
на 100%, чтобы он занимал всю ширину экрана. Также задаём фиксированное позиционирование (position
), чтобы он отображался поверх остального контента. Фон (background-color
) делаем чёрным и скрываем лишний контент (overflow
).
Каждому элементу меню присваивается отображение блоком (block
), добавляются отступы (padding
) и цвет (color
). При наведении курсора мы меняем фон (background-color
) с белого на серый.ХХХХХ
Наконец, с помощью свойства CSS transition
, мы ставим max-height
, равный нулю, чтобы меню было скрыто по умолчанию, но отображалось при щелчке по иконке:
/* Nav menu */
.nav{
width: 100%;
height: 100%;
position: fixed;
background-color: var(--black);
overflow: hidden;
}
.menu a{
display: block;
padding: 30px;
color: var(--white);
}
.menu a:hover{
background-color: var(--gray);
}
.nav{
max-height: 0;
transition: max-height .5s ease-out;
}

Стилизация иконки меню
Далее мы стилизуем элемент label
, чтобы он выглядел как иконка меню. Задаём cursor: pointer
, отображающийся, когда пользователь взаимодействует с меню, размещаем элемент label
справа и добавляем отступы.
Затем добавляем элемент span
, чтобы сформировать три линии иконки меню. Для этого используются псевдо-элементы CSS [::before]
и [::after]
, создающие верхнюю и нижнюю линии.
Селектор .hamb-line
описывает центральную, или вторую, линию, а .hamb-line::before
и .hamb-line::after
располагают первую и третью линии на 5 пикселей выше и ниже центральной.
Наконец, мы описываем свойство display
в .side-menu
, чтобы скрыть чекбокс:
/* Menu Icon */
.hamb{
cursor: pointer;
float: right;
padding: 40px 20px;
}/* Style label tag */
.hamb-line {
background: var(--white);
display: block;
height: 2px;
position: relative;
width: 24px;
} /* Style span tag */
.hamb-line::before,
.hamb-line::after{
background: var(--white);
content: '';
display: block;
height: 100%;
position: absolute;
transition: all .2s ease-out;
width: 100%;
}
.hamb-line::before{
top: 5px;
}
.hamb-line::after{
top: -5px;
}
.side-menu {
display: none;
} /* Hide checkbox */

Стилизация нажатой иконки
В коде ниже мы указываем, как меняется иконка меню при нажатии. Для начала, когда чекбокс активен (.side-menu:checked
), задаём max-height
для nav
, чтобы раскрыть меню.
Затем мы превращаем иконку в крестик. Сначала скрываем вторую линию, делая её фон прозрачным (background: transparent
). Затем формируем Х, поворачивая верхнюю и нижнюю линии на -45 и 45 градусов соответственно.
Наконец, для улучшения пользовательского опыта на мобильных устройствах отключаем прокрутку страницы при открытом меню, чтобы фокус оставался на нём.
/* Toggle menu icon */
.side-menu:checked ~ nav{
max-height: 100%;
}
.side-menu:checked ~ .hamb .hamb-line {
background: transparent;
}
.side-menu:checked ~ .hamb .hamb-line::before {
transform: rotate(-45deg);
top:0;
}
.side-menu:checked ~ .hamb .hamb-line::after {
transform: rotate(45deg);
top:0;
}
body:has(.side-menu:checked) {
overflow: hidden;
}
Вот результат, отображающий открытое меню:

Добавляем адаптивность
Чтобы меню адаптировалось к размеру экрана, мы используем media queries для условного включения свойств CSS. Иначе говоря, свойства внутри media query будут применены к веб-странице только тогда, когда набор условий действителен:
/* Responsiveness */
@media (min-width: 768px) {
.nav{
max-height: none;
top: 0;
position: relative;
float: right;
width: fit-content;
background-color: transparent;
}
.menu li{
float: left;
}
.menu a:hover{
background-color: transparent;
color: var(--gray);
}
.hamb{
display: none;
}
}
В коде выше мы добавляем правило @media
с условием min-width: 768px
. Это значит, что при ширине экрана от 768 пикселей и больше, пользователи будут видеть полноразмерное меню, а не иконку.
Для элемента nav
мы убираем ограничение по высоте (max-height: none
), размещая в правом верхнем углу экрана и задавая его ширину, как fit-content
.
Пункты меню выравниваются в строку с помощью элемента float
. Мы делаем их фон прозрачным, но серым при наведении курсора. Наконец, мы используем свойство display
, чтобы скрыть иконку меню.

Здесь на видео можно посмотреть, как выглядит интерфейс в динамике.
Позиционирование: фиксированное, относительное или липкое
Свойство position в CSS управляет тем, как навигационное меню размещается на странице. Вместе с ним свойства top, right, bottom, left определяют позиционирование элементов.
При фиксированном позиционировании навигационное меню остаётся на месте при прокрутке. В таком случае панель навигации перекрывает контент, а содержимое страницы проходит под ним:


Относительное позиционирование размещает элемент относительно местоположения по умолчанию. Однако другой контент на странице не подстраивается под возникающий отступ:


Липкое позиционирование заставляет панель навигации прокручивается вместе с остальным содержимым, пока положение не совпадёт с указанным отступом. Остальное содержимое страницы продолжает прокручивается под меню:


Чтобы увидеть разницу, поменяйте свойство position
у элемента .header
с fixed
на relative
или sticky
:
.header{
background-color: var(--black);
box-shadow: 1px 1px 5px 0px var(--gray);
position: sticky;
top: 100px;
width: 100%;
}
Добавляем подменю
Иногда нам нужно сделать вложенные пункты меню. Это экономит место в панели навигации и делает структуру вашего сайта более организованной, и поэтому более удобной для пользователя.
Для этого в файл index.html
добавляем дополнительный элемент li
, как показано ниже. Иконку для класса fa fa-caret-down
можно получить, добавив ссылку на файл стилей font-awesome
в ваш HTML:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
...
<li class="subnav">
<p class="subnavbtn">Contact <i class="fa fa-caret-down"></i></p>
<div class="subnav-content">
<a href="#">Email</a>
<a href="#">Twitter</a>
<a href="#">Phone</a>
</div>
</li>
Далее в CSS можно задать правила, при которых подменю будет отображаться при наведении курсора, как показано ниже:
/* Sub nav */
.subnav-content {
background-color: var(--white);
width: 100%;
z-index: 1;
padding: 20px 0 ;
display: none;
}
.subnav-content a {
color: var(--black);
text-decoration: none;
padding: 0;
margin: 10px 0;
text-align: center;
}
.subnav:hover .subnav-content {
display: block;
}
....
@media (min-width: 768px) {
....
/* Sub nav */
.subnav-content {
padding: 20px 0 ;
display: none;
background-color: var(--black);
}
.subnav-content a {
color: white;
}
}
В этом видео демонстрируется, как работает готовое подменю.
Дополнительная стилизация
Улучшение внешнего вида меню помогает сделать дизайн сайта более выразительным и индивидуальным. В этом разделе мы покажем, как применять CSS-переменные, градиенты, тени и другие техники стилизации, чтобы легко настроить внешний вид вашего меню.
CSS-переменные
CSS-переменные позволяют один раз задать значение, а затем использовать его в разных частях файла стилей. Это удобно, когда нужно быстро обновить внешний вид меню — вместо того чтобы менять одно и то же значение в нескольких местах, вы просто обновляете переменную.
Ниже пример того, как можно задать CSS-переменные и использовать их при стилизации меню:
/* Define CSS variables for colors */
:root {
--menu-background-color: #333; /* Dark background color */
--menu-text-color: #fff; /* White text color */
--hover-color: #ff9900; /* Color on hover */
}
/* Use CSS variables in your menu styles */
.nav {
width: 100%;
height: 100%;
background-color: var(--menu-background-color);
overflow: hidden;
}
.menu a {
display: block;
padding: 30px;
color: var(--menu-text-color);
}
Градиенты и тени
Чтобы добавить глубины и акцентировать элементы меню, можно использовать CSS-градиенты и тени. Это позволяет создать более интересный визуальный эффект и избежать плоского дизайна. Вот пример того, как можно их использовать:
/* Add gradient background */
.menu {
background-image: linear-gradient(to bottom, #333, #666); /* Gradient from dark to light */
}
/* Add box shadow */
.menu {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); /* Shadow with blur effect */
}
Эффекты при наведении курсора
Эффекты при наведении курсора делают меню интерактивным и дают пользователю визуальную обратную связь. С их помощью можно, например, изменить цвет, размер или стиль пункта меню.
Чтобы добавить эффект к пункту меню, вы можете использовать псевдокласс :hover
в CSS. Вот как это можно сделать:
/* Change text color on hover */
.menu a:hover {
color: var(--hover-color); /* Change to your desired hover color */
}
/* Add underline on hover */
.menu a:hover {
text-decoration: underline;
}
/* Scale effect on hover */
.menu a:hover {
transform: scale(1.3); /* Increase the size by 30% on hover */
}
/* Fade in/out effect on hover */
.menu a {
opacity: 0.7; /* Set initial opacity */
transition: opacity 0.3s ease; /* Add transition for smooth effect */
}
.menu a:hover {
opacity: 1; /* Change to full opacity on hover */
}
На GIF ниже показано, как такие эффекты работают при наведении:

Анимации
Лёгкие анимации — такие как эффект выдвижения или постепенное проявление — делают меню более живым и приятным в использовании.
Для достижения такого эффекта мы используем CSS-анимации. Сначала определим свойства в нашем CSS-файле:
/* Define the slide animation */
@keyframes slideIn {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
/* Apply the animation to the menu items */
.side-menu:checked~nav .menu a {
animation: slideIn 0.5s forwards;
}
В примере выше мы задали анимацию slideIn
, она перемещает элементы меню сверху от -100%
вниз до 0
, одновременно делая их видимыми, благодаря свойству opacity. Анимация запускается при наведении курсора на пункт «Контакты» на десктопе или при открытии мобильного меню:

Доступность и ARIA-атрибуты
Доступность — важный аспект веб-разработки. Он гарантирует, что ваш сайт будет удобным для пользователей с ограниченными возможностями. Атрибуты ARIA (Accessible Rich Internet Applications) играют ключевую роль в этом.
Пример добавления ARIA-атрибутов в мобильное меню:
<!-- Navigation bar -->
<header class="header" role="banner">
<!-- Logo -->
<a href="#" class="logo" aria-label="Home">
LR
</a>
<!-- Hamburger icon -->
<input class="side-menu" type="checkbox" id="side-menu" />
<label class="hamb" for="side-menu" aria-label="Menu">
<span class="hamb-line"></span>
</label>
<!-- Menu -->
<nav class="nav" role="navigation" aria-label="Main">
<ul class="menu">
<li><a href="#" aria-current="page">Gallery</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">About</a></li>
<!-- Contact submenu -->
<li aria-haspopup="true">
<div class="subnav">
<a href="#" class="subnavbtn" aria-label="Contact" aria-controls="contact-submenu"
tabindex="0">Contact <i class="fa fa-caret-down"></i></a>
<ul class="subnav-content" role="menu" id="contact-submenu">
<li><a href="#" role="menuitem">Email</a></li>
<li><a href="#" role="menuitem">Twitter</a></li>
<li><a href="#" role="menuitem">Phone</a></li>
</ul>
</div>
</li>
</ul>
</nav>
</header>
В приведённом выше коде мы добавили к шапке атрибут role="banner"
, который помогает программам чтения с экрана его идентифицировать. Кроме того, мы использовали role="navigation"
для навигационного меню, поясняя его значение для вспомогательных технологий.
Для пользователей, использующих программы чтения с экрана, мы также добавили атрибут aria-label
к логотипу и иконке. Эти метки добавляют текстовое описание, поясняющее значение этих элементов.
Атрибут aria-current="page"
указывает, на какой странице находится пользователь. Это позволяет людям точно знать об их текущем местоположении на сайте.
Для подменю мы внесли изменение, используя элемент ul
вместо div
, чтобы вспомогательным технологиям была понятна иерархия элементов подменю, упрощая навигацию для пользователей.
Добавьте следующий CSS-код в media query:
@media (min-width: 768px) {
...
.subnav-content li {
display: block;
width: 100%;
background-color: var(--black);
}
.subnavbtn:focus+.subnav-content {
display: block;
}
.subnav:hover .subnav-content {
display: block;
}
.subnav:focus-within .subnav-content {
display: block;
}
.subnav-content a:focus {
background-color: var(--white);
color: var(--black);
}
}
Этот код улучшает отображение подменю: обеспечивается корректное расположение элементов и выделяются активные пункты подменю для пользователей, использующих только клавиатуру.
Чтобы гарантировать, что подменю остаётся видимым при фокусе на соответствующей ссылке или при наведении, мы обновили свойство display
: теперь оно отображается как block
при фокусе. Это изменение делает меню удобнее.
Ниже показано, как можно взаимодействовать с меню только с помощью клавиши Tab:

Горизонтальное и вертикальное меню на телефоне
На мобильных устройствах меню навигации может располагаться как горизонтально, так и вертикально. В горизонтальном варианте ссылки отображаются в один ряд над основным содержимым. В вертикальном — располагаются вдоль одной из сторон экрана и, как правило, открываются по клику на иконку меню.
Горизонтальное меню: плюсы и минусы
Такая панель навигации используется чаще всего и выглядит знакомо для большинства пользователей. Для носителей русского языка порядок слева направо интуитивно понятен. Он также поощряет более краткие названия ссылок.
Однако пространство здесь ограничено, поэтому сложно разместить много ссылок. В результате может быть сложно добавить верхнеуровневые ссылки, а выпадающие подменю могут перекрывать контент.
Вертикальное меню: плюсы и минусы
В сравнении с горизонтальным меню вертикальная панель навигации предоставляет больше места для ссылок. Соответственно, названия ссылок могут быть длиннее, а добавление новых пунктов упрощается.
Но если ссылок много, меню может выглядеть перегруженным, и оно менее привычно для пользователей. Кроме того, подменю, раскрывающиеся в сторону, могут быть менее удобными, чем выпадающие списки в горизонтальном меню.
Сравните эти примеры вертикального и горизонтального меню ниже:


Заключение
В этом уроке мы создали отзывчивое меню по принципу mobile-first, используя только HTML и CSS — без JavaScript. Полный код, используемый в этой статье, доступен в репозитории GitHub.
Метод, используемый в этой статье, — это лишь один из многих, которые вы можете использовать. Экспериментируйте с разными способами, и выбирайте тот, что вам нравится больше.
Удачи в разработке!