CSS-live.ru

Золотая рыбка CSS3 Grid Layout (часть 1)

После написания моей недавней статьи про плотную упаковку блоков я всё-таки не смог удержаться от соблазна более подробно познакомиться со спецификацией CSS Grid Layout Module Level 1, и меня удивило, почему я раньше не обращал на неё внимание. Поэтому в виде заслуженного наказания я решил написать о ней эту статью.

Возможно многие из вас уже задавали себе вопрос, что делает в названии статьи фраза «Золотая рыбка» и как это связано со спецификацией CSS Grid Layout? Дело в том, что по мере изучения этого модуля я пришёл к мнению, что это настолько мощный и невероятно гибкий механизм, что, как и золотая рыбка, он может выполнить любое ваше желание. Даже модный Flexbox не может похвастаться таким арсеналом.

Вот список некоторых возможностей этого модуля:

  • легко построить любую сетку, каркас, n-колоночный макет, стандартный набор из шапки, нескольких колонок и подвала, и вообще соорудить макет любой сложности.
  • проще простого манипулировать любым grid-элементом: перемещать его в любой столбец/строку сетки, растягивать/сжимать на любую ширину/высоту, а также указывать размеры, составляющие, например, сумму ширин соседних двух grid-элементов и т.д.
  • также grid-элементами можно отдельно управлять в своих столбцах/строках сетки, например, выравнивая их по любой из четырёх сторон, по базовой линии, по середине и т.д.
  • в любых столбцах/строках сетки можно размещать сколько угодно grid-элементов, накладывая их друг на друга и указывая им z-index, работающий также, как и при position: absolute/relative/fixed.
  • вы наверняка знакомы со значением repeat свойства backroud-position для повторения фона изображения. Так вот, у Grid Layout тоже есть похожее значение, которое позволяет повторять части сетки при её построении.

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

Для начала я решил сделать краткий обзор некоторых свойств/значений, чтобы впоследствии чувствовать себя более увереннее.

Краткий обзор

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

Судя по всему, закончила свою историю и отправляется в музей (с безжалостным статусом «заметка рабочей группы») бывшая когда-то многообещающей спецификация CSS3 для раскладки — Template Layout (разметка областей страницы шаблонами в коде, похожими на ASCII-арт). Но сильно скучать без нее нам не придется: все интересные наработки оттуда перекочевали в модуль Grid Layout 1-го уровня, новая редакция которого была выпущена неделей раньше.

Т.е., несмотря на то, что многообещающая спецификация Template Layout закончила своё существование, все её сильные стороны перешли в наш Grid Layout, который к тому же обновился и теперь имеет более человеческий синтаксис. В общем, как вы поняли, рассматривать мы будем именно Grid Layout 1-го уровня в последней редакции. Именно первого уровня, не удивляйтесь: перспективные новинки CSS не обязаны иметь в названии цифру 3. Модули, вводящие новую функциональность — как этот — стартуют с самого начала, с единицы.

И второе, где мы можем тестировать эту спецификацию? Этот модуль работает только лишь в браузере IE10+ (что удивительно) и в последних версиях Chrome, но только если пройти по ссылке chrome://flags и включить параметр «Включить экспериментальные функции веб-платформы».

Что касается IE10+, то в нём поддерживается предыдущая версия Grid Layout, поэтому он пролетает мимо, и мы будем тестировать примеры в браузере Chrome. Так что подготовьте полигоны и в путь!

Спецификация Grid Layout

Изначально у меня была идея пойти по спецификации Grid Layout, описывая каждое свойство и значения, как в статье Визуальное руководство по свойствам Flexbox из CSS3 сделал Димитар Стоянов, но после глубокого погружения в Grid Layout я осознал, что синтаксис в этой спеке —  реальная жесть, и что не стоит травмировать вашу психику, поэтому давайте лучше начнём с простых практических примеров, усложняя их по ходу пьесы и параллельно изучая синтаксис.

Как включить Grid-механизм

На самом деле проще простого, надо всего лишь выставить grid-контейнеру display: grid и его дочерние элементы будет обладать магией grid’ов.

.grid {
  display: grid;
}

Простой трёхколоночный макет и единица измерения «Фактор гибкости»

Для начала давайте создадим обычный трёхколоночный макет (пока без шапки и подвала) и поиграемся с колонками.

Допустим у нас есть такая разметка:

<div class="grid">
	<div class="grid-item grid-item1">item 1<br />
		item 1<br />
		item 1<br />
		item 1</div>

	<div class="grid-item grid-item2">item 2</div>

	<div class="grid-item grid-item3">item 3</div>
</div>

Сетка в Grid Layout состоит из колонок и рядов, а следовательно, для управления ими есть конкретные свойства.

В этом примере мы воспользуемся свойством именно для колонок — grid-template-columns для grid-контейнера, которое представляет из себя собирательное свойство для множества значений для управления колонками.

.grid {
  display: grid;
  
  /*свойство для колонок*/
  grid-template-columns: 100px 1fr 100px;
}

А вот и результат:

3-columns-1

Ширина каждой боковой колонки составляет 100px, а средняя растягивается на всю оставшуюся ширину. Причём заметьте, что высота колонок подстраивается под высоту самой высокой (в нашем случае, это первая колонка). Но как это работает?

Как вы могли заметить, в свойстве grid-template-columns я использовал три значения, каждое из которых отвечает за свою колонку. С первым и последним всё понятно — по 100px в ширину будут боковые колонки, но на среднем значении остановимся подробнее.

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

Но чтобы стало совсем понятно, что такое «fr», давайте рассмотрим ещё один пример.

Изменим CSS, сделав ширину контейнера фиксированной и поделив колонки на фракции:

.grid {
  display: grid;
 
  /* выставим фикс. ширину контейнеру */
  width: 600px;
  
  /* применим для всех колонок фракции гибкости */
  grid-template-columns: 1fr 2fr 3fr;
}

И вот что вышло:

fix-fr-1

На рисунке показана ширина каждой колонки. В значении свойства grid-template-columns сумма всех фракций равна 6 (1fr + 2fr + 3fr): 1fr приходится на первую колонку, 2fr на вторую, и 3fr — на третью. Соответственно, если ширина контейнера 600px, то мы делим 600px на 6 фракций и получаем ширину каждой фракции — 100px. Ну а дальше всё просто: т.к. первая колонка занимает одну фракцию, то её ширина будет 100px, вторая колонка занимает две фракции и становится шириной в 200px, ну а третью посчитайте сами;)

Кому-то пример с nfr мог напомнить мог напомнить поведение flex: n n 0px во флексбоксах. Так и есть, и эта аналогия явно упомянута в спецификации.

Ширина колонок по минимальному/максимальному размеру контента

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

Немного изменим HTML-код, добавив в первую и третью колонку немного текста, убрав в первой переносы строк (<br>):

<div class="grid">
    <div class="grid-item grid-item1">
       Represents the largest min-content contribution of the grid items occupying the grid track.
    </div>
    <div class="grid-item grid-item2">
       item 2
    </div>
    <div class="grid-item grid-item3">
       Represents the largest max-content contribution of the grid items occupying the grid track.
    </div>
</div>

А свойству grid-template-columns выставим значения min-content 200px max-content;:

.grid {
  display: grid;
  
  grid-template-columns: min-content 200px max-content;
}

И посмотрим на результат:

3-columns-minmax-content

Несмотря на то, что в боковых колонках +- одинаковое количество контента, они ведут себя по разному. Почему? На самом деле всё просто: т.к. первой колонке выставлено значение min-content, она ужимается по минимальному содержимому (в данном случае это слова «Represents» и «contribution»), а третья колонка с max-content, напротив, расширяется по максимуму, чтобы вместить всё содержимое в одну строку (т.е. все слова). Кстати, если даже мы специально выставим контейнеру маленькую ширину (напр. width: 100px;), то контейнер не ужмётся до 100px, а вместо этого станет ровно той ширины, которая нужна, чтобы уместить результат всех значений в свойстве grid-template-columns. Можете убедиться в этом в тестовом примере.

Значение для минимальной и максимальной ширины конкретной колонки

Для более гибкого управления отдельными колонками мастера этой спецификации придумали значение minmax(min, max). Рассмотрим его также на примере, немного изменив наш код.

.grid {
  display: grid;
  
  grid-template-columns: 200px minmax(100px, 300px) 200px;
}

Чтобы лучше понять, как работает это значение, я сделал два скриншота (и на всякий случай наглядный пример):

minmax-100 Рисунок 1. Минимальная ширина экрана

minmax-300 Рисунок 2. Максимальная ширина экрана

Обратите внимание на поведение средней колонки при уменьшении/увеличении ширины экрана. Её ширина варьируется от 100 (рисунок 1) до 300px (рисунок 2). Причиной тому являются значения в функции minmax(). Первое значение minmax() представляет минимальный размер колонки, а второе — максимальный. Следовательно, т.к. мы установили значения в (100px, 300px), то средняя колонка будет не меньше 100, но не больше 300px. Также вы можете посмотреть полноэкранный пример и сами поиграться с шириной экрана.

Замечание: значения функции minmax() не обязательно должны представлять собой единицы измерения в пикселях. В эту функцию можно подставлять размеры, заданные в любых единицах длины (хоть абсолютных, хоть относительных). Можно подставлять и проценты, функцию calc() (с размерностью длины) и значение «auto». Кстати, о последнем в спецификации сказано следующее:

auto

В качестве максимума идентично max-content. В качестве минимума представляет собой наибольший минимальный размер (как указано в min-width/min-height) элементов сетки, занимающих колонку или ряд сетки.

Т.е. «auto» за максимальную длину берёт значение max-content, растягивая grid-элемент по максимальному содержимому, а за минимальную — значение, указанное в min-width/min-height.

Другие способы построения колонок

Перечисленные выше значения для grid-template-columns — лишь цветочки, поскольку CSS Grid Layout предоставляет и другие, уникальные механизмы для построения колонок. И сейчас мы их рассмотрим.

Именованные линии

Следующий способ, который мы сейчас обсудим, кое-что мне напомнил. Пару лет назад мои друзья с форума придумали проект для спецификации CSS, который они назвали «Guidelines», т.е. «Направляющие». Идея спецификации заключалась в удобном и простом механизме для построения сетки при помощи направляющих. Похожий механизм есть в фотошопе. К сожалению из-за большого цейтнота моим коллегам не удалось завершить свою идею и добраться до W3C, но идея не пропала полностью и что-то похожее появилось в модуле Grid Layout.

Рассмотрим простой пример.

Для начала добавим в HTML-код дополнительный grid-элемент:

<div class="grid">
    <div class="grid-item grid-item1">
       Represents the largest min-content contribution of the grid items occupying the grid track.
    </div>
    <div class="grid-item grid-item2">
       item 2
    </div>
    <div class="grid-item grid-item3">
       Represents the largest max-content contribution of the grid items occupying the grid track.
    </div>
    <div class="grid-item grid-item4">
       item 4
    </div>
</div>

А CSS будет таким:

.grid {
  display: grid;
  
  grid-template-columns: [first] 100px [main] 200px [right] 300px [right2] 150px [last];
}

И результат:

Grid-Lines-1

В нашем случае линия «first» идёт в списке значений первой, а значит она располагается на отметке 0px. Значение 100px между ней и следующей линией (main) задает расстояние или интервал между этими линиями, а значит, ширину первой колонки. Тоже самое происходит с остальными линиями и интервалами между ними. Вторая линия (main) находится на отметке 100, третья (right) на отметке 300, а четвёртая (right2) на отметке 500px, а пятая (last) на отметке 650px. Следовательно ширина от (main) до (right) равна 200px, от (right) до (right2) — 300px, а от (right2) до (last) — 150px.

Замечание: во всех примерахв в статье надо учитывать, что если grid-элементов будет больше чем «колонок», указанных в свойстве grid-template-columns, то все лишние элементы окажутся на следующей строке. Например. если не менять последний CSS-код и добавить пару grid-элементов, то результат будет таким:

Grid-Lines-2

А теперь давайте усложним наш пример, взяв его прямо из спецификации. Оставим количество grid-элементов равным шести и немного изменим CSS:

.grid {
  display: grid;
  
  grid-template-columns: [first nav] 150px [main] 1fr [last];
  grid-template-rows: [first header] 50px [main] 1fr [footer] 50px [last];
}

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

grid-named-lines Рисунок из спецификации: именованные линии

Первым делом обратите внимание на новое свойство grid-template-rows. Оно похоже на знакомое нам свойство grid-template-columns, только отвечает не за колонки, а за ряды, что в принципе и понятно из его названия. Далее заметьте в CSS-коде, что первое значение каждого свойства состоит из двух именнованных линий (first nav) и (first header). Это означает, что значение в скобках может содержать любое количество линий, идущих через пробел.

Замечание: например, если в виде значения для grid-template-columns/grid-template-rows мы используем именованные линии, то это значение не обязательно должно начинаться (или заканчиваться) именно с именованной линии (как в примере выше «[first] 100px … [last]»). Мы легко можем написать так: «100px [first] 100px … [last] 100px». В этом случае ширина первой и последней колонки будет по 100px.

Обновление от 01.04.2016: старый синтаксис именованных линий заменен на актуальный. Заменены круглые скобки на квадратные.

P.S. Это тоже может быть интересно:

14 комментариев

  1. Спасибо за познавательную статью.
    От себя добавлю что в firefox можно включить поддержку grid с помощью layout.css.grid.enabled в about:config
    Но походу единицу измерения fr он не понимает

    1. Да, в Firefox ведется работа над реализацией Grid Layout, очень экспериментальная поддержка отдельных свойств есть и больше половины тикетов в Багзилле на эту тему уже закрыто. Но, видимо, этого всё еще недостаточно для того, чтобы уже анонсировать эту поддержку для веб-разработчиков. Судя по этому источнику, включить Grid планируется «в Firefox 42, а то и раньше».

  2. По моему в grid-template-rows: (first header) 50px (main) 1fr (footer) 50px (last);
    50px между first header и main маловато, у меня в хроме контент из них начинает наезжать друг на друга, нужно хотя бы 90px поставить, а лучше 100px

    1. С другой стороны, если процент лисицы упадет меньше какого-то порога, поддержка в Хроме автоматом будет означать поддержку «почти везде». В мобильном сегменте — и того раньше.

      Но я не думаю, что дела у мозиллы пойдут настолько плохо:). По-моему, пик оттока с Фокса на Хром как раз пройден, а новые версии обещают массу вкусностей (особенно для разработчиков). Впрочем, поживем-увидим)

        1. старый добрый Билл тоже обещает для разработчиков кучу плюшек, поэтому тормознутый ИЕ возможно в скором времени тоже о себе заявит
          Хамарин уже часть девелоперов переманил на свою сторону..

          1. Конкретно по поводу гридов «Билл», похоже, всё-таки пока довольствуется своей старой версией (зато он пока единственный, где гриды хоть как-то можно использовать в продакшне). Но на это еще можно повлиять.

    2. Я наоборот вернулся с Хрома на лисицу… Бардак с плагинами там откровенно напряг. Плюс периодически возникает неконтролируемый жор ресурсов… Впрочем я под Linux’ом сижу, тут другая кухня чем на виндах. Да и ослика никогда под Linux не будет…

  3. Спасибо за статью.
    Нашел небольшую ошибку:
    grid-template-columns: (first nav) 150px (main) 1fr (last); — со скобками код работает некорректно, судя по спеке там должны быть квадратные скобки — []

    1. Совершенно верно, теперь скобки должны быть квадратные. Спека поменялась почти сразу после выхода этой статьи, о чем и написано в самом начале ее продолжения:).

  4. если ширина контейнера 600px, то мы делим 600px на 6 фракций

    Почему на 6? Вы в примере объявили три фракции.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.