Как сверстать крутой модный сайт на flexbox

В этом уроке мы будем учиться верстать с помощью flexbox. Для удобства использования, приведу содержание:

Предыстория

Всякий, кто верстает сайты хотя бы несколько лет (ещё лучше, если прям с античных времён, с нулевых или, даже, с девяностых) — помнит кромешный ад, когда сайты верстались таблицами. Ад этот жил так долго и так крепко вошёл в сознание «верстунов», что кое-какие отголоски его доносятся даже до наших дней. Потом появилась «div’ная» вёрстка — прогрессивные ребята перешли на контейнеры div. Нельзя сказать, что стало значительно легче или веселее. Если таблица — она и в африке таблица, то универсальный контейнер div ещё нужно было заставить принять табличный вид.

Да что там, вспомните, сколько крови из мозолей на ваших пальцах вытекло, пока вы мучились со всеми этими float’ами, margin’ами и прочими прелестями CSS.
Понятно, что нужен был новый div, с блэкджеком и шлюхами. И он появился! И имя ему стало flexbox. Появился он уже давненько — первые черновики и идеи зародились ещё в 2008. В 2011 браузеры начали худо-бедно внедрять новые стандарты. Но куда там, в вебе просто не бывает. Поэтому в 2014-2015хх годах появилась более-менее полная поддержка flexbox, ну а теперь, наконец-то, можно говорить о том, чтобы верстать с помощью этого чуда сайты.

Что такое flexbox и как этим пользоваться

Что же такое flexbox? Flexbox — это, мать его, контейнер, который умеет выстраиваться в строку, в колонку, растягиваться и сжиматься, расползаться по странице и собираться в кучку. И при этом — самое главное! — он не трахает вам мозг своей принципиальностью («я контейнер, я буду только с новой строки и хрен ты меня заставишь сделать то, что тебе нужно!» — как бы говорит нам <div>). Принцип flexbox — сделать вам хорошо.

Теперь, давайте посмотрим, как использовать всё это счастье на практике.

Текст посередине экрана

Начнём с самого. Допустим, есть задача сверстать нормальный хипстерский сайт. А чем характерен хипстерский сайт? Правильно! Огромной картинкой на фоне и текстом посередине экрана. С помощью Flexbox эта задача решается неприлично просто.

.flex-wrapper {
	width:100%;
	height:100%;
	display:flex;
}
#block-center {
	margin:auto;
}

С правила display:flex; начинается магия. Оно приказывает блоку принять светлую сторону и избавить вас от страданий. А теперь посмотрите на правило margin:auto; — теперь автоматическое выравнивание работает не только по ширине, но и по высоте!

А вот так это выглядит на странице:

<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>

<div class="flex-wrapper">
    
<div id="block-center">Привет, я Flex-контейнер в центре экрана!</div>

</div>

</body>
</html>

Быстро, пока не отошли от шока, погнали дальше!

Колонки

Одна из самых больших болей вёрстки — колонки. Все эти бесконечные омерзительные float’ы, идиотские clearfx’ы по всему сайту — сплошное уродство.
Что мы делаем теперь? Всё просто.

.column-wrapper {
	display:flex;
	flex-direction:row;
	flex-wrap:no-wrap;
}
.col-2 {
	flex-basis:25%;
}
.col-3 {
	flex-basis:33%;
}
.col-4 {
	flex-basis:25%;
}

html:

<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>

<div class="column-wrapper">
    
<div class="col-4">1</div>

    
<div class="col-4">2</div>

    
<div class="col-4">3</div>

    
<div class="col-4">4</div>

</div>


</body>
</html>

И всё! Таким способом можно нафигачить сколько угодно колонок. И все они будут сами выстраиваться в строку, будут резиновыми (или фиксированными), не будут выпадать за пределы родительских блоков, не требуют сброса обтекания после себя и т.д.

А если нужна одна фиксированная и одна резиновая колонка? Да легко!

.col-fixed {
	flex-basis:300px;
}
.col-flex {
	flex-basis:100%;
}


<div class="column-wrapper">
    
<div class="col-fixed">1</div>

    
<div class="col-flex">2</div>

</div>

Как оно, а?
А можно и так:


<div class="column-wrapper">
    
<div class="col-fixed">1</div>

    
<div class="col-flex">2</div>

    
<div class="col-fixed">3</div>

</div>


А как сделать, чтобы вставить padding внутрь колонки и не считать её фактическую ширину/высоту? Вот так:

.column-wrapper {
	width:1170px;
	display:flex;
	flex-direction:row;
	flex-wrap:no-wrap;
}
.col-4 {
	flex-basis:25%;
	max-width:25%;
	padding:0 30px;
}

В чём сила, брат? Сила в том, что правило flex-basis — очень податливое. Если у блока будет место под полноценные 25% пространства, он займёт их. Если же ему нужно будет ужаться — он без проблем сделает это. А верхнюю планку ему как раз задаёт правило max-width. Аналогичным образом будет действовать и min-width.

Ну как, вы уже чувствуете приятное напряжение в паху? Отлично.

Карточки

Пойдём дальше. Что ещё приходится верстать довольно часто? Особенно на современных сайтах? Правильно, карточки!

.cards-wrapper {
	width:100%;
	display:flex;
	flex-direction:row;
	flex-wrap:wrap;
	justify-content:space-around;
}
.flex-card {
	flex-basis:200px;
	height:300px;
	border:1px solid red;
}

Что здесь происходит? .cards-wrapper — блок для карточек. Его правила:

flex-direction:row;

Отображение блоков в строку. Это значение по умолчанию, но для наглядности я его прописал.

flex-wrap:wrap;

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

justify-content:space-around;

Расставляет пространство вокруг карточек. Рассчитывается это пространство автоматически. Ну разве не прелесть?

html:

<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>

<div class="cards-wrapper">
    
<div class="flex-card">1</div>

    
<div class="flex-card">2</div>

    
<div class="flex-card">3</div>

    
<div class="flex-card">4</div>

    
<div class="flex-card">5</div>

    
<div class="flex-card">6</div>

    
<div class="flex-card">7</div>

    
<div class="flex-card">8</div>

</div>

</body>
</html>

Если автоматический подсчёт свободного пространства между карточками не устраивает, его можно задать и вручную.

.flex-card {
	flex-basis:200px;
	height:300px;
	margin:20px 50px;
	border:1px solid red;
}

В таком случае, все карточки можно просто выровнять по центру:

justify-content:center;

Или прижать к левому краю.

justify-content:flex-start;

А можно и к правому, это же Flexbox, детка, развлекайся, как хочешь.

justify-content:flex-end;

Теперь вёрстка вашего чудо-интернет-магазина, или убийцы Pinterest станет чище, быстрее и приятнее.

Функциональные элементы

Поиск

Посмотрим на другие функциональные элементы. Например, нужно красиво склеить строку поиска и кнопку. Раньше это делали таблицами или наяривали с помощью float.
А что теперь? А теперь это выглядит вот так:

.search-wrapper {
	width:300px;
	height:40px;
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
	justify-content:center;
	align-items:center;
}
.search-input {
	flex-basis:75%;
}
.search-button {
	flex-basis:25%;
}
.search-input,.search-button {
	height:auto;
}
input[type="text"] {
	width:100%;
	height:30px;
	line-height:30px;
	padding:0 15px;
	border-top-left-radius:10px;
	border-bottom-left-radius:10px;
}
input[type="submit"] {
	width:80px;
	height:30px;
	line-height:30px;
	padding:0 15px;
	border-top-right-radius:10px;
	border-bottom-right-radius:10px;
	background-color:#212121;
	color:#ffffff;
	border:none;
	outline:none;
}
<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>


<div class="search-wrapper">
    
<div class="search-input">
    	<input type="text" placeholder="Поиск">
    </div>

    
<div class="search-button">
        <input type="submit" value="GO">
    </div>

</div>

</body>
</html>

.search-wrapper — контейнер для элемента поиска, определяет общую ширину всего элемента. Здесь же может задаваться его положение и поведение на странице. Уже знакомым нам образом выставляем дочерние элементы в строку (flex-direction) и запрещаем перенос (flex-wrap). Затем определяем, на всякий случай, ширину контейнера поисковой строки и контейнера кнопки. Ну а далее просто определяем внешний вид обоих элементов. Там всё знакомо — иначе, что вы вообще тут делаете? Это уроки для взрослых верстальщиков!

Панель меню

А если нужно что-нибудь покрупнее? Например, верхняя панель, где в левой части будет логотип, а в правой — меню? Пожалуйста.

.panel-wrapper {
	display:flex;
	width:100%;
	justify-content:space-between;
}
.logo-part {
	flex-basis:20%;
	padding:0 20px;
}
.menu-part {
	flex-basis:80%;
}
.flex-menu {
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
	justify-content:flex-end;
	list-style:none;
}
.flex-menu li {
	flex-basis:150px;
	max-width:150px;
	height:35px;
	display:flex;
	align-items:center;
	justify-content:center;
	padding:10px 15px;
}

Для родительского блока задаём правило justify-content:space-between; которое автоматически растащит дочерние блоки по краям. Это нужно для того, чтобы блок с логотипом (.logo-part) прижался влево, а блок с меню (.menu-part) — вправо.

Само меню, за компанию, тоже сделаем с помощью флексов. Для этого списку <ul> зададим отображение flex, уберём буллиты, выставим элементы в строку, запретим перенос и прижмём их вправо (justify-content:flex-end;). Для <li> определим ширину, а также выровняем внутренний элемент <a> по высоте и ширине и зададим padding для красоты. Обратите внимание, как <li> ограничен по размеру с помощью max-width.

 

<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>

<div class="panel-wrapper">
    
<div class="logo-part">
        Здесь ваше лого
    </div>

    
<div class="menu-part">
        
<ul class="flex-menu">
            
<li><a href="#">Меню</a></li>

            
<li><a href="#">Которое</a></li>

            
<li><a href="#">Никуда</a></li>

            
<li><a href="#">Не</a></li>

            
<li><a href="#">Ведёт</a></li>

        </ul>

    </div>

</div>

</body>
</html>

 

Круто? Не то слово! Может быть, замахнуться на что-то ещё более крупное и серьёзное? М? Каркас страницы? Что вы думаете? Почему бы и нет, давайте пробовать!

Верстаем сайт на flexbox

 

body {
	display:flex;
	flex-direction:column;
	height:100%;
}
header {
	flex-basis:200px;
	flex-shrink:0;
}
main {
	flex-basis:auto;
	flex-shrink:0;
	flex-grow:1;
}
footer {
	flex-basis:150px;
	flex-shrink:0;
}
<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
    
<header>Это шапка</header>

    <main>Это тело страницы</main>
    
<footer>А это подвал</footer>

</body>
</html>

Как вы заметили, помимо модных флексов, для каркаса страницы использованы и новые теги HTML5, которые до сих пор встречается не особо часто.

В данном случае флекс-контейнером мы сделаем весь body, чего мелочиться. Блоки размещаем по вертикали (flex-direction:column;). Задаём высоту height:100%;
чтобы работало в нежно нами любимом IE.

В шапке <header> вы можете видеть уже знакомое правило flex-basis:200px;

Но теперь, поскольку блоки расставлены вертикальное, это правило определяет не ширину, а высоту блока! Обратите на это внимание. Правило flex-shrink:0; говорит, что блок не может сжиматься, если ему станет тесно. В теге, который отвечает за тело страницы, сразу несколько интересных моментов. flex-basis:auto; — автоматический подсчёт высоты блока, flex-shrink:0; — «несжимаемость» блока, а flex-grow:1; означает, что блок будет занимать всё оставшееся свободное место.

Для чего это нужно? Это нужно для того, чтобы — внимание — подвал прижался к низу страницы. Одной, мать его, строкой! Одним правилом! Без всякой херни с отрицательными отступами, дополнительными обёртками и прочего. И высота подвала может быть любая. И работает даже в IE. Пока вы открываете праздничную бутылку шампанского (а как такое не отметить), я немного уточню каркас, чтобы приблизить его к реальности. Сделаем стандартный старпёрский трёхколоночный макет. Вот, как это будет выглядеть.

body {
	display:flex;
	flex-direction:column;
	height:100%;
	align-items:center;
}
header,main,footer {
	width:1170px;
}
header {
	flex-basis:200px;
	flex-shrink:0;
}
main {
	flex-basis:auto;
	flex-shrink:0;
	flex-grow:1;
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
}
footer {
	flex-basis:150px;
	flex-shrink:0;
}
#left-col, #right-col {
	flex-basis:20%;
}
#center-col {
	flex-basis:60%;
}

В <body> мы добавили правило align-items:center; чтобы выровнять элементы по ширине. Обратите внимание — поскольку блоки теперь выставлены по вертикали, то и оси поменяли своё направление. И если раньше align-items выравнивал блоки по вертикали, то теперь он выравнивает их по горизонтали. (на самом деле, ничего не меняется, просто система координат разворачивается на 90 градусов по часовой).

Уточнили <main>. Поскольку здесь будут колонки, мы действуем аналогично пункту с колонками. И, собственно, определяем ширины самих колонок, left-col, right-col и center-col. В макете за колонки будет отвечать тег <section>

.html

<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
	
<header>Это шапка</header>

	<main>
	    
<section id="left-col">Здесь может быть меню</section>

            
<section id="center-col">Здесь будет крутой контент</section>

            
<section id="right-col">Эта колонка никому не нужна, но все её хотят</section>

	</main>
	
<footer>А это подвал</footer>

</body>
</html>

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

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

Успехов! А на этом, урок заканчивается. Теперь вы знаете про чудо-инструмент под названием Flexbox, умеете его применять и, думаю, уже прикинули, сколько нервных клеток вы теперь будете экономить при вёрстке страниц. Осталось лишь ответить на Главный Вопрос Верстальщика — что там с кросс-браузерностью? Всё достаточно неплохо, даже IE с 10й версии справляется с флексами, а Edge и вовсе ведёт себя прилично, что не свойственно браузерам от Microsoft. Разумеется, старые версии браузеров (старше 2-3-х лет) прелести flexbox поддерживать не будут, от слова «никак», но, тут уже вам решать, стоит ли игра свеч, нужно ли тащить за собой легаси и кто ваша целевая аудитория.

Теперь у меня точно всё. Удачи, и да пребудут с вами новейшие спецификации во всех браузерах.

body {
	display:flex;
	flex-direction:column;
	height:100%;
	align-items:center;
}
header,main,footer {
	width:1170px;
}
header {
	flex-basis:200px;
	flex-shrink:0;
}
main {
	flex-basis:auto;
	flex-shrink:0;
	flex-grow:1;
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
}
footer {
	flex-basis:150px;
	flex-shrink:0;
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
}
#left-col, #right-col {
	flex-basis:20%;
max-width:20%;
}
#center-col {
	flex-basis:60%;
	max-width:60%;
	padding:10px 15px;
}
.col-4 {
	flex-basis:25%;
	max-width:25%;
	padding:10px 15px;
}
/*Панель меню*/
.panel-wrapper {
	display:flex;
	width:100%;
	justify-content:space-between;
}
.logo-part {
	flex-basis:20%;
	padding:0 20px;
}
.menu-part {
	flex-basis:80%;
}
.flex-menu {
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
	justify-content:flex-end;
	list-style:none;
}
.flex-menu li {
	flex-basis:150px;
	max-width:150px;
	height:35px;
	display:flex;
	align-items:center;
	justify-content:center;
	padding:10px 15px;
}
.section-menu {
	display:flex;
	list-style:none;
	flex-direction:column;
}
.section-menu li {
	flex-basis:30px;
	max-height:30px;
	display:flex;
	align-items:flex-start;
	justify-content:flex-start;
	padding:10px 15px;
}
/*Карточки*/
.cards-wrapper {
	width:100%;
	display:flex;
	flex-direction:row;
	flex-wrap:wrap;
	justify-content:space-between;
}
.flex-card {
	flex-basis:150px;
	height:220px;
	border:1px solid #212121;
	margin:15px 0;
}
/*Прочие элементы*/
.search-wrapper {
	width:200px;
	height:40px;
	display:flex;
	flex-direction:row;
	flex-wrap:nowrap;
	justify-content:center;
	align-items:center;
}
.search-input {
	flex-basis:75%;
}
.search-button {
	flex-basis:25%;
}
.search-input,.search-button {
	height:auto;
}
input[type="text"] {
	width:100%;
	height:30px;
	line-height:30px;
	padding:0 15px;
	border-top-left-radius:10px;
	border-bottom-left-radius:10px;
}
input[type="submit"] {
	width:60px;
	height:30px;
	line-height:30px;
	padding:0 15px;
	border-top-right-radius:10px;
	border-bottom-right-radius:10px;
	background-color:#212121;
	color:#ffffff;
	border:none;
	outline:none;
}
<html>
<head>
<title>Знакомство с flexbox</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
    
<header>
        
<div class="panel-wrapper">
            
<div class="logo-part">
                Здесь ваше лого
	    </div>

            
<div class="menu-part">
                
<ul class="flex-menu">
                    
<li><a href="#">Меню</a></li>

                    
<li><a href="#">Которое</a></li>

                    
<li><a href="#">Никуда</a></li>

                    
<li><a href="#">Не</a></li>

                    
<li><a href="#">Ведёт</a></li>

                </ul>

            </div>

	</div>

    </header>

    <main>
        
<section id="left-col">
            
<ul class="section-menu">
                
<li><a href="#">А это</a></li>

                
<li><a href="#">Просто</a></li>

                
<li><a href="#">Меню</a></li>

                
<li><a href="#">Раздела</a></li>

            </ul>

	</section>

        
<section id="center-col">
            
<div class="cards-wrapper">
                
<div class="flex-card">1</div>

                
<div class="flex-card">2</div>

                
<div class="flex-card">3</div>

                
<div class="flex-card">4</div>

                
<div class="flex-card">5</div>

                
<div class="flex-card">6</div>

                
<div class="flex-card">7</div>

                
<div class="flex-card">8</div>

            </div>

	</section>

        
<section id="right-col">
            
<div class="search-wrapper">
                
<div class="search-input">
		    <input type="text" placeholder="Поиск">
                </div>

                
<div class="search-button">
		    <input type="submit" value="GO">
                </div>

            </div>

        </section>

    </main>
    
<footer>
        
<section class="col-4">Первая колонка подвала</section>

        
<section class="col-4">Вторая колонка подвала</section>

        
<section class="col-4">Третья колонка подвала</section>

        
<section class="col-4">Четвёртая колонка подвала</section>

    </footer>

</body>
</html>