Привет, хабр!
Одним прекрасным вечером, не предвещающим ничего интересного, в наш чатик поступило предложение от автора публикации «Переводим в код 5 действительно полезных шаблонов адаптивной разметки» , написанной им весной 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:
Элементы в контейнере поддаются выравниванию при помощи свойства 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: auto;
и flex: none;
, что означает flex: 1 1 auto;
и flex: 0 0 auto;
соответственно.
Последним свойством гибких элементов остался align-self . Тут все просто, это то же самое, что align-items у контейнера, позволяющее переопределять выравнивание для конкретно взятого элемента.
Для начала обозначим основную ширину страницы, выровняем по центру, прижмем футер к низу страницы. Как всегда в общем.
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-grow: 1; 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.5rem;
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.25rem; 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. Спецификация позволяет строить весьма сложные макеты страниц при этом применяя простой код.
Модуль разметки Flexbox (от английского Flexible Box – гибкий блок ), находящийся на данный момент на этапе «Возможная рекомендация » стандартизации W3C (W3C Candidate Recommendation ) нацелен на обеспечение более эффективного способа разметки, выравнивания и распределения места между элементами в контейнере, даже если их размер неизвестен и/или определяется динамически (вот почему его назвали «гибкий» ).
Основная идея, стоящая за гибкой разметкой – это наделить контейнер способностью изменять высоту/ширину (и порядок ) его элементов для оптимального заполнения доступного пространства (в основном для поддержки всех видов и размеров экранов ).
Flex -контейнер растягивает элементы, чтобы заполнить свободное пространство или сжимает их, чтобы предотвратить выход за границы.
Что наиболее важно, разметка Flexbox не зависит от направления, в противоположность обычным разметкам (блокам, которые ориентированы вертикально и строчным элементам, располагающимся горизонтально ).
И хотя элементы обычной разметки хорошо работают для страниц, им не хватает гибкости (и это не игра слов ) для поддержки больших и сложных приложений (особенно если речь заходит об изменении ориентации, размеров, растяжении, сжатии и так далее ).
Примечание : разметка Flexbox лучше всего подходит для компонентов приложения и небольших макетов, в то время как разметка с помощью grid предназначена для верстки более масштабных макетов.
Так как Flexbox это целый модуль, а не отдельное свойство, он включает в себя много элементов, в том числе и целый набор свойств. Некоторые из них предназначены для применения к контейнеру (родительскому элементу, известному как flex-контейнер ), в то время как другие должны быть применены к дочерним элементам (назовем их flex-элементы ).
Если обычная разметка основывается на направлениях блоков и строчных элементов, то flex -разметка базируется на направлениях flex -потока. Пожалуйста, обратите внимание на приведенную ниже схему из спецификации, объясняющую основную идею, стоящую за flex -разметкой:
Это свойство определяет flex -контейнер: строчной или блоковый, в зависимости от установленного значения. Также оно разрешает flex -контекст для всех его прямых потомков:
Container { display: flex; /* или inline-flex */ }
Отметим, что CSS -столбцы (columns ) не имеют эффекта во flex -контейнере.
Представьте, что flex -элементы в первую очередь располагаются либо в горизонтальных, либо вертикальных колонках:
Container { flex-direction: row | row-reverse | column | column-reverse; }
Container{ flex-wrap: nowrap | wrap | wrap-reverse; }
Это сокращенная форма свойств flex-direction и flex-wrap , которые вместе определяют главную и поперечную оси flex -контейнера. По умолчанию задается значение row nowrap :
flex-flow: <‘flex-direction’> || <‘flex-wrap’>
Container { justify-content: flex-start | flex-end | center | space-between | space-around; }
Container { align-items: flex-start | flex-end | center | baseline | stretch; }
Примечание : это свойство не будет работать, если есть только одна строка flex -элементов:
Container { align-content: flex-start | flex-end | center | space-between | space-around | stretch; }
Item {
order:
Если для всех элементов свойство flex-grow установлено в 1, то для каждого дочернего элемента будет задан одинаковый размер внутри контейнера. Если вы установите для одного из дочерних элементов значение 2, то он займет в два раза больше места, чем другие:
Item {
flex-grow:
Это свойство определяет для flex -элементов возможность сжиматься в случае необходимости:
Item {
flex-shrink:
Отрицательные числа недопустимы.
Это свойство определяет размер элементов по умолчанию перед распределением оставшегося пространства:
Item {
flex-basis:
Это свойство является сокращенной формой для комбинации свойств flex-grow, flex-shrink и flex-basis . Второй и третий параметры (flex-shrink и flex-basis ) задаются опционально. Значения по умолчанию: 0 1 auto :
Item { flex: none | [ <"flex-grow"> <"flex-shrink">? || <"flex-basis"> ] }
Доступные значения вы можете найти в описании свойства align-items :
Item { align-self: auto | flex-start | flex-end | center | baseline | stretch; }
Обратите внимание, что float , clear и vertical-align не работают с flex -элементами.
Давайте начнем с очень простого примера, решив практически ежедневную проблему: идеальное выравнивание по центру. Нет ничего проще, если использовать flexbox:
Parent { display: flex; height: 300px; /* Или что угодно */ } .child { width: 100px; /* Или что угодно */ height: 100px; /* Или что угодно */ margin: auto; /* Магия! */ }
Этот пример основывается на том факте, что свойство margin , установленное в значение auto , поглощает лишнее пространство. Поэтому такое задание отступа выравнивает элемент ровно по центру от обеих осей.
Теперь давайте воспользуемся еще несколькими свойствами. Предположим, что у нас есть список из 6 элементов, все они фиксированного размера (для эстетичности ), но с возможностью автоматического заполнения.
Мы хотим, чтобы они были красиво, равномерно распределены по горизонтальной оси таким образом, чтобы при изменении размера окна браузера, все смотрелось хорошо (без использования медиа запросов ):
Flex-container { /* Сначала создаем контекст flex-разметки */ display: flex; /* Затем определяем направление потока и хотим ли мы, чтобы элементы были обернуты. * Помните, что это то же самое, что и: * flex-direction: row; * flex-wrap: wrap; */ flex-flow: row wrap; /* Теперь мы определяем, как распределять оставшееся пространство */ justify-content: space-around; }
Готово! Все остальное — уже вопросы оформления. Здесь располагается демонстрация этого примера. Перейдите по ссылке и попробуйте изменить размер окна, чтобы посмотреть, что произойдет:
HTML:
CSS:
Flex-container { padding: 0; margin: 0; list-style: none; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-flex-flow: row wrap; justify-content: space-around; } .flex-item { background: tomato; padding: 5px; width: 200px; height: 150px; margin-top: 10px; line-height: 150px; color: white; font-weight: bold; font-size: 3em; text-align: center; }
Давайте попробуем еще кое-что. Допустим, у нас на самом верху веб-сайта находится меню навигации, выровненное вправо, но мы хотим, чтобы на экранах среднего размера оно располагалось по центру, а для устройств с маленьким экраном – располагалось в один столбец. Все достаточно просто:
CSS:
/* Большие экраны*/ .navigation { display: flex; flex-flow: row wrap; /* Выравнивание элементов к концу строки по главной оси*/ justify-content: flex-end; } /* Средние экраны */ @media all and (max-width: 800px) { .navigation { /* Для экранов среднего размера, мы центрируем элементы, равномерно распределяя свободное пространство между ними */ justify-content: space-around; } } /* Небольшие экраны */ @media all and (max-width: 500px) { .navigation { /* Для небольших экранах мы используем направление не по строкам, а по столбцам */ flex-direction: column; } }
HTML:
CSS:
Navigation { list-style: none; margin: 0; background: deepskyblue; display: -webkit-box; display: -moz-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-flex-flow: row wrap; justify-content: flex-end; } .navigation a { text-decoration: none; display: block; padding: 1em; color: white; } .navigation a:hover { background: darken(deepskyblue, 2%); } @media all and (max-width: 800px) { .navigation { justify-content: space-around; } } @media all and (max-width: 600px) { .navigation { -webkit-flex-flow: column wrap; flex-flow: column wrap; padding: 0; } .navigation a { text-align: center; padding: 10px; border-top: 1px solid rgba(255,255,255,0.3); border-bottom: 1px solid rgba(0,0,0,0.1); } .navigation li:last-of-type a { border-bottom: none; } }
Давайте попробуем сделать еще лучше, поиграв с «гибкостью » flex -элементов. Как насчет мобильной разметки в три столбца с заголовком и подвалом во всю ширину? И с выводом элементов, независимым от порядка, заданного исходным кодом:
CSS:
Wrapper { display: flex; flex-flow: row wrap; } /* Задаем для всех элементов ширину в 100% */ .header, .main, .nav, .aside, .footer { flex: 1 100%; } /* В этом случае для ориентации на мобильные устройства мы полагаемся на исходный порядок: * 1. заголовок * 2. навигация * 3. основная часть * 4. боковая панель * 5. подвал */ /* Средние экраны*/ @media all and (min-width: 600px) { /* Обе боковые панели располагаются в одной строке */ .aside { flex: 1 auto; } } /* Большие экраны */ @media all and (min-width: 800px) { /* Инвертируем порядок вывода первой боковой панели и основной части, и указываем основному элементу необходимость занимать максимум в два раза больше места, чем две боковые панели */ .main { flex: 2 0px; } .aside-1 { order: 1; } .main { order: 2; } .aside-2 { order: 3; } .footer { order: 4; } }
Свойство order управляет порядком, в котором дочерние элементы появляются внутри flex-контейнера. По умолчанию они располагаются в том порядке, как добавлены исходно во flex-контейнер.
flex-элементы могут быть переупорядочены с помощью этого простого свойства без изменения кода HTML.
Значение по умолчанию : 0.
Это свойство задаёт коэффициент роста, который определяет, насколько flex-элемент будет увеличиваться относительно остальных flex-элементов во flex-контейнере, при распределении положительного свободного пространства.
Если все flex-элементы имеют одинаковое значение flex-grow , то все элементы будут иметь и одинаковый размер в контейнере.
Второй flex-элемент занимает больше места относительно размера других flex-элементов.
Значение по умолчанию : 0.
flex-shrink задаёт коэффициент сжатия, который определяет, насколько flex-элемент будет сжиматься относительно остальных flex-элементов во flex-контейнере, при распределении отрицательного свободного пространства.
По умолчанию все flex-элементы могут быть сжаты, но если мы установим значение flex-shrink нулевым (без сжатия), то элементы сохраняют исходный размер.
Значение по умолчанию : 1.
Отрицательные числа недопустимы.
Это свойство принимает те же значения, что и свойства width и height , и определяет начальный основной размер flex-элемента, до того, как свободное пространство распределяется в соответствии с коэффициентами.
flex-basis указан для четвёртого flex-элемента и диктует его начальный размер.
Значение по умолчанию : auto.
Это свойство является сокращением для свойств flex-grow , flex-shrink и flex-basis . Крое других значений также можно установить auto (1 1 auto ) и none (0 0 auto ).
Значение по умолчанию : 0 1 auto .
W3C рекомендует использовать сокращённое свойство flex , вместо отдельных свойств, поскольку flex правильно сбрасывает любые неуказанные компоненты для типового использования .
Свойство align-self позволяет переопределить выравнивание по умолчанию (или значение, указанное через align-items ) для отдельных flex-элементов. Для понимания доступных значений обратитесь к описанию align-items для flex-контейнера.
Для третьего и четвёртого flex-элементов переопределено выравнивание через свойство align-self .
Об авторе : Рэйчел Эндрю - не только главный редактор журнала Smashing Magazine, но и веб-разработчик, писатель и спикер. Она автор ряда книг, в том числе The New CSS Layout , один из разработчиков системы управления контентом Perch . Пишет о бизнесе и технологиях на своём сайте rachelandrew.co.uk .
Краткое содержание: в этой статье мы рассмотрим свойства выравнивания во Flexbox и некоторые основные правила, как работает выравнивание по основной и поперечной осям.
Значения точно так же работают по вертикали, то есть если flex-direction применяется для column . Правда, у вас в столбце может не оказаться свободного места для распределения, если не добавить высоту или block-size контейнера, как в этом демо .
Затем я могу применить align-content с любым из значений .
Если в качестве flex-direction указано column , то align-content работает как в следующем примере .
Как и в случае с justify-content , мы работаем с группой строк и распределяем свободное пространство.
Container {
place-content: space-between stretch;
}
Соответствует этому:
Container {
align-content: space-between;
justify-content: stretch;
}
А такой код:
Container {
place-content: space-between;
}
Равноценен следующему:
Container { align-content: space-between; justify-content: space-between; }
Как вариант, её можно определить свойством height в контейнере:
Почему элементы растягиваются до размера самого высокого элемента - это потому что начальным значением параметра align-items является stretch . Элементы растягиваются по поперечной оси до размера контейнера в этом направлении.
Обратите внимание, что в многострочном контейнере каждая строка действует как новый контейнер. Самый высокий элемент в этой строке будет определять размер всех элементов в этой строке.
Кроме начального значения stretch , можно присвоить элементам align-items значение flex-start , в этом случае они выравниваются по началу контейнера и больше не растягиваются по высоте.
Значение flex-end перемещает их в конец контейнера по поперечной оси.
Если вы используете значение center , то элементы центрируются относительно друг друга:
Мы также можем выровнять их по базовой линии. Это гарантирует выравнивание текста по одному основанию, в отличие от выравнивания полей вокруг текста.
Все эти варианты можно попробовать в демо .
В следующем примере в контейнере используется align-items для выравнивания всей группы по центру, но также align-self для первого и последнего элементов.
Также может быть полезно подумать, как justify-content и align-content работают в CSS Grid Layout. В Grid эти свойства используются для распределения свободного пространства в grid-контейнере между grid-дорожками . Здесь тоже мы берём группу дорожек - и с помощью этих свойств распределяем между ними свободное пространство. Поскольку мы оперируем в группе и в Grid, и во Flexbox, то не можем взять отдельный элемент и сделать с ним что-то другое. Тем не менее, есть способ получить оформление макета, которое хотят верстальщики, когда говорят о свойстве self на главной оси. Это использование автоматических полей.
Автоматические поля очень хорошо работают во Flexbox для выравнивания отдельных элементов или групп элементов на главной оси. В следующем примере показан типичный случай. Есть панель навигации, элементы отображаются в виде строки и используют начальное значение justify-content: start . Я хочу, чтобы последний пункт отображался отдельно от других в конце строки - при условии, что в строке достаточно места для этого.
Берём этот элемент и указываем для свойства margin-left значение auto . Это значит, что поле пытается получить как можно больше места слева от элемента, то есть элемент выталкивается к правой границе.
Если вы используете автоматические поля на главной оси, то justify-content перестанет действовать, так как автоматические поля займут всё пространство, которое в противном случае распределялось бы с помощью justify-content .
В текущей спецификации вы не можете изменить запасное выравнивание. Есть примечание к спецификациям , которое допускает возможность указания произвольного запасного варианта в будущих версиях.
В следующем коде последний элемент слишком широк для контейнера, а при небезопасном выравнивании и гибком контейнере в левой части страницы элемент обрезается, поскольку переполнение выходит за границы страницы.
Container {
display: flex;
flex-direction: column;
width: 100px;
align-items: unsafe center;
}
.item:last-child {
width: 200px;
}
Безопасное выравнивание предотвращает потерю данных, перемещая переполнение на другую сторону.
Container {
display: flex;
flex-direction: column;
width: 100px;
align-items: safe center;
}
.item:last-child {
width: 200px;
}
Эти ключевые слова пока поддерживаются не всеми браузерами, однако демонстрируют, как спецификации Box Alignment добавляют контроля во 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:
Элементы в контейнере поддаются выравниванию при помощи свойства 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: auto;
и flex: none;
, что означает flex: 1 1 auto;
и flex: 0 0 auto;
соответственно.
Последним свойством гибких элементов остался align-self . Тут все просто, это то же самое, что align-items у контейнера, позволяющее переопределять выравнивание для конкретно взятого элемента.
Для начала обозначим основную ширину страницы, выровняем по центру, прижмем футер к низу страницы. Как всегда в общем.
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-grow: 1; 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.5rem;
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.25rem; 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. Спецификация позволяет строить весьма сложные макеты страниц при этом применяя простой код.
legno-board.ru - Сайт о саде, даче и комнатных растениях