Главная > Меню и Навигация > Меню сайта в стиле Google Nexus

Меню сайта в стиле Google Nexus


8 августа 2013, 12:45. Разместил: Design FactoRy
Сегодня мы хотим рассказать вам о том, как воссоздать боковое меню со страницы Google Nexus 7. Оно выезжает в сопровождении очень красивого эффекта. При наведении на специальную иконку меню, также открываются и иконки меню. При нажатии по этой кнопке, вам будет отображено полное боковое меню. Первым пунктом данного меню является поиск, оформленный таким же образом, как и другие пункты меню.

Меню сайта в стиле Google Nexus

Мы воссоздадим это меню при помощи ненумерованных, вложенных списков и нескольких CSS-переходов. Мы воспользуемся javascript, чтобы применить классы для эффектов открывания и управления событиями при наведении и нажатии. При помощи jQuery мы настраиваем размер меню, что имеет огромное значение для небольших по размеру устройств.

Приступаем!

Разметка

Наше меню будет состоять их 2 основных элементов: основное меню – то, которое вы можете видеть вверху страницы в виде шапки сайта, и боковое меню. Первому меню мы зададим класс gn-menu-main, и обернем второе меню в элемент ‘nav’. Конечно же, вы можете использовать ту структуру, которую вам хочется.
Первый пункт меню будет содержать анкор иконки меню и элемент ‘nav’:

<ul id="gn-menu" class="gn-menu-main">
<li class="gn-trigger">
<a class="gn-icon gn-icon-menu"><span>Menu</span></a>
<nav class="gn-menu-wrapper">
<!-- ... -->
</nav>
</li>
<li><a href="http://tympanus.net/codrops">Codrops</a></li>
<li><!-- ... --></li>
<!-- ... -->
</ul>

Внутрь элемента nav мы добавим еще одну оболочку, которая поможет нам скрывать никчемный скроллинг в браузерах windows. Ядром нашего меню будет ненумерованный список с классом gn-menu. Он будет состоять из элементов списка, некоторые из которых будут иметь дополнительный список. Первый пункт меню будет представлен в виде строки поиска:

<div class="gn-scroller">
<ul class="gn-menu">
<li class="gn-search-item">
<input placeholder="Search" type="search" class="gn-search">
<a class="gn-icon gn-icon-search"><span>Search</span></a>
</li>
<li>
<a class="gn-icon gn-icon-download">Downloads</a>
<ul class="gn-submenu">
<li><a class="gn-icon gn-icon-illustrator">Vector Illustrations</a></li>
<li><a class="gn-icon gn-icon-photoshop">Photoshop files</a></li>
</ul>
</li>
<li><a class="gn-icon gn-icon-cog">Settings</a></li>
<li><!-- ... --></li>
<!-- ... -->
</ul>
</div><!-- /gn-scroller -->

Теперь, давайте все это оформим.

CSS-код

Обратите внимание на то, что CSS не содержит браузерных префиксов, но в исходный файлах они присутствуют.

Давайте начнем с указания border-box для всех свойств box-sizing:

*,
*:after,
*::before {
box-sizing: border-box;
}

Так как мы будем использовать иконический шрифт для иконок, нам нужно открыть IcoMoon, и выбрать несколько красивых иконок из набора Eco Ico от Matthew Skiles.

@font-face {
font-weight: normal;
font-style: normal;
font-family: 'ecoicons';
src: url("../fonts/ecoicons/ecoicons.eot");
src: url("../fonts/ecoicons/ecoicons.eot?#iefix") format("embedded-opentype"), url("../fonts/ecoicons/ecoicons.woff") format("woff"), url("../fonts/ecoicons/ecoicons.ttf") format("truetype"), url("../fonts/ecoicons/ecoicons.svg#ecoicons") format("svg");
}

Затем мы воспользуемся псевдо-элементом для того, чтобы добавить иконки в анкоры.

Но для начала давайте оформим все списки:

.gn-menu-main,
.gn-menu-main ul {
margin: 0;
padding: 0;
background: white;
color: #5f6f81;
list-style: none;
text-transform: none;
font-weight: 300;
font-family: 'Lato', Arial, sans-serif;
line-height: 60px;
}

Здесь представлены базовые (reset) стили для списков и подсписков.

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

.gn-menu-main {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 60px;
font-size: 13px;
}

Базовые стили для всех ссылок в нашем меню и подменю будут следующими:

.gn-menu-main a {
display: block;
height: 100%;
color: #5f6f81;
text-decoration: none;
cursor: pointer;
}

Давайте также определим стили при наведении курсора мыши, в которых мы инвертируем цвета. Для первого пункта бокового меню, которым будет наш поиск, нам понадобится отдельный стиль при наведении. Здесь у нас не будет анкора, который будет заполнять пункт списка, поэтому давайте определим hover-стиль для li и укажем, что будет происходить с иконкой (анкором) и самим элементом li:

.no-touch .gn-menu-main a:hover,
.no-touch .gn-menu li.gn-search-item:hover,
.no-touch .gn-menu li.gn-search-item:hover a {
background: #5f6f81;
color: white;
}

Дочерний элемент пункта списка будет выравнен по левой стороне, и будет оформлен правой границей:

.gn-menu-main > li {
display: block;
float: left;
height: 100%;
border-right: 1px solid #c6d0da;
text-align: center;
}

Первый пункт списка будет представлять собой специальный trigger-пункт, и так как нам нужно скрыть текст и использовать псевдо-элемент для иконки меню, мы установим свойство user-select на none, а свойству ширины (width) зададим то же значение, что указано в высоте всего пункта.

.gn-menu-main li.gn-trigger {
position: relative;
width: 60px;
user-select: none;
}

Последний пункт нашего основного списка будет выравнен по правому краю, и нам также нужно будет слегка изменить границу:

.gn-menu-main > li:last-child {
float: right;
border-right: none;
border-left: 1px solid #c6d0da;
}

Анкоры основного меню будут оформлены небольшими отступами, а также мы применим немного другой стиль текста:

.gn-menu-main > li > a {
padding: 0 30px;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: bold;
}

Давайте удалим выравнивания при помощи следующего миниатюрного хака clearfix от Nicolas Gallagher:

.gn-menu-main:after {
display: table;
clear: both;
content: '';
}

Итак, на данный момент у нас отсутствуют стили для иконки меню, но давайте оставим это на потом – мы применим их после того, как определим другие псевдо-классы иконки.

Давайте перейдем к оболочке бокового меню. Зачем нужны дополнительные оболочки? Что же, если вас не смущает не нужная полоса прокрутки, то можете просто опустить эти стили, и просто указать overflow-y: scroll. Но так как полоса прокрутки действительно преломляет наш минималистский дизайн в браузерах Windows, нам нужно воспользоваться этим небольшим трюком. Мы выставляем свойство overflow оболочки на hidden, с определенной шириной (изначально этой ширины достаточно для того, чтобы отобразить иконки). Затем мы задаем оболочке скроллинга большую ширину и высоту равную 100%. Полоса прокрутки будет скрыта. Затем наше меню будет расширяться до необходимой высоты, и его можно будет прокручивать.
Изначально нам нужно скрыть меню, поэтому мы зададим ему значение negative left (равное его ширине). Почему мы не воспользуемся здесь параметром 2d translate?

.gn-menu-wrapper {
position: fixed;
top: 60px;
bottom: 0;
left: 0;
overflow: hidden;
width: 60px; /* will be transitioned to 340px */
border-top: 1px solid #c6d0da;
background: white;
transform: translateX(-60px); /* will be transitioned to 0px */
transition: transform 0.3s, width 0.3s;
}

.gn-scroller {
position: absolute;
overflow-y: scroll;
width: 370px;
height: 100%;
}

.gn-menu {
border-bottom: 1px solid #c6d0da;
text-align: left;
font-size: 18px;
}

Давайте добавим тень блока, чтобы отделить пункты списка. Это поможет нам избежать двойных линий при скрывании пунктов дополнительного меню:

.gn-menu li:not(:first-child),
.gn-menu li li {
box-shadow: inset 0 1px #c6d0da
}

Давайте добавим переход к пунктам списка дополнительного меню, и выставим их изначальную высоту на 0:

.gn-submenu li {
overflow: hidden;
height: 0;
transition: height 0.3s;
}

Цвет здесь будет немного светлее родительских пунктов меню:

.gn-submenu li a {
color: #c1c9d1
}

Теперь давайте стилизуем отдельный пункт с поиском и поле ввода запроса. Нам нужно, чтобы оно было едва заметным, как это реализовано на странице Google Nexus, поэтому мы зададим ему прозрачный цвет фона и сделаем так, чтобы placeholder-элементы выглядели как обычные пункты меню:

input.gn-search {
position: relative;
z-index: 10;
padding-left: 60px;
outline: none;
border: none;
background: transparent;
color: #5f6f81;
font-weight: 300;
font-family: 'Lato', Arial, sans-serif;
cursor: pointer;
}

/* placeholder */

.gn-search::-webkit-input-placeholder {
color: #5f6f81
}

.gn-search:-moz-placeholder {
color: #5f6f81
}

.gn-search::-moz-placeholder {
color: #5f6f81
}

.gn-search:-ms-input-placeholder {
color: #5f6f81
}

Большинство браузеров будут скрывать placeholder при клике по полю ввода, что гораздо лучше дает пользователям понять, что это поле ввода. В Chrome такого не наблюдается, поэтому мы воспользуемся этим небольшим трюком для симуляции подобного эффекта, установив цвет placeholder на прозрачный после того, как пользователь кликает по полю ввода:

.gn-search:focus::-webkit-input-placeholder,
.no-touch .gn-menu li.gn-search-item:hover .gn-search:focus::-webkit-input-placeholder {
color: transparent
}

input.gn-search:focus {
cursor: text
}

При наведении мы изменяем цвет текста ввода на белый, - то же самое, что мы делали на других анкорах (это текст, который вводит пользователь):

.no-touch .gn-menu li.gn-search-item:hover input.gn-search {
color: white
}

То же самое мы делаем и для текста placeholder:

/* placeholder */

.no-touch .gn-menu li.gn-search-item:hover .gn-search::-webkit-input-placeholder {
color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search:-moz-placeholder {
color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search::-moz-placeholder {
color: white
}

.no-touch .gn-menu li.gn-search-item:hover .gn-search:-ms-input-placeholder {
color: white
}

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

.gn-menu-main a.gn-icon-search {
position: absolute;
top: 0;
left: 0;
height: 60px;
}

Теперь давайте оформим псевдо элемент ::before для иконок. Мы зададим им параметр inline-block и ширину в 60 пикселей. Нам нужно сбросить все стили шрифта, так как сейчас мы будем использовать наш иконический шрифт, который мы указали в самом начале нашего CSS-кода:

.gn-icon::before {
display: inline-block;
width: 60px;
text-align: center;
text-transform: none;
font-weight: normal;
font-style: normal;
font-variant: normal;
font-family: 'ecoicons';
line-height: 1;
speak: none;
-webkit-font-smoothing: antialiased;
}

Давайте определим содержимое для всех иконок:

.gn-icon-help::before {
content: "\e000"
}

.gn-icon-cog::before {
content: "\e006"
}

.gn-icon-search::before {
content: "\e005"
}

.gn-icon-download::before {
content: "\e007"
}

.gn-icon-photoshop::before {
content: "\e001"
}

.gn-icon-illustrator::before {
content: "\e002"
}

.gn-icon-archive::before {
content: "\e00d"
}

.gn-icon-article::before {
content: "\e003"
}

.gn-icon-pictures::before {
content: "\e008"
}

.gn-icon-videos::before {
content: "\e009"
}

В целом, нам нужно, чтобы текст анкора отображался рядом с иконкой, но иногда нам нужно бдует просто отобразить иконку. Но нам не нужен просто пустой анкор, в HTML-коде должен быть текст. Поэтому мы оборачиваем эти особые случаи в span-элемент, который затем скрываем, устанавливая ширину и высоту на 0, а свойство overflow на hidden. Почему бы просто не использовать display: none? Скрывание контента таким образом может привести к тому, что его невозможно будет достать через экранные читалки, поэтому давайте удостоверимся в том, что мы не «стираем» что-либо важное для них:

.gn-icon span {
width: 0;
height: 0;
display: block;
overflow: hidden;
}

Давайте также не забывать о нашей небольшой иконке меню в нашем основном меню. Итак, здесь мы не будем использовать иконку из иконического шрифта, но тем не менее, вы, конечно же, можете это сделать. Вместо этого мы создадим ее при помощи box-shadow, который будет изменять цвета (фоновый цвет и синий цвет), чтобы создать иллюзию 3 линий. Здесь вы по желанию также можете использовать градиент.

.gn-icon-menu::before {
margin-left: -15px;
vertical-align: -2px;
width: 30px;
height: 3px;
background: #5f6f81;
box-shadow: 0 3px white, 0 -6px #5f6f81, 0 -9px white, 0 -12px #5f6f81;
content: '';
}

При наведении мы просто инвертируем цвета тени блока:

.no-touch .gn-icon-menu:hover::before,
.no-touch .gn-icon-menu.gn-selected:hover::before {
background: white;
box-shadow: 0 3px #5f6f81, 0 -6px white, 0 -9px #5f6f81, 0 -12px white;
}

А когда на меню стоит выделение (боковое меню открыто), мы добавляем еще больше синего:

.gn-icon-menu.gn-selected::before {
background: #5993cd;
box-shadow: 0 3px white, 0 -6px #5993cd, 0 -9px white, 0 -12px #5993cd;
}

Последнее, что нам нужно сделать, это определить два класса для открывания меню и отображения только иконок, а также для отображения всего меню сразу. Давайте назовем этот класс gn-open-part. Другой же класс, gn-open-all, мы применяем либо при клике по иконке основного меню, либо при наведении по области с иконками.
В обоих случаях, нам нужно будет сбросить translate на 0:

.gn-menu-wrapper.gn-open-all,
.gn-menu-wrapper.gn-open-part {
transform: translateX(0px);
}

Если мы хотим открыть все меню, нам нужно будет установить правильную ширину:

.gn-menu-wrapper.gn-open-all {
width: 340px;
}

Открытие полного меню также раскроет и все подменю:

.gn-menu-wrapper.gn-open-all .gn-submenu li {
height: 60px;
}

Последнее, но не менее важное – это media query, который позволит сделать так, что меню будет использовать всю ширину экрана:

@media screen and (max-width: 422px) { 
.gn-menu-wrapper.gn-open-all {
transform: translateX(0px);
width: 100%;
}

.gn-menu-wrapper.gn-open-all .gn-scroller {
width: 130%;
}
}

Мы также настраиваем ширину оболочки скроллинга таким образом, чтобы она была больше 100%. Это не особо важно, так как мы и так не будем видеть скроллинги на большинстве устройств данного размера.

Итак, теперь мы разобрались со стилизацией, и нам нужно добавить некоторый код javascript для логического процесса открывания и закрывания меню (то есть, для применения классов).

javascript-код

Итак, давайте создадим небольшой скрипт, который позаботится о функционале меню. Когда мы наводим на иконку меню, нам нужно, чтобы первая часть меню выезжала, чтобы мы могли видеть иконки. Если мы наводим на область бокового меню или кликаем по иконке основного меню, тогда перед нами появляется все остальное. Повторное нажатие по иконке меню или нажатие по любой другой области на всей странице приводит к тому, что меню закрывается обратно. Давайте посмотрим, как мы можем все это реализовать.

Мы начинаем с кеширования элементов и инициализации некоторых переменных. Функция bodyClickFn определяет, что происходит, когда меню открыто и мы кликаем по какой-либо обалсти в документе. Нам также следует позаботиться о событиях touch.

_init : function() {
this.trigger = this.el.querySelector( 'a.gn-icon-menu' );
this.menu = this.el.querySelector( 'nav.gn-menu-wrapper' );
this.isMenuOpen = false;
this.eventtype = mobilecheck() ? 'touchstart' : 'click';
this._initEvents();

var self = this;
this.bodyClickFn = function() {
self._closeMenu();
this.removeEventListener( self.eventtype, self.bodyClickFn );
};
}

Давайте взглянем на события, которые нам нужно инициализировать. Нам нужно открыть первую часть меню (давайте назовем ее меню иконок), когда курсор наведен на иконку-триггер основного меню. Когда мы перемешаем мышь в другую область, это же меню должно закрываться обратно.

this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } );
this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } );

Как только меню иконок окажется в окне просмотра, наведении по нему приведет к открытию полной версии меню. После того, как оно будет отображено, и мы кликнем где-либо в документе, меню обратно закроется. Нам нужно привязать соответствующее событие (click или touchstart) к документу.

this.menu.addEventListener( 'mouseover', function(ev) {
self._openMenu();
document.addEventListener( self.eventtype, self.bodyClickFn );
} );

Наконец, если мы кликаем по иконке меню, нам нужно, чтобы все меню было отображено. Нам также нужно привязать соответствующее событие (click или touchstart).

this.trigger.addEventListener( this.eventtype, function( ev ) {
ev.stopPropagation();
ev.preventDefault();
if( self.isMenuOpen ) {
self._closeMenu();
document.removeEventListener( self.eventtype, self.bodyClickFn );
}
else {
self._openMenu();
document.addEventListener( self.eventtype, self.bodyClickFn );
}
} );

И последнее: нам не нужно, чтобы меню закрывалось, когда мы кликаем где-либо за пределами меню. Так как мы привязываем к документу событие click/touchstart (при закрывании меню), нам нужно следать следующее:

this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } );

И здесь предложена последняя функция _initEvents и методы для открывания и закрывания меню.

_initEvents : function() {
var self = this;

if( !mobilecheck() ) {
this.trigger.addEventListener( 'mouseover', function(ev) { self._openIconMenu(); } );
this.trigger.addEventListener( 'mouseout', function(ev) { self._closeIconMenu(); } );

this.menu.addEventListener( 'mouseover', function(ev) {
self._openMenu();
document.addEventListener( self.eventtype, self.bodyClickFn );
} );
}
this.trigger.addEventListener( this.eventtype, function( ev ) {
ev.stopPropagation();
ev.preventDefault();
if( self.isMenuOpen ) {
self._closeMenu();
document.removeEventListener( self.eventtype, self.bodyClickFn );
}
else {
self._openMenu();
document.addEventListener( self.eventtype, self.bodyClickFn );
}
} );
this.menu.addEventListener( this.eventtype, function(ev) { ev.stopPropagation(); } );
},
_openIconMenu : function() {
classie.add( this.menu, 'gn-open-part' );
},
_closeIconMenu : function() {
classie.remove( this.menu, 'gn-open-part' );
},
_openMenu : function() {
if( this.isMenuOpen ) return;
classie.add( this.trigger, 'gn-selected' );
this.isMenuOpen = true;
classie.add( this.menu, 'gn-open-all' );
this._closeIconMenu();
},
_closeMenu : function() {
if( !this.isMenuOpen ) return;
classie.remove( this.trigger, 'gn-selected' );
this.isMenuOpen = false;
classie.remove( this.menu, 'gn-open-all' );
this._closeIconMenu();
}

И на этом все! Спасибо за чтение, и надеемся, что вам понравилось данное руководство!

Посмотреть демо | Скачать исходный код
Внимание! У вас нет прав для просмотра скрытого текста.

Вернуться назад