Практичне застосування FlexBox

Привіт, хабр!

Одним прекрасним вечором, не віщує нічого цікавого, в наш чатик надійшла пропозиція від автора ось цієї статті, написаної ним навесні 2012 року, написати статтю-ремейк, але із застосуванням FlexBox і супутнім поясненням що і як працює. Після деякої частки сумнівів, інтерес глибше розібратися в специфікації всі таки переміг і я благополучно сів верстати ті самі приклади. В ході занурення в цю область стало з'ясовуватися безліч нюансів, що переросло в щось більше ніж просто переверстати макетики. Загалом у цій статті хочу розповісти про такий чудовою специфікації, під назвою «CSS Flexible Box Layout Module» і показати деякі цікаві особливості і приклади застосування. Всіх кому цікаво, люб'язно запрошую під хабракат.


На що хотілося б звернути увагу, що для верстки лейаута на FlexBox, розробнику потрібно деяка ступінь адаптації. На власному прикладі відчув, що багаторічний досвід грає злий жарт. FlexBox вимагає трохи іншого уявлення про вибудовування елементів у потоці.

Технічна частина

Перш ніж переходити до якихось прикладів, варто розібратися які властивості входять в дану специфікацію і як вони працюють. Так як деякі з них не дуже зрозумілі, а деякі оточені міфами, які до дійсності не мають ні найменшого відношення.
Отже. У FlexBox є два основних типи елементів: Гнучкий Контейнер (Flex Container) і його дочірні елементи Гнучкі Елементи (Flex Item). Для ініціалізації контейнера достатньо присвоїти, через css, елементу display: flex; або display: inline-flex;. Різниця між flex і inline-flex полягає лише в принципі взаємодії з оточуючими контейнер елементами, подібно display: block; display: inline-block;, відповідно.
Всередині гнучкого контейнера створюються дві осі, головна вісь (main-axis) і перпендикулярна або крос вісь (cross axis). Переважно гнучкі елементи вишиковуються саме по головній осі, а потім вже по крос осі. За замовчуванням головна вісь горизонтальна і має напрямок зліва направо, а крос вертикальна вісь і спрямована зверху вниз.



Напрямком осей можна керувати за допомогою css-властивості flex-direction. Дана властивість приймає ряд значень:
row (default): Головна вісь гнучкого контейнера має ту ж орієнтацію, як і інлайн вісь поточного режиму напрямку рядків. Початок (main-start) і кінець (main-end) напрямку головної осі відповідають початку (inline-start) і кінця (inline-end) інлайн осі (inline-axis).
row-reverse: Все те ж саме, що і в row тільки main-start і main-end міняються місцями.
column: так само як і row, тільки тепер головна вісь спрямована зверху вниз.
column-reverse: так само як row-reverse, тільки головна вісь спрямована знизу вгору.
Як це працює можна подивитися в прикладі на jsfiddle.

За замовчуванням усі гнучкі елементи в контейнері вкладаються в один рядок, навіть якщо не поміщаються в контейнер, вони виходять за його кордони. Дане поведінка перемикається за допомогою властивості flex-wrap. У цієї властивості є три стани:
nowrap (default): гнучкі елементи вишиковуються в одну рядок зліва направо.
wrap: гнучкі елементи будуються в багаторядковому режимі, перенесення здійснюється за напрямом крос осі, зверху вниз.
wrap-reverse: так само як і wrap, але перенесення відбувається знизу вгору.
Дивимося приклад.

Для зручності є додаткове властивість flex-flow, в якому можна вказати flex-direction та flex-wrap. Виглядає це наступним чином: flex-flow: <flex-direction> <flex-wrap>

Елементи в контейнері піддаються вирівнювання за допомогою властивості justify-content вздовж головної осі. Це властивість приймає цілих п'ять різних варіантів значень.
flex-start (default): гнучкі елементи вирівнюються по початку головної осі.
flex-end: елементи вирівнюються по кінця головної осі
center: елементи вирівнюються по центру головної осі
space between: елементи займають всю доступну ширину в контейнері, крайні елементи впритул притискаються до країв контейнера, а вільний простір рівномірно розподіляється між елементами.
space-around: гнучкі елементи розташовуються таким чином, що вільний простір рівномірно розподіляється між елементами. Але варто відзначити, що простір між краєм контейнера і крайніми елементами буде в два рази менше ніж простір між елементами в середині ряду.
Звичайно ж поклацати приклад роботи цієї властивості можна тут.

Це ще не все, ми так само маємо можливість вирівнювання елементів за крос осі. Застосувавши властивість align-items, яке приймає також п'ять різних значень, можна домогтися цікавого поведінки. Це властивість дозволяє вирівнювати елементи в рядку відносно один одного.
flex-start: всі елементи притискаються до початку рядка
flex-end: елементи притискаються до кінця рядка
center: елементи вирівнюються по центру рядка
baseline: елементи вирівнюються по базової лінії тексту
stretch (default): елементи розтягуються повністю заповнюючи рядок.

Ще одне схоже властивість на попереднє це align-content. Тільки воно відповідає за вирівнювання цілих рядків щодо гнучкого контейнера. Воно не буде давати ефекту якщо гнучкі елементи займають один рядок. Властивість приймає шість різних значень.
flex-start: всі лінії притискаються до початку крос-осі
flex-end: всі лінії притискаються до кінця крос-осі
center: Всі лінії паком вирівнюються по центру крос осі
space between: лінії розподіляються від верхнього краю до нижнього залишаючи вільний простір між рядками, крайні ж рядка притискаються до країв контейнера.
space-around: лінії рівномірно розподіляються по контейнера.
stretch (default): лінії розтягуються займаючи весь доступний простір.
Спробувати як працюють align-items та align-content можна цьому прикладі. Я спеціально два цих властивості представив в одному прикладі, так як вони досить щільно взаємодіють кожен виконуючи своє завдання. Зверніть увагу що відбувається, коли елементи розташовуються в один рядок і кілька.

З параметрами гнучкого контейнера розібралися, залишилося розібратися з властивостями гнучких елементів.
Перше властивість, з яким ми познайомимося це order. Це властивість дозволяє змінювати позицію в потоці конкретного елемента. За замовчуванням усі гнучкі елементи мають order: 0; і будуються в порядку природного потоку. приклад можна побачити як міняються місцями елементи якщо до них застосовувати різні значення order.

Одне з основних властивостей є flex-basis. З допомогою цієї властивості ми можемо вказувати базову ширину гнучкого елемента. За замовчуванням має значення auto. Ця властивість тісно пов'язана з flex-grow та flex-shrink, про які я розповім трохи пізніше. Приймає значення ширини px, %, em і інших одиницях. По суті це не строго ширина гнучкого елемента, це свого роду відправна точка. Щодо якої відбувається розтягування або усадка елемента. У режимі auto елемент отримує базову ширину щодо контенту всередині нього.

flex-grow на кількох ресурсах має абсолютно некоректне опис. Там йдеться про те, що нібито воно задає співвідношення розмірів елементів в контейнері. Насправді це не так. Ця властивість задає фактор збільшення елемента при наявності вільного місця в контейнері. За замовчуванням ця властивість має значення 0. Давайте уявимо, що у нас є гнучкий контейнер, який має ширину 500px, всередині нього є два гнучких елементи, кожний з яких має базову ширину 100px. Тим самим у контейнері залишається ще 300px вільного місця. Якщо першого елемента вкажемо flex-grow: 2;, а другого елементу вкажемо flex-grow: 1;. В результаті ці блоки займуть всю доступну ширину контейнера, тільки ширина першого блоку буде 300px, а другого тільки 200px. Що ж сталося? А сталося ось що, доступні 300px вільного місця в контейнері розподілилися між елементами в співвідношенні 2:1, +200px першому і +100px другого. Власне так воно і працює.

Тут ми плавно переходимо до іншого аналогічного властивості, а саме flex-shrink. За замовчуванням має значення 1. Воно так само задає чинник на зміну ширини елементів, тільки у зворотний бік. Якщо контейнер має ширину менше ніж сума базової ширини елементів, то починає діяти цю властивість. Наприклад, контейнер має ширину 600px, а flex-basis елементів за 300px. Першого елемента вкажемо flex-shrink: 2;, а другого flex-shrink: 1;. Тепер стиснемо контейнер на 300px. Отже сума ширини елементів на 300px більше ніж контейнер. Ця різниця розподіляється в співвідношенні 2:1, виходить від першого блоку віднімаємо 200px, а від другого 100px. Новий розмір елементів виходить 100px і 200px, у першого і другого елемента. Якщо ми встановлюємо flex-shrink значення 0, то ми забороняємо стискатися елементу до розмірів менше ніж його базова ширина.

Насправді це дуже спрощений опис того, як це все працює, щоб було зрозумілим загальний принцип. Більш докладно, якщо кому цікаво, алгоритм описаний в специфікації.

Всі три властивості можна записати у скороченій формі за допомогою виразу flex. Це має наступний вигляд:
flex: <flex-grow> <flex-shrink> <flex-basis>;
А так само ми можемо писати ще два скорочених варіанти, flex: auto; та flex: none;, що означає flex: 1 1 auto; та flex: 0 0 auto; відповідно.

Останнім властивістю гнучких елементів залишився align-self. Тут все просто, це те ж саме, що align-items біля контейнера, що дозволяє перекрити вирівнювання для конкретно взятого елемента.

Все, набрид! Приклади давай!

З технічною частиною розібралися, вийшло досить затягнуто, але в це треба вникнути. Тепер можна перейти до практичного застосування.
В ході верстки тих самих «п'яти дійсно корисних шаблонів адаптивної розмітки», довелося вирішувати типові ситуації, з якими розробник стикається досить часто. З flexbox реалізація цих рішень стає простіше і гнучкіше.
Візьмемо все той же 4-ий макет, оскільки в ньому найбільше цікавих елементів.



Для початку позначимо основну ширину сторінки, вирівняємо по центру, притиснемо футер до низу сторінки. Як завжди в загальному.

html {
background: #ccc;
min-height: 100%;
font-family: sans-serif;
display: -webkit-flex;
display: flex;
flex-direction: column;
}
body {
margin: 0;
padding: 0 15px;
display: -webkit-flex;
display: flex;
flex-direction: column;
flex: auto;
}
.header {
width: 100%;
max-width: 960px;
min-width: 430px;
margin: 0 auto 30px;
padding: 30px 0 10px;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: space between;
box-sizing: border-box;
}
.main {
width: 100%;
max-width: 960px;
min-width: 430px;
margin: auto;
flex: auto;
box-sizing: border-box;
}
.footer {
background: #222;
width: 100%;
max-width: 960px;
min-width: 430px;
color: #eee;
margin: auto;
padding: 15px;
box-sizing: border-box;
}


За рахунок того що ми для .main вказали flex-grow: 1; він розтягується на всю доступну висоту, тим самим притискаючи футер до низу. Бонусом у цьому рішенні є, то що футер може бути нефіксованим висоти.

Розмістимо логотип тепер і меню в хедері.
.logo {
font-size: 0;
margin: -10px 10px 10px 0;
display: flex;
flex: none;
align-items: center;
}
.logo:before,
.logo:after {
content: ";
display: block;
}
.logo:before {
background: #222;
width: 50px;
height: 50px;
margin: 0 10px 0 20px;
border-radius: 50%;
}
.logo:after {
background: #222;
width: 90px;
height: 30px;
}
.nav {
margin: -5px 0 0-5px;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
}
.nav-itm {
background: #222;
width: 130px; 
height: 50px;
font-size: 1.5 rem;
color: #eee;
text-decoration: none;
margin: 5px 0 0 5px;
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
}


Оскільки для хедера зазначено flex-wrap: wrap; і justify-content: space between; логотип і меню розкидає по різних сторонах хедера, при цьому якщо місця для меню буде не вистачати воно елегантно зміститься під логотип.

Далі ми бачимо великий піст або банер, важко сказати що це конкретно, але і не суть. У нас є картинка праворуч і текст із заголовком ліворуч. Я особисто дотримуюся думки, що будь-які елементи повинні бути максимально гнучкими, незалежно від того адаптиваная це верстка або статика. Отже у нас є в цьому пості сайд-бар в якому розміщена картинка, строго кажучи, ми не можемо точно сказати яка ширина нам потрібна, бо сьогодні у нас велика картинка, завтра маленька і кожен раз переробляти елемент з нуля неохота. Значить нам потрібно, щоб сайд-бар зайняв потрібне йому місце, а інше місце пішло на контент. Так і зробимо:

.box {
font-size: 1.25 rem;
line-height: 1.5;
font-style: italic;
margin: 0 0 40px-50px;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.box-base {
margin-left: 50px;
flex: 1 0 430px;
}
.box-side {
margin-left: 50px;
flex: none;
}
.box-img {
max-width: 100%;
height: auto;
}


Як ви бачите .box-base, там де у нас заголовок і текст, я вказав базову ширину допомогою flex-basis: 430px;, а так само заборонив усадку блоку за допомогою flex-shrink: 0;. Цією маніпуляцією ми сказали, що контент не може стати менше ніж 430px в ширину. А зважаючи на те, що для .box я вказую flex-wrap: wrap; в той момент, коли сайд-бар і контент не будуть поміщатися в контейнер .box, сайд-бар автоматично провалиться під контент. І це все без застосування @ media! Я вважаю це дійсно дуже круто.

У нас залишився трехколоночный контент. Рішень подібної задачі декілька, я покажу один з них, в інших макетах є й інший варіант.
Створюємо контейнер, назвемо його .content і налаштуємо.
.content {
margin-bottom: 30px;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
}


У контейнері три колонки, .banners, .posts, .comments
.banners {
flex: 1 1 200px;
}
.posts {
margin: 0 0 30px 30px;
flex: 1 1 200px;
}
.comments {
margin: 0 0 30px 30px;
flex: 1 1 200px;
}


Задав колонкам базову ширину 200px, щоб колонки не звужувалися прям занадто сильно, нехай краще вони в міру потреби переносяться один під одного.

По макету, нам з контентом, обійтися без @ media не вийде, тому ще трохи налаштуємо поведінка колонок для ширини <800px і <600px.
@media screen and (max-width: 800px) {
.banners {
margin-left: -30px;
display: -webkit-flex;
display: flex;
flex-basis: 100%;
}
.posts {
margin-left: 0;
}
}
@media screen and (max-width: 600px) {
.content {
display: block;
}
.banners {
margin: 0;
display: block;
}
.comments {
margin: 0;
}
}


Ось і вся магія, що стосується побудови лейаута на FlexBox. Ще одна задача, яка мені сподобалася, знаходиться в 5-му макеті, конкретно це стосується адаптації контенту.



Ми бачимо, як на десктопному вирішенні посади побудовані в сітку по три штуки в ряд. Коли ширина viewport стає менше 800px, то сітка перетворюється в колонку з постами, де фото поста вибудовується з лівого і правого боку від вмісту посту, по черзі. А при ширині менше 600px фото поста ховається зовсім.
.grid {
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: space between;
}
.grid-itm {
margin-bottom: 30px;
flex-basis: calc(33.33% - 30px * 2/3);

display: -webkit-flex;
display: flex;
flex-wrap: wrap;
}
.grid-img {
margin: 0 auto 20px;
flex: 0 1 80%;
}
.grid-cont{
flex: 0 1 100%;
}
.grid-title {
text-align: center;
}
@media screen and (max-width: 800px) {
.grid-itm {
flex-wrap: nowrap;
flex-basis: 100%;
}
.grid-img {
flex: 0 0 auto;
}
.grid-itm:nth-child(even) .grid-img {
margin: 0 0 0 30px;
order: 2;
}
.grid-itm:nth-child(odd) .grid-img {
margin: 0 30px 0 0;

}
.grid-cont {
flex: 1 1 auto;
}
.grid-title {
text-align: left;
}
}
@media screen and (max-width: 600px) {
.grid-img {
display: none;
}
}


Насправді це всього лише мала частина того, що можна реалізувати на FlexBox. Специфікація дозволяє будувати досить складні макети сторінок, при цьому застосовуючи простий код.

Трохи посилань

Приклади шаблонів можна побачити по посиланням на github.
Знання вбирав звідси.

Сподіваюся мій пост комусь допоможе і дозволить швидше використовувати нові технології в повну силу.
На цьому у мене все. Дякую за увагу!

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.