Как сверстать вертикальное меню

Эта статья основана на одном из практических заданий verstaem.online.

Вы можете читать материал как обычное руководство или сначала попробовать выполнить задание самостоятельно, а затем сравнить свое решение с подробным разбором.

Перейти к заданию

В этой статье мы создадим вертикальное меню навигации с иконками и активным пунктом. На его примере разберем, как выстраивать элементы в колонку, оформлять состояние активной ссылки и создавать аккуратные отступы между пунктами меню.

Создаем HTML-разметку

Сначала создадим базовую HTML-разметку с помощью Emmet. Для этого достаточно ввести ! и нажать Tab. Обратите внимание на атрибут lang. Поскольку основной контент страницы написан на русском языке, здесь используем значение ru.

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Вертикальное меню</title>
  </head>
  <body>
  </body>
</html>

Переходим к верстке меню. В данном случае это не просто меню, а именно навигация по сайту, а значит здесь логично использовать специальный тег <nav>. Внутри него имеются одинаковые по смыслу элементы, который идут друг за другом — значит, это список, <ul>. Внутри элементов списка мы добавим ссылки.

А вот внутри ссылок будет текст и изображение. Но изображение не с помощью тега <img>, а с помощью <svg>. Почему так? Все дело в том, что по макету нам обязательно требуется менять цвет изображения с тусклого на более яркий у активного элемента. Это можно сделать и через <img>, но тогда пришлось бы делать две картинки, дублировать их, в общем — портить разметку. И да, с <svg> разметка кажется явно больше, и это так. Но зато мы получаем удобство в виде возможности изменения цвета.

<nav class="nav">
  <ul class="nav__list">
    <li class="nav__item">
      <a href="#" class="nav__link nav__link--active">
        <svg
          width="20"
          height="20"
          viewBox="0 0 20 20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M12.5 17.5V10.8333C12.5 10.6123 12.4122 10.4004 12.2559 10.2441C12.0996 10.0878 11.8877 10 11.6667 10H8.33333C8.11232 10 7.90036 10.0878 7.74408 10.2441C7.5878 10.4004 7.5 10.6123 7.5 10.8333V17.5"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M2.5 8.33333C2.49994 8.09088 2.55278 7.85135 2.65482 7.63142C2.75687 7.4115 2.90566 7.21649 3.09083 7.05999L8.92417 2.05999C9.22499 1.80575 9.60613 1.66626 10 1.66626C10.3939 1.66626 10.775 1.80575 11.0758 2.05999L16.9092 7.05999C17.0943 7.21649 17.2431 7.4115 17.3452 7.63142C17.4472 7.85135 17.5001 8.09088 17.5 8.33333V15.8333C17.5 16.2754 17.3244 16.6993 17.0118 17.0118C16.6993 17.3244 16.2754 17.5 15.8333 17.5H4.16667C3.72464 17.5 3.30072 17.3244 2.98816 17.0118C2.67559 16.6993 2.5 16.2754 2.5 15.8333V8.33333Z"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
        Главная
      </a>
    </li>
    <li class="nav__item">
      <a href="#" class="nav__link">
        <svg
          width="20"
          height="20"
          viewBox="0 0 20 20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M3.33333 4.16667H16.6667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M3.33333 10H16.6667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M3.33333 15.8333H16.6667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
        Задания
      </a>
    </li>
    <li class="nav__item">
      <a href="#" class="nav__link">
        <svg
          width="20"
          height="20"
          viewBox="0 0 20 20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M15.8333 17.5V15.8333C15.8333 14.9493 15.4821 14.1014 14.857 13.4763C14.2319 12.8512 13.3841 12.5 12.5 12.5H7.5C6.61595 12.5 5.7681 12.8512 5.14298 13.4763C4.51786 14.1014 4.16667 14.9493 4.16667 15.8333V17.5"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M10 9.16667C11.841 9.16667 13.3333 7.67428 13.3333 5.83333C13.3333 3.99238 11.841 2.5 10 2.5C8.15905 2.5 6.66667 3.99238 6.66667 5.83333C6.66667 7.67428 8.15905 9.16667 10 9.16667Z"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
        Профиль
      </a>
    </li>
    <li class="nav__item">
      <a href="#" class="nav__link">
        <svg
          width="20"
          height="20"
          viewBox="0 0 20 20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M2.5 10C2.5 11.4834 2.93987 12.9334 3.76398 14.1668C4.58809 15.4001 5.75943 16.3614 7.12987 16.9291C8.50032 17.4968 10.0083 17.6453 11.4632 17.3559C12.918 17.0665 14.2544 16.3522 15.3033 15.3033C16.3522 14.2544 17.0665 12.918 17.3559 11.4632C17.6453 10.0083 17.4968 8.50032 16.9291 7.12987C16.3614 5.75943 15.4001 4.58809 14.1668 3.76398C12.9334 2.93987 11.4834 2.5 10 2.5C7.90329 2.50789 5.89081 3.32602 4.38333 4.78333L2.5 6.66667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M2.5 2.5V6.66667H6.66667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
          <path
            d="M10 5.83333V10L13.3333 11.6667"
            stroke-width="2"
            stroke-linecap="round"
            stroke-linejoin="round"
          />
        </svg>
        История
      </a>
    </li>
  </ul>
</nav>

Когда вы будете самостоятельно доставать svg-изображения из макета, вы увидите в их кода атрибут stroke с каким-то цветом. Этот атрибут обязательно нужно удалить (как в коде выше), иначе CSS-стили не смогут переопределять цвета.

Добавляем стили

Подключим файл стилей через <link> и зададим несколько базовых правил. Для этого задания достаточно переопределить box-sizing, сбросить отступ у <body> и подключить шрифт Inter, который используется в макете.

* {
  box-sizing: border-box;
}

@font-face {
  font-family: "Inter";
  src: url("../fonts/InterMedium.woff2");
  font-display: swap;
  font-weight: 500;
}

body {
  margin: 0;
  padding: 20px;
  font-family: "Inter", sans-serif;
}

После этого мы должны заняться стилизацией блока «сверху вниз», от родителя к потомкам. Поэтому стилизуем навигацию, задавая все стили из макета.

.nav {
  border: 1px solid #243041;
  border-radius: 24px;
  padding: 16px;
  width: 100%;
  max-width: 280px;
  background: #111827;
}

Далее стоит стилизовать список, ведь именно он управляет содержимым, и именно с помощью списка мы можем задать отступы между элементами, расставить их в столбец как нам нужно и так далее. И здесь, чтобы расставить элементы в столбец, используем flex-direction: column. Это свойство «поворачивает» ось flexbox, делая главной осью вертикаль, а не горизонталь.

.nav__list {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  list-style: none;
  gap: 12px;
}

Последним штрихом будут стили для ссылок. Здесь нам также пригодится flexbox, чтобы расставить в строку иконку и текст. И здесь есть нюанс — это стили для самого svg-изображения. Мы явно находим его, используя css-вложенность, и задаем ему размеры, а также параметр stroke, который мы удалили из HTML выше. Обратите внимание на значение currentColor. Это значение очень удобно использовать именно в таких случаях, когда цвет иконки и цвет текста совпадает. Мы явно говорим «возьми цвет текста текущего элемента и подставь сюда», поэтому цвета иконки и текста совпадают. И более того, когда мы поменяем цвет текста (в активном элементе или при наведении), цвет иконки тоже поменяется. И если пойти еще дальше, то даже transition от текста передастся к иконке.

Помимо стилей ссылок мы сделаем и активное состояние, и hover-состояние, причем, сделаем их одинаковыми. Кстати, обратите внимание на стиль написания transition. Мы явно указываем через запятую каждое свойство, которое хотим сделать плавным. Это кажется сложным, постоянно указывать все через запятую вместо all, но это также улучшает нам производительность. Мы буквально говорим браузеру «не трать ресурсы на все подряд, сделай плавным только это».

.nav__link svg {
  display: block;
  width: 20px;
  height: 20px;
  stroke: currentColor;
}

.nav__link:hover {
  color: #f8fafc;
  background: rgba(59, 130, 246, 0.15);
}

.nav__link--active {
  color: #f8fafc;
  background: rgba(59, 130, 246, 0.15);
  pointer-events: none;
}

Важный момент здесь — свойство pointer-events: none. С его помощью мы «отключаем» возможность взаимодействия с элементом, то есть, на него больше нельзя нажать. Это сделано намеренно, ведь элемент уже активный, нет смысла делать его активным еще раз.

Теперь вы умеете создавать вертикальное меню навигации с активным пунктом, выравнивать иконки с текстом и оформлять список ссылок. Попробуйте добавить новые разделы или изменить активный элемент.

Решение задачи

предыдущий пост следующий пост