Адаптивное меню на CSS

Возвращаясь к теме всё возрастающей значимости мобильных устройств, невозможно обойти стороной меню сайта. По бо́льшей части для создания адаптивных меню используются решения на JavaScript, но всегда ли это оправдано? Пожалуй, нет. Ведь его можно сделать на чистом CSS, изменяя видимость элементов при помощи чекбокса.

Не будем долго разглагольствовать о плюсах и минусах данного подхода, а сразу обратимся к конкретному примеру. В данном случае это будет простое одноуровневное меню.

Что делать с многоуровневыми меню? Здесь есть несколько вариантов:

  • начать смотреть решения на JavaScript/jQuery
  • открывать второй уровень по наведению (что, надо сказать, не является хорошим решением)

Разметка будет чуть более многословной, чем нам это необходимо: здесь содержатся элементы, которые и вовсе могут не пригодиться. Однако, она довольно проста и разобраться в ней совсем несложно.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1.0, width=device-width">
  <title>Pure CSS Menu</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header><!-- шапка -->
    <input type="checkbox" id="menu-checkbox" />
    <nav role="navigation"><!-- навигация -->
      <label for="menu-checkbox" class="toggle-button" data-open="Menu" data-close="Close" onclick></label>
      <ul class="main-menu"><!-- список пунктов меню -->
        <li><a href="#">home</a></li>
        <li><a href="#">about</a></li>
        <li><a href="#">contacts</a></li>
        <li><a href="#">projects</a></li>
        <li><a href="#">publications</a></li>
      </ul>
    </nav>
  </header>
  <article>
    <h1>CSS Menu</h1>
    <p>Just css and html</p>
  </article>
</body>
</html>

Дальше постепенно будем разбирать каждый кусочек css-кода по принципу mobile first.

Стили

На устройствах с экраном менее 640 пикселей пункты меню скрываются и остаётся только стилизованный через label чекбокс. Если чекбокс отмечен, меню становится видимым. Код для реализации:

/* Menu  */
#menu-checkbox {
  /* чекбокс скрыт */
  position: absolute;
  clip: rect(1px, 1px, 1px, 1px);
}
.main-menu {
  /* пункты меню скрыты */
  position: absolute;
  clip: rect(1px, 1px, 1px, 1px);
  padding: 0;
  margin: 0;
}
.main-menu li {
  /* пункты меню расположены вертикально */
  /* каждый имеет ширину 100% */
  font-size: 1em;
  display: block;
  width: 100%;
}
.main-menu a {
  /* немного стилизуем ссылки */
  display: block;
  border-bottom: 1px solid blueviolet;
  color: lightslategray;
  padding: .5em;
  text-decoration: none;
}
.main-menu a:hover {
  text-decoration: underline;
}
.toggle-button {
  text-align: center;
  display: block;
  /* не забываем явно указать каким должен быть курсор */
  cursor: pointer;
  background-color: #333;
  color: #fff;
}
.toggle-button:after {
  /* получаем данные из атрибута data-open в html */
  content: attr(data-open);
  display: block;
  margin: 10px 0;
  padding: 10px 30px;
}
#menu-checkbox:checked + nav[role="navigation"] .main-menu {
  /* когда чекбокс активен меню становится видимым */
  position: static;
}
#menu-checkbox:checked + nav[role="navigation"] .toggle-button:after {
  content: attr(data-close);
}

С мобильными устройствами разобрались. Осталось внести изменения для тех устройств, экран которых >= 640px. Делать это будем через медиа-запросы.

@media screen and (min-width: 640px) {
  .toggle-button {
    /* label скрыт */
    position: absolute;
    clip: rect(1px, 1px, 1px, 1px);
  }
  .main-menu {
    /* меню отображается */
    position: static;
    margin: 0 auto;
    background-color: #333;
    height: 30px;
  }
  .main-menu li {
    /* теги <li> расположены в ряд */
    width: calc(100% / 5);
    text-align: center;
    display: inline-block;
    margin-right: -4px;
  }
  .main-menu a {
    display: inline-block;
    margin-right: -4px;
    font-size: .8em;
    background-color: #333;
    text-decoration: none;
    color: #fff;
    text-transform: capitalize;
    border-bottom: 0;
  }
}

Стилизация метки

Разумеется, вместо data-open/close можно использовать что угодно. Например, добавить иконочный шрифт.

.toggle-button:after {
  font-family: 'fontello';
  content: "\e804";
  cursor: pointer;
  padding: 15px;
  font-size: 1.5em;
  text-align: center;
}
#menu-checkbox:checked + .main-menu .toggle-button:after {
  content: "\e804";
}
menu with fontello icon

Или иконку с вместе с поясняющим текстом:

<label
  for="menu-checkbox"
  class="toggle-button"
  data-open="меню "
  data-close="меню "
  onclick
></label>

Здесь мы получаем значение атрибутов data-open/close, отображаем данные через before, а саму иконку добавляем с помощью псевдоэлемента after.

.toggle-button:before {
  content: attr(data-close);
  font-size: 1rem;
  position: relative;
  right: 3px;
  bottom: 3px;
}
.toggle-button:after {
  content: "\f0c9";
  font-family: 'FontAwesome';
  display: inline-block;
}
#menu-checkbox:checked + nav[role="navigation"] .menu {
  display: block;
}
#menu-checkbox:checked + nav[role="navigation"] .toggle-button:after {
  content: "\f0c9";
  font-family: 'FontAwesome';
  color: green;
}
#menu-checkbox:checked + nav[role="navigation"] .toggle-button:before {
  color: green;
}
menu_with_fontello_icon

В последних двух примерах читателю нужно будет самостоятельно поиграть с позиционированием иконок и расположением самого текста.

Каких-то невероятных эффектов от подобной реализации меню ждать не приходится, хотя не сто́ит забывать о возможностях CSS-анимации.