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

В этой статье мы рассмотрим паттерны раскладки, которые подходят для реализации на флексбоксе. В качестве примера будет использоваться интерфейс приложения Tracks, реализующего принципы атомарного дизайна. Я расскажу о ситуациях, где флексбокс выручает и упомяну о подводных камнях сопряжения его с конкретными паттернами раскладки. Мы рассмотрим паттерны, вызывающие затруднения, предложим запасные варианты для решения проблем и дополнительные тактики для того, чтобы начать использовать это свойство уже сейчас.

Гибкие атомарные компоненты

В интерфейсе Tracks реализован подход, рассматривающий каждый фрагмент изолированно на основе принципов Брэда Фроста.

Философию атомарного дизайна можно рассматривать как аналог ЛЕГО-блоков для веб-дизайна. Научные термины такие как организм, молекула, атом используются для того, чтобы дать разработчикам классификацию элементов интерфейса и, соответственно, более глубокое понимание каждого фрагмента как части целого. Этот способ категоризации создает возможность для идентификации этих паттернов и предотвращает влияние на этот процесс внешних факторов, таких как сетки, цвета и расстояния. Построение интерфейса с микроуровня дает возможность более широкого многократного использования его базовых микроэлементов.

 Эти элементы приложения используются для вывода данных на основе принципов атомарного дизайна

Иллюстрация 1. Эти элементы приложения используются для вывода данных на основе принципов атомарного дизайна. Можете ли вы угадать, в каких из них используется Флексбокс? (увеличенная версия)

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

Основной интерфейс приложения Tracks

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

Дизайн интерфейса был передан как набор прототипов InVision, с документацией потока и пользовательского интерфейса. В течение первоначального аудита интерфейса, я начал выявлять области, где имеет смысл внедрить флексбокс. Я также решил использовать флексбокс для раскладки страницы, используя традиционные паттерны “боковая колонка слева, основной контент справа”, обычно реализуемые на плавающих блоках.

Одним из важных аспектов редизайна Tracks были требования о поддержке только современных браузеров, то есть Internet Explorer (IE) 10+, Android 4.1+ and iOS 7+, это позволяло внедрить флексбокс, хоть и с использованием префиксов. Что может быть запасным вариантом, если флексбокс не поддерживается? Можно использовать Modernizr и делать раскладку в не поддерживающих флексбокс браузерах традиционными способами, применяя их к сгенерированному классу .no-flexbox (также можно использовать запросы CSS с помощью @suppports). Например:

<ul class="flexbox-target">
  <li>…</li>
  <li>…</li>
  <li>…</li>
</ul>
html.flexbox ul.flexbox-target,
html.no-js ul.flexbox-target {
  display: flex;
  flex-direction: row;
}

html.no-flexbox ul.flexbox-target li,
html.no-js ul.flexbox-target li {
  display: inline-block; /* Could also use a float-positioned-layout system instead */
}

Там где поддержка флексбокса отсутствует, мы будем использовать display: inline-block. К этой же декларации мы добавим класс no-js, на случай отключения в браузере JavaScript. Каскад CSS будет работать даже там, где нет ни флексбокса, ни JavaScript, а также при проблемах с загрузкой. Флексбокс может сосуществовать с float, display: table и position: relative; браузеры, поддерживающие флексбокс использует его в приоритетном порядке, а браузеры его не поддерживающие ограничатся традиционными механизмами раскладки CSS.

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

Строчные паттерны

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

навигация с вертикальным центрированием

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

Разметка паттерна навигации административной панели состоит из тега nav, оборачивающего ссылки. Вот HTML этого паттерна:

<header role="banner">
  <nav role="navigation">
    <a href="pipeline.html">Back to pipeline</a>
    <a href="account.html">Account</a>
    <a href="users.html">Users</a>
    <a href="export.html">Export</a>
  </nav>
</header>

И соответствующие стили:

nav[role="navigation"] {
  display: flex;
  align-items: center; /* Center navigation items vertically */
}

nav[role="navigation"] a {
  display: inline-block; /* To avoid layout issues for inline elements with the order property in IE 10 */
}

nav[role="navigation"] a[href="pipeline.html"] {
  flex: 1;
}

CSS получился такой же минималистичный, как и разметка. Обратите внимание на inline-block, заданный для навигационных ссылок. Эта декларация решает любые возможные будущие проблемы в IE10, в случае, если вы решите поменять последовательность элементов со свойством order. Также мы знаем, что все внешние и внутренние отступы, заданные прямым потомкам флекс-контейнера вызывают проблемы с раскладкой в IE10, во избежание таких вещей имеет смысл всегда кроссбраузерно проверять верстку.

Паттерн навигации в шапке сайта с центрированным лого

Иллюстрация 4. Паттерн навигации в шапке сайта с центрированным лого часто встречается в вебе и легко адаптируется к флексбоксу. (увеличенная версия)

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

Раскладка состоит из коллекции элементов меню, размещенных по левую и правую сторону от логотипа в центре. Разметка для этого паттерна следующая:

<header class="pipeline-header" role="banner">
  <a href="pipeline.html" class="pipeline-logo">… </a>

  <nav class="pipeline-nav" role="navigation">…</nav>

  <form class="pipeline-search" role="form">…</form>

  <a href="#menu">…</a>
</header>

Флексбокс может снизить потребность в HTML-хаках и позволяет сохранять семантику, как продемонстрировано в разметке. Поддержка семантики важна, так как у этого HTML есть высокий шанс использования в будущем; есть для этого и много других причин, выходящих за пределы данного обсуждения.

До появления флексбокса разработчики использовали подходы display: inline-block и даже float: left, чтобы организовать строчную раскладку. Теперь флексбокс стал жизнеспособным вариантом и разработчиков ничто не вынуждает использовать плохие практики ради красивого дизайна. Требуемый CSS не столь краток, как в предыдущем примере с паттерном на иллюстрации 3, но он проще в реализации, чем старые методы.

.pipeline-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.pipeline-header > a {
  display: inline-block; /* IE 10 doesn't recognize order, so we do this to avoid odd layouts there. */
}

.pipeline-logo {
  flex: 1;
  order: 2;
  text-align: center;
}

.pipeline-nav {
  flex: 1.25;
  order: 1;
}

.pipeline-search {
  flex: 1;
  order: 3;
}

a[href="#menu"] {
  order: 4;
}

При использования флексбокса в паттерне с иллюстрации 3, помните, что последовательность в разметке может быть изменена. Если логотип требуется сдвинуть, это легко делается с помощью свойства order. Учитывайте, что порядок в разметке важен для доступности и это вызывает противоречия, когда дело доходит до флексбокса, особенно с учетом различной реализации доступности в браузерах. Браузеры (кроме Firefox) и скринридеры используют для навигации с клавиатуры порядок разметки, а не визуальный порядок, созданный средствами CSS.

изменение потока средствам флексбокса

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

Ниже представлен код для такой раскладки. Разметка не используется для изменения порядка вывода элементов.

<div class="container">
  <header role="banner"></header>
  <main role="main"></main>
  <footer role="contentinfo"></footer>
</div>

Здесь CSS используется для изменения порядка вывода элементов диаграмме справа иллюстрации 5.

.container {
  display: flex;
  flex-direction: columns; /* row is the default value */
}

header {
  order: 2;
}

main {
  order: 3;
}

footer {
  order: 1;
}

Этот тип раскладки используется не только для навигации. Вы могли видеть его в футере.

футер с навигацией

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

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

Строчные поля ввода

Формы могут быть кошмаром для разработчиков, особенно когда они тесно связаны с замысловатой структурой сетки, сделанной в Photoshop. Паттерн “inline label”, как я его называю, также важен для нашей индустрии как Fender Stratocaster для рок-музыки.

Строчные лейблы и поля ввода

Иллюстрация 7. Строчные лейблы и поля ввода это еще одна область использования флексбокса. Но будьте осторожны с тем, как текст лейбла отталкивает поле ввода в зависимости от количества текста. (увеличенная версия)

Как уже упоминалось в предыдущем разделе, вам надо определиться с тем, как контент будет распространяться внутри контейнера при изменении размера браузера или при наличии динамического контента.

Поля ввода с display:table и флексбокс

Иллюстрация 8. Решайте, как ваш контент будет расширяться. Слева display:table с вертикальным выравниванием по середине, справа флексбокс с выравниванием по центру. (увеличенная версия)

Эти скриншоты четко показывают ошибки флексбокса с динамическим или большим содержимым. Эффект на правом изображении я называю “выталкивание от центра”, это означает, что новое содержимое стремится от центра к краям.

Вот разметка для паттерна строчного лейбла на иллюстрации 8.

<div class="form-group">
  <label>…</label>
  <select>…</select>
</div>

Решением проблемы будет использование display: table; для длинного текста. Это позволяет контенту идти сверху вниз, а не от центра к краям.

.form-group {
  display: flex;
}

.form-group label {
  display: table;
  vertical-align: middle;
}

.form-group input {
  flex: 1;
}

Сочетание флексбокса и display: table это отличная техника, которую имеет смысл изучить глубже. Но при ее использовании важно всегда проверять раскладку в тестовом окружении, чтобы своевременно выявлять баги.

Поле ввода с кнопкой

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

Я видел очень много полей поиска, реализованных по этому паттерну. Это очень гибкий паттерн, который можно многократно использовать в самых разных шаблонах. Конечно, CSS может здесь помещать универсальности паттерна за счет использования свойств специфичных для контекста, этого надо избегать.

Необходимый HTML типичен и включает див-обертку для структуры флексбокса.

<div class="form-group">
  <input type="text" placeholder="Add a note…">
  <button>Add</button>
</div>

А вот и стили:

.form-group {
  display: flex;
}

.form-group input {
  flex: 1;
}

Выпадающее меню

Выпадающее меню с флексбоксом

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

Выпадающее меню состоит из колонки слева, содержащей вертикально центрированные строчные элементы, и списка элементов справа, в котором каждый элемент расположен на своей строке.

Меню основного интерфейса

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

Разметка для этой навигационного меню использует следующий HTML в качестве основы.

<nav class="menu">
  <div class="menu__options">
    <a href="export-data.html">Export</a>
    <a href="help.html">Get Help</a>
  </div>

  <div class="menu__items">
    <a href="account.html">Account</a>
    <a href="preferences.html">Preferences</a>
    <a href="users.html">Users</a>
    <a href="payment.html">Payments</a>
    <a href="logout.html">Logout</a>
  </div>
</nav>

Соответствующий ему CSS прост и понятен, все, как любят разработчики.

.menu {
  display: flex;
}

.menu__options {
  display: flex;
  align-items: center;
}

.menu__items {
  display: flex;
  flex-direction: column;
}

Нужная раскладка делается несколькими строчками кода. Плюс этот код отделен от сеточной структуры и у разметки остается семантический смысл. Это еще один пример возможностей флексбокса, позволяющих избежать сложных способов позиционирования и перегруженной разметки.

Медиа-объекты

медиа-объект

Иллюстрация 12. В паттерне медиа-объект используется флексбокс, SVG фиксированной ширины располагается слева, а все остальное содержимое флексбокса располагается рядом. (увеличенная версия)

В этом универсальном паттерне, известном как “медиа-объект”, изображение или видео располагается на одной стороне, а остальное содержимое рядом.

<div class="media-obj">
  <div class="media-obj__fig">…</div>
  <div class="media-obj__body">…</div>
</div>

CSS:



.medi-obj {
  display: flex;
  align-items: flex-start;
}

.media-obj__body {
  flex: 1;
}

Филип Уолтон поделился отличным подходом на своем сайте “Решения с флексбоксом” (я рекомендую всем изучить этот сайт). Филип дает несколько полезных напоминаний и советов по реализации определенных паттернов с флексбоксом, а также поддерживает репозиторий со всеми представленными на сайте паттернами.

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

Флексбокс отлично работает с этим паттерном, но будьте осторожны, ведь медийное содержимое может взаимодействовать с остальным содержимым так, как показано выше. В примере вы видите, как графическое пространство схлопывается, а текст выталкивается сверху. Это может показаться глупым примером, потому как, кто сделает свой браузер настолько узким? Но важно не это — просто мы должны понимать как содержимое соотносится со своим окружением перед тем, как использовать флексбокс.

Решением для этого паттерна будет задание изображениям max-width: 100% для медиа внутри флексбокса или использование фиксированной ширины изображений и медиа-запросов для их регулировки при необходимости.

Календарь на флексбоксе

Календарь

Календарь. (увеличенная версия)

Календарь это один из самых распространенных виджетов. Вы спросите, а почему бы не использовать таблицу? В нашем случае календарь используется для простого выбора дат, поэтому я решил использовать кнопки для дней, месяцев и годов и ограничить эти кнопки рамками рядов (каждый ряд календаря на неделю обернут отдельным div). Использование этого подхода позволяет уменьшить количество разметки и облегчить создание раскладки (огромная благодарность Шэйну Хадсону за построение логики и предоставление идеи).

Разметка действительно проста и включает в том числе и расположенные сверху кнопки для управления разбивкой календаря на страницы :

<div class="datepicker">
  <header class="datepicker__header flex-container">
    <button>Left Arrow</button>
    <span>2015</span>
    <button>Right Arrow</button>
  </header>

  <div class="datepicker__view flex-container">
    <button>Jan</button>
    <button>Feb</button>
  </div>
</div>

Этот CSS не может быть меньше, он достаточно краток для написания и осмыслен для чтения.

.flex-container {
  display: flex;
}

.datepicker__header {
  justify-content: space-between;
}

.datepicker__view {
  align-items: center;
  justify-content: flex-start;
}

календарь с выбором месяца

Иллюстрация 14. Это календарь с выбором месяца, слева использовано justify-content: space-between, а справа justify-content: flex-start, слева на один элемент-обертку больше. (увеличенная версия)

Эти два примера четко показывают необходимость планировать заранее. Как оборачивать контент? Как он будет реагировать на изменение зоны видимости? Нужно ли оборачивать контент? Все эти вопросы позволяют выбрать стратегию, соответствующую контексту.

Раскладка

Флексбокс великолепен для элементов интерфейса, но он неплох и в определенных паттернах раскладки. Типичный паттерн это основной контейнер (с главным контентом), плывущий в одну сторону и дополнительный контейнер, плывущий в другую.

Раскладка страницы с флексбоксом

Иллюстрация 15. Боковая колонка влево и основной контент вправо — это совершенный сценарий для флексбокса. Это также напоминает технику искусственных колонок. (увеличенная версия)

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

Наша разметка для административного раздела использует серию div, оборачивающих каждый контейнер как на изображении выше.

<div class="admin-ui">
  <div class="admin-ui__nav">…</div>

  <div class="admin-ui__body">
    <nav>…</nav>
    <main>…</main>
  </div>
</div>
.admin-ui__body {
  display: flex;
}

.admin-ui__body nav {
  flex: 30%;
}

.admin-ui__body main {
  flex: 70%;
}

Вот отличный запасной вариант для паттерна раскладки с иллюстрации 14. Паттерн не требует сложной сеточной структуры. А наш старый друг display: table всегда готов помочь при необходимости.

See the Pen Display: table-cell by Dennis Gaebel (@dennisgaebel) on CodePen.

display: table-cell это мощная CSS декларация, появившаяся еще в CSS2 и прекрасно работающая в качестве запасного варианта в данном контексте. Это правило вынуждает элемент вести себя как ячейка таблицы и это поведение является наиболее близкой заменой флексбокса. Также можно изменить порядок вывода, используя приемы табличной верстки с декларациями display: table-header-group и display: table-footer-group.

Приклеенные футеры

Футер, расположенный внизу зоны видимости это одна из стандартных задач для веб-разработчиков. Если контента на странице достаточно, это происходит само собой, но задача состоит в том, чтобы футер был внизу страницы независимо от наличия контента.

Приклеенный футер

Иллюстрация 16. Приклеенный футер в правой боковой колонке. (увеличенная версия)

Вот разметка правой боковой колонки:

<div class="admin-edit">
  <button class="admin-edit__save">Save Deal</button>

  <div class="admin-edit__footer">
      <p>…</p>
      <button>Copy</button>
      <button>Delete</button>
  </div>
</div>

И стили:

.admin-edit {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

При отсутствии в браузере флексбокса у нас есть запасной вариант, поддерживающий раскладку методами времен IE6.

See the Pen Sticky Footer on Steroids by Dennis Gaebel (@dennisgaebel) on CodePen.

Иллюстрация 17. Приклеенный футер с флексбоксом и запасным вариантом в виде display: table-row;. Работает во всех браузерах вплоть до IE6.

Еще одной частью раскладки, где стоит внедрить флексбокс является основной вид приложения, так называемый “трубопровод”. У каждой карты задана фиксированная ширина и свойство flex. Это дает всем картам последовательные и четкие пропорции.

Трубопровод

Трубопровод. (увеличенная версия)

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

Будьте осторожны

При тестировании верстки в браузере мы заметили, что сочетание свойства flex со свойствами, задающими отступы, ломает раскладку в IE10. (см. пример 18)

.parent {
  display: flex;
}
.parent .child {
  flex: 1;
  padding: 20px; /* Breaks layout in IE 10 */
}

Когда обертке модуля задан display: flex, а дочерние элементы, используют свойство flex вместе с padding или margin, раскладка ломается в IE 10.

Запрос @supports (display: flex) {} это нативная имплементация modernizr. К сожалению, поддержка @supports на данный момент невелика и рассчитывать на него не стоит, вместо этого есть другие запасные варианты:

  • Modernizr для детектирования поддержки флексбокса и генерируемый им класс no-flexbox.
  • Для центрирования используйте transform или display: table .
  • display: table это основной запасной вариант.
  • Управляйте порядком на основе разновидностей табличного представления table-caption, table-header-group и table-footer-group.
  • Используйте флоаты в качестве запасного варианта для основной раскладки страницы.
  • Используйте display: inline и display: inline-block в качестве запасного варианта в строчных паттернах.
  • Используйте условные комментарии для замены флексбокса в IE9-.

Йэн Девлин написал отличную статью о техниках раскладки с табличным отображением. Он дал отличный обзор контроля раскладки с помощью table-caption, table-header-group и table-footer-group.

Заключение

Флексбокс сегодня совершенно реален. После долгих лет разработки его спецификация стала стабильной и позволяет решать основные вопросы, связанные с раскладкой. Я также рекомендую курс из 20 видео по флексбокс на сайте flexbox.io. Первые 13 видео раскрывают основы флексбокса, а в оставшихся 7 строится все от навигации до мобильного приложения, целиком на флексбоксе. Сейчас уже нет смысла спорить о флексбоксе, его надо использовать в работе.

Дополнительные материалы