От автора: в данной обучающей статье мы разберем некоторые реальные и некогда действительно трудновыполнимые моменты, которые сейчас легко решаются благодаря использованию flexbox.
Введение
Некоторое время назад я написал ознакомительную статью про flexbox на сайте SitePoint. В данной статье я постарался выяснить и ответить на вопрос: А готовы ли мы использовать flexbox?
В прошлый раз я говорил, что вам следует очень аккуратно использовать данную возможность, но настаивал на том, что нужно обязательно познакомиться с ее синтаксисом и внедрять ее в современные проекты. Сейчас я рад, что могу дать положительный ответ на свой вопрос: Да, мы готовы использовать flexbox!
Если вы еще не знакомы с flexbox (флексбокс), то вот небольшое пояснение. CSS модуль построения гибкой разметки — это разметочный модуль, позволяющий легко размещать блоки на экране, в пределах выделенного пространства. Это большое достижение по сравнению с классической блочной моделью, т.к. флексбокс позволяет вообще не использовать свойство float. Блоки (боксы) могут быть объединены в строки или разбиты на колонки. Для каждого флексбокс-элемента может быть задан определенный порядок. А также можно сразу управлять выравниванием, отступами и размерами этих элементов.
Пожалуй, что в данной статье мне не удастся рассмотреть каждый аспект данного модуля, поэтому я предлагаю вам познакомиться с ним поподробнее, перейдя по вышеуказанной ссылке на сайт MDN. А в данной обучающей статье вы рассмотрим распространенные проблемы с разметкой и увидим, как легко и быстро их можно решить. Каждый шаблон разметки, о котором я расскажу, будет отзывчивым, что еще больше подчеркнет ту легкость, с которой можно создавать разметку с помощью флексбокс. Вот что мы рассмотрим:
Простая сеточная система
Разметка Священного Грааля
Резиновая навигация с изменяющейся шириной поля поиска
Два разных варианта вертикального выравнивания
Хорошо, давайте приступать
1 — Простая сеточная система
Сегодня сеточные системы присутствуют в большинстве разметок проектов, и классическое поведение блочной модели в CSS вынудило нас прибегнуть к использованию плавающих или строчно-блочных элементов, каждый из которых имеет собственные недостатки. Флексбокс позволяет нам легко создавать действительно классную и масштабируемую сеточную систему благодаря написанию всего нескольких строчек в CSS. Давайте рассмотрим это поподробнее. Представьте, что у нас есть следующая простая разметка:
1 2 3 4 5 6 7 |
<div class="grid"> <div class="grid__row"> <div class="grid__item">1</div> <div class="grid__item">2</div> ... </div> </div> |
При традиционном подходе создания сетки нам бы пришлось каким-то образом определить, сколько элементов может помещаться в одну строку, а затем установить ширину для каждого элемента сетки. А с помощью флексбокс мы можем добавлять в строку столько элементов, сколько захотим, и их ширина сама будет подстраиваться под общую ширину строки. Другими словами, у нас может быть следующая разметка, и при этом нам не нужно волноваться об указании количества элементов в каждой строке в CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<div class="grid"> <div class="grid__row"> <div class="grid__item">1</div> <div class="grid__item">2</div> </div> <div class="grid__row"> <div class="grid__item">1</div> <div class="grid__item">2</div> <div class="grid__item">3</div> </div> <div class="grid__row"> <div class="grid__item">1</div> <div class="grid__item">2</div> <div class="grid__item">3</div> <div class="grid__item">4</div> </div> </div> |
А теперь давайте посмотрим на CSS код. Некоторые свойства я использовал чисто в эстетических целях (например, рамки и внутренние отступы), а в остальном все очень просто:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.grid { border: solid 1px #e7e7e7; } .grid__row { display: flex; } .grid__item { flex: 1; padding: 12px; border: solid 1px #e7e7e7; } |
Вот как-то так! И наша сетка уже готова к использованию. Добавляя display: flex к контейнерам .grid__row, мы создаем, так называемый, флекс-контейнер, а каждый дочерний элемент в контейнере сразу становится флекс-элементом. Применяя свойство flex: 1 к флекс-элементам, мы заставляем их занимать равное количество места относительно общей ширины контейнера. Теперь у вас может быть сколько угодно строк в сетке, а в каждой из них может быть свое собственное количество сеточных элементов. И это будет простая полностью отзывчивая сеточная система без всякого дополнительного CSS.
А что можно сказать насчет брейкпойнтов (контрольных точек) и колоночной разметки? Если мы хотим, чтобы элементы сетки выстраивались в колонки, а не в строки, то мы можем просто объявить свойство flex-direction: column для контейнеров с классом .grid__row. В этом случае мы можем создать очень простую отзывчивую сетку, внеся некоторые изменения. И наша разметка будет выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<div class="grid"> <div class="grid__row grid__row--sm"> <div class="grid__item">1</div> ... </div> <div class="grid__row grid__row--md"> <div class="grid__item">1</div> ... </div> <div class="grid__row grid__row--lg"> <div class="grid__item">1</div> ... </div> </div> |
А наш CSS вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
.grid { border: solid 1px #e7e7e7; } .grid__row { display: flex; flex-direction: column; } .grid__item { flex: 1; padding: 12px; border: solid 1px #e7e7e7; } @media all and ( min-width: 480px ) { .grid__row--sm { flex-direction: row; } } @media all and ( min-width: 720px ) { .grid__row--md { flex-direction: row; } } @media all and ( min-width: 960px ) { .grid__row--lg { flex-direction: row; } } |
И вуаля. Супер-простая отзывчивая сеточная система длиною всего в несколько CSS строк. Эта система настолько непробиваемая, что вы даже можете вкладывать сетки друг в друга, не заботясь о последствиях:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<div class="grid"> <div class="grid__row grid__row--sm"> <div class="grid__item"> <div class="grid"> <div class="grid__row grid__row--lg"> <div class="grid__item">Nested 1</div> ... </div> </div> </div> <div class="grid__item">2</div> </div> <div class="grid__row grid__row--md"> <div class="grid__item">1</div> ... </div> </div> |
Посмотрите на это в действии.
2 — Разметка Священного Грааля
Разметка Священного Грааля является довольно известной в веб-дизайне, и даже в эпоху веб-приложений и всего прочего веселья, данный вариант разметки по-прежнему играет важную роль в вебе — такая разметка постоянно используется на сайтах, у которых много контента. Еще в 2006 году разметка Священного Грааля была замечательно разобрана и описана на сайте A List Apart. В ней используются плавающие элементы (свойство float), отрицательные внешние отступы (свойство margin) и минимальные значения для ширины (свойство min-width), чтобы совмещенные размеры элементов не конфликтовали друг с другом и не ломали разметку. И все это, с учетом нынешней необходимости в создании отзывчивой разметки, означало использование большого количества вычислений, отмены обтекания и других трюков, чтобы это все работало правильно. И если вы решили поменять ширину боковой колонки (сайдбара), то нужно было заново производить все вычисления.
Флексбокс позволяет избавиться от существенной головной боли, т.к. мы можем определить колоночную или строчную разметку, а также явно указать порядок следования элементов в CSS, даже если они расположены в другом порядке в нашей разметке. Вот типичный пример создания разметки Священного Грааля:
1 2 3 4 5 6 7 8 9 |
<body class="holy-grail"> <header class="holy-grail__header"></header> <main class="holy-grail__body"> <div class="holy-grail__content"></div> <div class="holy-grail__sidebar holy-grail__sidebar--first"></div> <div class="holy-grail__sidebar holy-grail__sidebar--second"></div> </div> <footer class="holy-grail__footer"></footer> </body> |
В моем демо-примере разметка Священного Грааля находится внутри документа, поэтому в ней нет тегов body или main, как это показано выше. Тем не менее, мы заинтересованы в именах классов и появлении разделов в нашей разметке в отличие от непосредственно самих элементов. В частности, обратите внимание на классы-модификаторы, которые используются для двух боковых колонок, и на тот порядок, в котором они следуют в разметке. Давайте последовательно рассмотрим, что здесь происходит:
У нас есть родительский контейнер, .holy-grail, а в нем у нас находится три флекс-элемента. Это элементы с классами .holy-grail__header, holy-grail__body и holy-grail__footer.
Данные три элемента расположены друг над другом и занимают 100% от ширины экрана. Таким образом, для флекс-контейнера должно быть задано колоночное направление.
Тело нашей разметки, .holy-grail__body, является внутренним флекс-контейнером. Его дочерние флекс-элементы должны иметь колоночную разметку на небольших экранах и строчную разметку на широких экранах.
Учитывая все вышесказанное, давайте построим разметку Священного Грааля:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
.holy-grail { display: flex; flex-direction: column; } .holy-grail__header, .holy-grail__footer { flex: 0 0 100%; } .holy-grail__body { display: flex; } .holy-grail__sidebar { /* ничего на небольших экранах */ } .holy-grail__sidebar--first { order: 1; } .holy-grail__sidebar--second { order: 3; } .holy-grail__content { order: 2; } @media all and ( min-width: 720px ) { .holy-grail__body { flex-direction: row; } .holy-grail__sidebar { flex: 0 0 180px; } .holy-grail__content { flex: 1; } } @media all and ( min-width: 960px ) { .holy-grail__sidebar { flex: 0 0 240px; } } |
Это действительно настолько просто! Как я уже упоминал, мы изначально определяем два флекс-контейнера (для небольших экранов). Для первой контрольной точки мы изменяем флекс-направление у тела разметки на строчное, а боковым колонкам задаем ширину 180px, используя сокращенную запись — свойство flex. Данная запись позволит нам ограничить значения для свойств flex-grow и flex-shrink, а также явно указать ширину. Для контента используется свойство flex: 1, чтобы он заполнял доступное пространство. Расположение флекс-элементов в нужном порядке также оказалось пустяком благодаря свойству order. Пожалуй, остались только дополнительные стили, которые вы захотите добавить с эстетической точки зрения… а в остальном это действительно просто и эффективно. А упомянул ли я о том, что по умолчанию флексбокс создает колонки одинаковой высоты?
Взгляните на этот демо-пример.
3 — Резиновая навигация с изменяющейся шириной поля поиска
В нашем следующем примере мы будем создавать нечто веселое, включающее красивый переход. Мы создадим резиновую навигацию, которая растягивается на всю ширину, а в ней мы разместим поле поиска, которое будет плавно растягиваться в состоянии фокуса. Используя силу флексбокса, мы сможем добавить столько пунктов меню, сколько захотим, не меняя при этом CSS. Я буду использовать некоторые классы-модификаторы для достижения нужного результата. В качестве небольшого бонуса я собираюсь сделать нашу навигацию полностью отзывчивой, добавив для нее кнопку-переключатель! Вот как будет выглядеть HTML:
1 2 3 4 5 6 7 8 9 10 11 12 |
<nav class="flexy-nav"> <button id="flexy-nav__toggle" class="flexy-nav__toggle">Toggle Nav</button> <ul id="flexy-nav__items" class="flexy-nav__items"> <li class="flexy-nav__item"><a href="#" class="flexy-nav__link">Item 1</a></li> <li class="flexy-nav__item"><a href="#" class="flexy-nav__link">Item 2</a></li> <li class="flexy-nav__item"><a href="#" class="flexy-nav__link">Item 3</a></li> <li class="flexy-nav__item"><a href="#" class="flexy-nav__link">Item 4</a></li> </ul> <form action="" class="flexy-nav__form"> <input class="flexy-nav__search" type="text" placeholder="Type search terms and hit enter..."> </form> </nav> |
Давайте разберем данную разметку, прежде чем мы перейдем к CSS. У нас есть основной флекс-контейнер с классом .flexy-nav. Кнопка используется в качестве переключателя, в ненумерованном списке содержатся пункты основного меню, а в форме содержится поле поиска. Для небольших экранов нам нужно использовать колоночную разметку для всех трех элементов, а также нам нужно, чтобы каждый пункт меню располагался в отдельной колонке. На широких экранах нам нужно спрятать кнопку-переключатель, выстроить элементы списка в строку и задать для формы фиксированную ширину. Элементы списка будут равномерно распределены среди оставшегося пространства. Когда мы переключаемся на текстовое поле (поле поиска), нам бы хотелось, чтобы оно плавно растягивалось по ширине, а все элементы списка, наоборот, плавно сужались. Вот CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
/* сброс стилей */ input, button { font: inherit; border-radius: none; box-shadow: none; appearance: none; } button { cursor: pointer; } /* контейнер навигации */ .flexy-nav { display: flex; flex-direction: column; } /* пункты меню */ .flexy-nav__items { display: none; flex: 1; flex-direction: column; list-style: none; margin: 0 0 4px 0; padding: 4px; text-align: center; } .flexy-nav__items--visible { display: flex; } .flexy-nav__item { background-color: #f1f1f1; border-bottom: solid 1px #e7e7e7; } .flexy-nav__item:last-child { border-bottom: 0; } .flexy-nav__link { padding: 8px; display: block; } /* переключатель меню */ .flexy-nav__toggle { margin: 0 0 4px 0; padding: 4px; color: #fff; background-color: #f07850; border: none; } .flexy-nav__toggle:hover, .flexy-nav__toggle:focus { outline: none; background-color: #c93f11; } /* форма для поля поиска в навигации */ .flexy-nav__form { height: 48px; } .flexy-nav__search { display: block; margin: 0; padding: 0 4px; width: 100%; height: 48px; color: #6d6d6d; background-color: #fff; border: solid 2px #e7e7e7; } .flexy-nav__search:focus { outline: none; border: solid 2px #6d6d6d; } /* медиа-запросы */ @media all and (min-width: 768px) { .flexy-nav { flex-direction: row; } .flexy-nav__items { display: flex; flex-direction: row; margin: 0; padding: 0; height: 48px; } .flexy-nav__item { flex: 1; margin-right: 4px; border-bottom: none; } .flexy-nav__link { padding: 0; line-height: 48px; } .flexy-nav__toggle { display: none; } .flexy-nav__form { flex: none; } .flexy-nav__search { width: 240px; transition: width 0.3s; } .flexy-nav__search:focus { width: 360px; } } |
Как и обещал, вот, до смешного простой, кусочек JavaScript кода, который позволит нам показывать/скрывать навигацию на небольших экранах:
1 2 3 4 5 6 7 8 |
(function() { var toggle = document.querySelector("#flexy-nav__toggle"); var nav = document.querySelector("#flexy-nav__items"); toggle.addEventListener("click", function(e) { e.preventDefault(); nav.classList.contains("flexy-nav__items--visible") ? nav.classList.remove("flexy-nav__items--visible") : nav.classList.add("flexy-nav__items--visible"); }); })(); |
Настолько все просто. Мы только что создали красивую и масштабируемую резиновую навигацию с помощью флексбокс, а также встроили плавный переход при изменении ширины у поля поиска. Мы можем сколько угодно добавлять или удалять ссылки, а также «на лету» изменять размеры поля поиска. И при этом функциональность нашего меню нисколько не пострадает. Ах, вот они прелести флексбокса. Не забудьте посмотреть соответствующий демо-пример.
4 — Вертикальное выравнивание
Давайте признаем тот факт, что вертикальное выравнивание в традиционном CSS просто никуда не годится. Строчно-блочные элементы могут иногда с этим помочь, есть также хаки с абсолютным позиционированием, а еще есть устаревшие табличные разметки (которые на данный момент неприемлемы для многих случаев с семантической точки зрения). У всех этих способов есть свои особенности, и они точно потребуют от вас дополнительных «танцев с бубнов», чтобы все работало, как надо.
Флексбокс с легкостью возьмет это на себя. Мы рассмотрим два примера вертикального выравнивания:
Сначала мы рассмотрим создание, так называемого, «медиа-объекта», в котором используется пользовательский аватар (расположенный слева) и имя пользователя + некоторая информация (расположенные справа). Мы будем использовать флексбокс, чтобы изображение и тело медиа-объекта были идеально выровнены по вертикали.
Затем мы просто рассмотрим вертикальное (горизонтальное) выравнивание элемента фиксированной ширины и переменной высоты внутри контейнера. Элемент будет оставаться расположенным точно по центру, несмотря на увеличение высоты.
Давайте начнем с первого примера. Как уже было сказано, нам нужно расположить пользовательский аватар слева, а описание — справа. И неважно насколько длинным или коротким будет описание. Нам нужно, чтобы оно всегда было идеально выровнено с аватаром. Вот стандартная разметка:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<div class="user"> <div class="user__avatar"></div> <div class="user__description"> <h2 class="user__username">John Doe</h2> <p class="user__excerpt">I'm John Doe...</p> </div> </div> <div class="user"> <div class="user__avatar"></div> <div class="user__description"> <h2 class="user__username">Harry Potter</h2> <p class="user__excerpt">I'm Harry Potter...with a really long description...</p> </div> </div> |
Прежде чем мы перейдем к CSS, обратите внимание на то, что мы будем использовать незнакомое до этого свойство. Это свойство align-items, и оно позволяет нам выравнивать элементы вдоль, так называемой, флекс-линии в перпендикулярном направлении. Другими словами, если наша флекс-линия расположена горизонтально, то мы можем выравнивать наши элементы в направлении, которое перпендикулярно данной линии. В нашем случае нам нужно выровнять элементы по центру, поэтому мы будем использовать значение align-items: center. Вот CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
.user { display: flex; align-items: center; } .user:last-child { margin-bottom: 0; } .user__avatar { flex: 0 0 96px; width: 96px; height: 96px; background-color: #e7e7e7; } .user__description { flex: 1; margin-left: 24px; padding: 12px; border: solid 1px #e7e7e7; } |
Вот так просто. Вы можете оформить текст, как вам захочется, сделать описания очень длинными или изменить размеры аватара. Это не имеет значения, функциональность останется прежней. Оцените данную возможность в действии.
Давайте перейдем ко второму примеру. На этот раз представьте, что у нас есть баннер, расположенный в самой верхней части разметки. И мы хотим разместить внутри баннера какой-то заголовок. На маленьких экранах высота баннера будет равна 180px, и она будет изменяться еще дважды, до значения 480px. И при всех изменениях высоты баннера нам нужно, чтобы текст внутри был расположен идеально по центру (как по горизонтали, так и по вертикали). Вот стандартная разметка:
1 2 3 4 5 6 |
<div class="banner"> <div class="banner__content"> <h2 class="banner__title">Symmetrical Perfection</h2> <span class="banner__sub">A beautiful sight, achieved with flexbox.</span> </div> </div> |
На этот раз мы задействовали также свойство justify-content, которое позволит нам распределить пространство вокруг элементов вдоль флекс-линии. А вот CSS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
.banner { display: flex; align-items: center; justify-content: space-around; height: 180px; background-color: #e7e7e7; } .banner__content { text-align: center; } .banner__title, .banner__sub { margin: 0; padding: 0; line-height: 1.5; } @media all and ( min-width: 480px ) { .banner { height: 240px; } } @media all and ( min-width: 768px ) { .banner { height: 360px; } } @media all and ( min-width: 960px ) { .banner { height: 480px; } } |
Неважно, насколько «высоким» будет баннер. Контент всегда будет идеально отцентрирован по горизонтали и вертикали. Вот в этом и заключается мощь флексбокса. Не забудьте посмотреть демо-пример.
Поддержка и вендорные префиксы
Вы должны знать ваш рынок и аудиторию… это ключевой момент. Флексбокс поддерживается во всех современных браузерах, включая IE10 и выше. Если вы занимаетесь созданием современных веб-приложений, то флексбокс — это мощный инструмент, и я очень рекомендую его использовать. Если вы создаете или переделываете веб-сайт, то проверьте статистику посещаемости, чтобы узнать вашу аудиторию. В настоящее время есть вероятность того, что около 99% вашей аудитории будет использовать современные браузеры.
Что касается вендорных префиксов, то у флексбокса их много. Это очень неразумно всерьез использовать флексбокс и при этом вручную прописывать все вендорные префиксы. Лично я использую для этого сборщик проектов (Gulp). Думаю, что вам следует серьезно подумать об использовании сборщиков проектов в вашей работе, даже если вы не используете флексбокс.
Заключение
Вот и все! На этом я заканчиваю данную статью. Если вы хотите найти реальный сайт, на котором используется флексбокс, то можете даже и не начинать поиски. На новом сайте callmenick.com флексбокс используется практически везде! Спасибо, что прочли статью до конца. Не забудьте, что вы можете посмотреть демо-примеры, а также скачать исходники по нижеприведенным ссылкам. Если у вас есть какие-либо вопросы, замечания или пожелания, оставьте их, пожалуйста, в комментариях.
Автор: Nick Salloum
Источник: //callmenick.com/
Редакция: Команда webformyself.