Сегодня мы займёмся созданием адаптивного меню. Оно будет автоматически подстраиваться под то устройство, на котором будет просматриваться: будь то обычный монитор, экран планшета или мобильного телефона. В качестве иконок будем использовать специальный шрифт для того чтобы изображения не зависили от количества пикселей.
Подготовка шрифта
Вообще создать шрифт из иконок несколько сложновато, однако с инструментом
IcoMoon данный процесс превращается в парочку пустяков. Иконочный шрифт ведёт себя точно так же как и обычный, так что мы без проблем можем задавать ему любой цвет и размер. Идеальное решение для дисплеев мобильных устройств с высоким разрешением.
В первую очередь, нам необходимо создать нужные иконки. Я использовал Illustrator, но для этого может подойти любой редактор векторной графики, к примеру Inkscape. Далее все иконки необходимо экспортировать в файл с форматом SVG.
Чтобы не париться, мы можем выбрать иконки из тех, что нам предоставляет сам сервис IcoMoon. После всех предварительных действий, нажимаем на кнопку “Font” для ввода дополнительной информации. Тут мы можем определиться с кодировкой, а так же задать каждой иконке свою букву.
После нажатия на кнопку “Download” нам отдадут на скачку ZIP файл с 4 файлами (SVG, EOT, TTF и WOFF), и демо страницу с уже готовым использованием шрифта в CSS.
HTML
Вот так выглядит HTML код нашего меню:
<nav id="menu" class="nav">
<ul>
<li>
<a href="#" title="">
<span class="icon"> <i aria-hidden="true" class="icon-home"></i></span><span>Home</span>
</a>
</li>
<li>
<a href="#" title=""><span class="icon"> <i aria-hidden="true" class="icon-services"></i></span><span>Services</span></a>
</li>
<li>
<a href="#" title=""><span class="icon"><i aria-hidden="true" class="icon-portfolio"></i></span><span>Portfolio</span></a>
</li>
<li>
<a href="#" title=""><span class="icon"><i aria-hidden="true" class="icon-blog"></i></span><span>Blog</span></a>
</li>
<li>
<a href="#" title=""><span class="icon"><i aria-hidden="true" class="icon-team"></i></span><span>The team</span></a>
</li>
<li>
<a href="#" title=""><span class="icon"><i aria-hidden="true" class="icon-contact"></i></span><span>Contact</span></a>
</li>
</ul>
</nav>
Для использования иконочного шрифта, мы просто добавляем элементам соответствующие классы, типа “icon-iconname” (то же самое можно сделать и с элементом span). Так же, прошу заметить, что мы добавили класс no-js к элементу body для того чтобы позже заюзать его через Modernizr. Наша задача состоит в том, чтобы при отключённом JavaScript, меню оставалось в рабочем состоянии.
CSS И JAVASCRIPT
Заметьте, что из данных листингов я убрал все браузерные префиксы. Полный код можно найти в исходниках.
В первую очередь пишем CSS, который применим для всех видов экранов:
/* глобальный CSS, который предназначен для всех видов экранов */
.nav ul {
max-width: 1240px;
margin: 0;
padding: 0;
list-style: none;
font-size: 1.5em;
font-weight: 300;
}
.nav li span {
display: block;
}
.nav a {
display: block;
color: rgba(249, 249, 249, .9);
text-decoration: none;
transition: color .5s, background .5s, height .5s;
}
.nav i{
/* Make the font smoother for Chrome */
transform: translate3d(0, 0, 0);
}
a, button {
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
При наведении мыши по одному из пунктов меню, несколько изменим отображение остальных. Вот и код:
/* стили для эффекта hover */
.no-touch .nav ul:hover a {
color: rgba(249, 249, 249, .5);
}
.no-touch .nav ul:hover a:hover {
color: rgba(249, 249, 249, 0.99);
}
Затем я хочу назначить какой-то фон всем элементам. В коде я использую конструкцию nth-child для извлечения нужных элементов. Таким образом мы можем добавить и оформить столько элементов меню, сколько нужно.
.nav li:nth-child(6n+1) {
background: rgb(208, 101, 3);
}
.nav li:nth-child(6n+2) {
background: rgb(233, 147, 26);
}
.nav li:nth-child(6n+3) {
background: rgb(22, 145, 190);
}
.nav li:nth-child(6n+4) {
background: rgb(22, 107, 162);
}
.nav li:nth-child(6n+5) {
background: rgb(27, 54, 71);
}
.nav li:nth-child(6n+6) {
background: rgb(21, 40, 54);
}
Используя медиа запрос min-width, мы можем написать отдельные стили для тех устройств, где ширина экрана больше 800px (50em, с размером шрифта 15px); преобразуем меню в горизонтальный вид:
@media (min-width: 50em) {
/* трансформируем меню в горизонтальный вид */
.nav li {
float: left;
width: 16.66666666666667%;
text-align: center;
transition: border .5s;
}
.nav a {
display: block;
width: auto;
}
Далее будем так же выбирать элементы через nth-child, для того чтобы добавить рамку в 4px различного цвета для каждого элемента меню. Применять данный эффект будем при активации состояний hover и active.
.no-touch .nav li:nth-child(6n+1) a:hover,
.no-touch .nav li:nth-child(6n+1) a:active,
.no-touch .nav li:nth-child(6n+1) a:focus {
border-bottom: 4px solid rgb(174, 78, 1);
}
.no-touch .nav li:nth-child(6n+2) a:hover,
.no-touch .nav li:nth-child(6n+2) a:active,
.no-touch .nav li:nth-child(6n+2) a:focus {
border-bottom: 4px solid rgb(191, 117, 20);
}
.no-touch .nav li:nth-child(6n+3) a:hover,
.no-touch .nav li:nth-child(6n+3) a:active,
.no-touch .nav li:nth-child(6n+3) a:focus {
border-bottom: 4px solid rgb(12, 110, 149);
}
.no-touch .nav li:nth-child(6n+4) a:hover,
.no-touch .nav li:nth-child(6n+4) a:active,
.no-touch .nav li:nth-child(6n+4) a:focus {
border-bottom: 4px solid rgb(10, 75, 117);
}
.no-touch .nav li:nth-child(6n+5) a:hover,
.no-touch .nav li:nth-child(6n+5) a:active,
.no-touch .nav li:nth-child(6n+5) a:focus {
border-bottom: 4px solid rgb(16, 34, 44);
}
.no-touch .nav li:nth-child(6n+6) a:hover,
.no-touch .nav li:nth-child(6n+6) a:active,
.no-touch .nav li:nth-child(6n+6) a:focus {
border-bottom: 4px solid rgb(9, 18, 25);
}
Затем оформим иконки и текст:
/* Положения иконок */
.icon {
padding-top: 1.4em;
}
.icon + span {
margin-top: 2.1em;
transition: margin .5s;
}
Анимируем высоту элементов при наведении мыши:
/* Анимируем высоту элементов */
.nav a {
height: 9em;
}
.no-touch .nav a:hover ,
.no-touch .nav a:active ,
.no-touch .nav a:focus {
height: 10em;
}
.no-touch .nav a:hover .icon + span {
margin-top: 3.2em;
transition: margin .5s;
}
Позиционируем иконки и готовим для CSS анимации:
/* Позиционируем иконки и подготавливаем их к анимации */
.nav i {
position: relative;
display: inline-block;
margin: 0 auto;
padding: 0.4em;
border-radius: 50%;
font-size: 1.8em;
box-shadow: 0 0 0 0.8em transparent;
background: rgba(255,255,255,0.1);
transform: translate3d(0, 0, 0);
transition: box-shadow .6s ease-in-out;
}
Для достижения нужного нам эффекта, мы анимируем исчезновение тени у самого блока, и изменим её размер с 0.8em до 0, а также поработаем над цветом. После этого закрываем наш первый медиа запрос.
/* Для эффекта, анимируем тень */
.no-touch .nav a:hover i,
.no-touch .nav a:active i,
.no-touch .nav a:focus i {
box-shadow: 0 0 0px 0px rgba(255,255,255,0.2);
transition: box-shadow .4s ease-in-out;
}
Второй медиа запрос будет предназначен для стилизации меню на экранах размер которых варьирует в промежутке от 800 до 980px:
@media (min-width: 50em) and (max-width: 61.250em) {
/* Изменяем отображение текста */
.nav ul {
font-size: 1.2em;
}
}
Теперь, когда версия для больших экранов готова, мы можем приступить к реализации стилей для экранов поменьше. На этот раз интересующий нас размер начинается от 800px, который эквивалентен 49.938em.
/* версия для планшетов и мобильных экранов */
@media (max-width: 49.938em) {
.no-touch .nav ul li:nth-child(6n+1) a:hover,
.no-touch .nav ul li:nth-child(6n+1) a:active,
.no-touch .nav ul li:nth-child(6n+1) a:focus {
background: rgb(227, 119, 20);
}
.no-touch .nav li:nth-child(6n+2) a:hover,
.no-touch .nav li:nth-child(6n+2) a:active,
.no-touch .nav li:nth-child(6n+2) a:focus {
background: rgb(245, 160, 41);
}
.no-touch .nav li:nth-child(6n+3) a:hover,
.no-touch .nav li:nth-child(6n+3) a:active,
.no-touch .nav li:nth-child(6n+3) a:focus {
background: rgb(44, 168, 219);
}
.no-touch .nav li:nth-child(6n+4) a:hover,
.no-touch .nav li:nth-child(6n+4) a:active,
.no-touch .nav li:nth-child(6n+4) a:focus {
background: rgb(31, 120, 176);
}
.no-touch .nav li:nth-child(6n+5) a:hover,
.no-touch .nav li:nth-child(6n+5) a:active,
.no-touch .nav li:nth-child(6n+5) a:focus {
background: rgb(39, 70, 90);
}
.no-touch .nav li:nth-child(6n+6) a:hover,
.no-touch .nav li:nth-child(6n+6) a:active,
.no-touch .nav li:nth-child(6n+6) a:focus {
background: rgb(32, 54, 68);
}
.nav ul li {
transition: background 0.5s;
}
}
На экранах, размеры которых варьируют между 520px (32.5em) и 799px (49.938em), будет отображать наше меню в 2 колонки и три строчки. Так же увеличим внутренние отступы элементов для того чтобы на сенсорных устройствах по ним можно было легко попасть.
/* CSS для версии колонок 2x3 */
@media (min-width: 32.5em) and (max-width: 49.938em) {
.nav li {
display: block;
float: left;
width: 50%;
}
.nav a {
padding: 0.8em;
}
.nav li span,
.nav li span.icon {
display: inline-block;
}
.nav li span.icon {
width: 50%;
}
.nav li .icon + span {
font-size: 1em;
}
.icon + span {
position: relative;
top: -0.2em;
}
На маленьких экранах нам не нужна красочная анимация, поэтому тут мы её упростим. После этого закрываем меда запрос.
/* анимируем иконки под новые размеры */
.nav li i {
display: inline-block;
padding: 8% 9%;
border: 4px solid transparent;
border-radius: 50%;
font-size: 1.5em;
background: rgba(255,255,255,0.1);
transition: border .5s;
}
.no-touch .nav li:hover i,
.no-touch .nav li:active i,
.no-touch .nav li:focus i {
border: 4px solid rgba(255,255,255,0.1);
}
Опять же, для небольших экранов мы изменим ширину блоков и размер текста.
/* Адаптируем размеры под самые маленькие экраны */
@media (min-width: 32.5em) and (max-width: 38.688em) {
.nav li span.icon {
width: 50%;
}
.nav li .icon + span {
font-size: 0.9em;
}
}
Для совсем мелких экранов, мы отобразим специальную кнопку, при клике на которую, будет появляться меню. Реализовать это можно с помощью JavaScript:
// Функция для смены класса
var changeClass = function (r,className1,className2) {
var regex = new RegExp("(?:^|\\s+)" + className1 + "(?:\\s+|$)");
if( regex.test(r.className) ) {
r.className = r.className.replace(regex,' '+className2+' ');
}
else{
r.className = r.className.replace(new RegExp("(?:^|\\s+)" + className2 + "(?:\\s+|$)"),' '+className1+' ');
}
return r.className;
};
// Создаём кнопки для маленьких экранов
var menuElements = document.getElementById('menu');
menuElements.insertAdjacentHTML('afterBegin','<button type="button" id="menutoggle" class="navtoogle" aria-hidden="true"><i aria-hidden="true" class="icon-menu"> </i> Menu</button>');
// Меняем класс при клике для отображения / скрытия меню
document.getElementById('menutoggle').onclick = function() {
changeClass(this, 'navtoogle active', 'navtoogle');
}
// клик по документу
document.onclick = function(e) {
var mobileButton = document.getElementById('menutoggle'),
buttonStyle = mobileButton.currentStyle ? mobileButton.currentStyle.display : getComputedStyle(mobileButton, null).display;
if(buttonStyle === 'block' && e.target !== mobileButton && new RegExp(' ' + 'active' + ' ').test(' ' + mobileButton.className + ' ')) {
changeClass(mobileButton, 'navtoogle active', 'navtoogle');
}
}
Для того чтобы не захламлять HTML, я предпочёл добавлять эту кнопку через JavaScript. Функция changeClass поможет нам в добавлении/удалении класса, которое нужно будет произвести, при клике по кнопке.
Теперь, когда мы позаботились о разных типах экранов, можем приступить к стилизации самого меню средствами CSS. Задаём стили для кнопок:
.nav .navtoogle{
display: none;
width: 100%;
padding: 0.5em 0.5em 0.8em;
font-family: 'Lato',Calibri,Arial,sans-serif;
font-weight: normal;
text-align: left;
color: rgb(7, 16, 15);
font-size: 1.2em;
background: none;
border: none;
border-bottom: 4px solid rgb(221, 221, 221);
cursor: pointer;
}
.navtoogle i{
z-index:-1;
}
.icon-menu {
position: relative;
top: 3px;
line-height: 0;
font-size: 1.6em;
}
По умолчанию, кнопка вызова меню скрыта. Её будем отображать только для экранов, размер которых меньше 519px (32.438em):
@media (max-width: 32.438em) {
.nav .navtoogle{
margin: 0;
display: block;
}
При клике по кнопке, будем анимировать высоту навигационной панели. Если JavaScript не включён, то кнопка отображена не будет, т.к. мы используем класс no-js.
/* Анимируем высоту навигации при клике по кнопке */
/* If JavaScript is disabled, the menu stays open */
.no-js .nav ul {
max-height: 30em;
overflow: hidden;
}
При включённом JavaScript мы изначально прячем меню, а отображаем только после того, как пользователь нажмёт на соответствующую кнопку, добавляя класс active:
/* при включённом JavaScript, прячем меню */
.js .nav ul {
max-height: 0em;
overflow: hidden;
}
/* Отображаем меню при клике по кнопке */
.js .nav .active + ul {
max-height: 30em;
overflow: hidden;
transition: max-height .4s;
}
Для мелких экранов иконки отобразим справа, а текст слева:
/* адаптируем отображение меню для небольших мониторов: иконки слева, текст справа */
.nav li span {
display: inline-block;
height: 100%;
}
.nav a {
padding: 0.5em;
}
.icon + span {
margin-left: 1em;
font-size: 0.8em;
}
Так же добавим рамку в 8px с левой стороны каждого элементов меню.
/* добавляем по 8 пикселей к левой границе каждого элемента меню */
.nav li:nth-child(6n+1) {
border-left: 8px solid rgb(174, 78, 1);
}
.nav li:nth-child(6n+2) {
border-left: 8px solid rgb(191, 117, 20);
}
.nav li:nth-child(6n+3) {
border-left: 8px solid rgb(13, 111, 150);
}
.nav li:nth-child(6n+4) {
border-left: 8px solid rgb(10, 75, 117);
}
.nav li:nth-child(6n+5) {
border-left: 8px solid rgb(16, 34, 44);
}
.nav li:nth-child(6n+6) {
border-left: 8px solid rgb(9, 18, 25);
}
На данном этапе, меню отлично смотрится на обычных мониторах. Однако на мобильных устройствах по элементам меню сложно попасть пальцами. С помощью Modernizr мы можем узнать если устройство является сенсорным. Если да, то документу будет подключён класс touch. Зная это, мы можем поработать над размерами элементов меню. Тут и закрываем наш последний media запрос.
/* делаем навигацию крупнее для сенсорных экранов */
.touch .nav a {
padding: 0.8em;
}
На этом всё! Надеемся, вам понравилось!