HTML5 INSIGHT


Введение

intuition game grid

Модуль CSS3 Grid Layout — это один из самых интересных, на мой взгляд, модулей в семействе CSS3. Официальная история модуля в виде черновика спецификации насчитывает сегодня чуть менее года. О предварительном анонсе еще с названием CSS Grid Alignment на TPAC 2010 Владимир Юнев писал еще в декабре 2010. Надо также отметить, что с чуть другим названием и несколько отличным синтаксимом, но с той же сутью, он был заявлен в качестве WD еще в 2007г. Сегодня работы по доводке модуля идут полным ходом, предварительная реализация уже есть в Internet Explorer 10 и есть надежда, что поддержка новых возможностей также появится в будущих версиях других популярных браузеров.

Зачем нужен Grid Layout?

Задача, которую решает модель CSS3 Grid Layout очень проста и понятна любому веб-верстальщику (да и не только ему): предоставить удобный механизм расположения контента по виртуальной сетке.

В отличие от старых табличных подходов, основанных на использовании table, здесь не замусоривается семантика документа и представление четко отделено от содержания. В отличие от различных вариаций блочной верстки со становящимися при сколь-нибудь сложной структуре безумными и многоэтажными float'ами в сочетании с вручную просчитанными отступами, здесь описание стилей становится тривиально простым и прозрачным. В отличии от абсолютного позиционирования с привязкой к координатам, здесь сохраняется гибкость. В отличие от популярных сегодня css/js — библиотек для разметки по сетке путем указания соответствующих классов (взять хоть те же bootstrap или yui css grids), здесь не замусоривается привязка классов.

Сплошное удовольствие! Осталось только дождаться широкой поддерки :) Впрочем, если вы планируете делать приложения в стиле Metro для Windows 8 на html/js, ключевые возможности, описываемые в модуле CSS3 Grid Layout, уже работают — и можно двигаться вперед, используя удобные и практичные механизмы.

Основы

Чтобы быстрее разобраться с тем, как работает Grid Layout, давайте сразу же начнем с примера. Представьте себе, что вам нужно решить классическую задачу верстки трехколоночного макета с примерно таким содержимым:

<section>
    <header>Title</header>
    <nav>Menu</nav>
    <article>Content</article>
    <aside>Notes</aside>
    <footer>Footer</footer>
</section>

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

smaple grid layout

Это классическая задача, поэтому я не буду останавливаться на том, как она решается в случае использования уже традиционных подходов для многоколоночной блочной верстки. Давайте посмотрим, что можно сделать с помощью Grid Layout (я пропускаю браузерные префиксы в примерах, однако, на практике из нужно добавлять — для IE это -ms-).

Вот пример кода на CSS:

section {
    display: grid;
    grid-columns: 150px 1fr 200px;
    grid-rows: 50px 1fr 50px;
}

section header {
    grid-column: 2;
    grid-row: 1;
}

section nav {
    grid-column: 1;
    grid-row: 2;
}

section article {
    grid-column: 2;
    grid-row: 2;
}

section aside {
    grid-column: 3;
    grid-row: 2;
}
                        
section footer {
    grid-column: 2;
    grid-row: 3;
}

В самом простом варианте все, что нужно сделать, сводится к тому, чтобы указать в контейнере (section), что содержимое внутри него нужно располагать по сетке и задать виртуальную сетку, используя свойства grid-columns и grid-rows (чуть ниже я расскажу о том, какие значения они принимают). Далее для каждого элемента внутри достаточно сказать, где он должен располагаться.

using grid layout sample

И это все! Итого:

  1. Виртуальная сетка: grid-columns и grid-rows в контейнере
  2. Размещение элемента: grid-column и grid-row.

Теперь давайте разбираться в деталях и дополнительных возможностях.

Виртуальная сетка

Треки: строки и столбцы

Для создания виртуальной сетки, по которой в дальнейшем размешаются элементы, необходимо описать треки сетки (Grid Tracks) — строки и столбцы внутри сетки:

#grid {
    display: grid;
    grid-columns: 150px 1fr; /* два столбца */
    grid-rows: 50px 1fr 50px; /* три строки */
}

Линии сетки

Треки располагаются между линиями сетки (Grid Lines), которые, в свою очередь, находятся справа-слева и сверху-снизу от каждого трека:

grid lines

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

#item {
    grid-column: 2;
    grid-row: 2;
}

grid item positioning

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

Растяжение на несколько ячеек

Чтобы растянуть элемент на несколько ячеек сетки, можно использовать свойства grid-row-span и grid-column-span:

#item {
    grid-column: 2;
    grid-column-span: 3;
    grid-row: 2;
    grid-row-span:2;
}

grid spanning

По умолчанию, оба свойства установлены в значение 1, что соответствует растяжению доступного пространства от указанной линии до следующей (+1).

Повторяющиеся треки

Часто бывает так, что предполагаемая для использования сетка содержит какой-то характерный шаблон:

#grid {
    display: grid;
    grid-columns:  24px 120px 24px 120px 24px 120px 24px 120px 24px;
    grid-rows: 1fr 24px 1fr 24px 1fr 24px;
}

grid tracks pattern

Было бы удобным описывать такие сетки в более компактном и прозрачном виде — и такая возможность есть! Пример выше можно описать так:

#grid {
    display: grid;
    grid-columns:  24px (120px 24px)[4];
    grid-rows: (1fr 24px)[3];
}

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

===

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

Единицы измерения

Как вы уже могли заметить выше, местами я использовал необычное значение при указании размеров некоторых столбцов и строчек — 1fr. Об этом и других возможных значениях при указании длины мы сейчас и поговорим.

При описании ширины колонок и высоты строк (размеров треков) можно использовать следующие единицы и значения:

  • линейные размеры — стандартные единицы указания длины, определенные в модуле CSS3 Values and Units, например, pt, px, em, vw и т. д.;
  • проценты — размер трека в процентах от размеров контенера с сеткой (хотя если высота или длина сетки зависит от контента, результат будет неопределенным);
  • доли (fraction) — неотрицательное число с последующей единицей измерения fr, размер каждой доли берется пропорциональным указанному числу (см. подробнее ниже);
  • max-content — ключевое слово для указания максимальной длины из максимальных длин элементов в треке;
  • min-content — ключевое слово для указания максимальной длины из минимальных длин элементов в треке;
  • minmax(min, max) — задает диапазон значений (принцип работы можно описать как minmax(p, q) = max(p, min(fill-available, q)) — максимум из нижнего порога и минимума доступного пространства и верхнего порога);
  • auto — ключевое слово, эквивалентное minmax(min-content, max-content).

Доли

Давайте попробуем разобраться, как работают доли (fraction value)? Сетка занимает некоторое пространство по ширине и высоте. Оно может зависеть от контента, быть жестко фиксированным или занимать все доступное пространство во внешнем контейнере. Далее, при описании треков части колонок и строк вы можете явно задать, какого размера они должны быть, для еще какой-то части вы можете указать, что из длина зависит от контента.

Теперь, если из доступной длины, отведенной под сетку по вертикали или горизонтали, вычесть сумму всех таких явных или “контентных” длин, оставшееся пространство распределяется между остальными треками пропорционально указанным в них долях (размер доли, деленный на сумму всех таких долей):

fractions sample

На примере выше это три столбца c ширинами в соотношении 2:1:1 и две строки с высотами в соотношении 5:2.

Пример

В черновике спецификации приводится такой комплексный пример, позволяющий увидеть всю эту схему с разными типами значений сразу:

#grid {
    display: grid;
    grid-columns: 100px 1fr max-content minmax(min-content, 1fr)
}

Здесь определяются следующие линии (треки, они же колонки в данном случае):

  1. Стартовая первая линия.
  2. Линия в 100px от первой.
  3. Еще одна линия на расстоянии ½ оставшегося после всех рассчетов пространства — от второй линии.
  4. И еще одна линия, чье расстояние от третьей равно максимальному из размеров контента элементов в колонке.
  5. Наконец, последняя линия, расположенная от четвертой на расстоянии равном либо минимальной длине элементов в колонке, либо ½ оставшегося пространства, смотря, что больше.

Тут есть еще небольшой нюанс относительно учета долей, которые оказались внутри функции minmax: они учитываются в общей сумме долей только если обозначены в максимальной позиции. Доли, обозначенный в минимальной позиции считаются равными 0px. Подробнее тонкости рассчета смотрите в спецификации.

===

Следующим шагом мы попробуем разобраться, как элементы размещаются между линиями сетки (внутри ячеек).

Привязка элементов

Теперь, когда элементы “привязаны” к линиям сетки, возникает естественный вопрос: а как же они располагаются между линиями?

alignment to lines

Привязка элементов к границам ячейки контролируется с помощью свойств grid-column-align и grid-row-align. Для управления можно использовать следующие значения:

  • start
  • end
  • center
  • stretch

Думаю, действия очевидны из названия, отмечу лишь, что в каждом случае речь идет о позиционировании элемента по его margin-боксу. По умолчанию используется значение stretch.

alignment to lines samples

Привязка в колонках (во горизонтали) соответствует направлению текста (например, в арабском языке start будет справа, а end слева). Привязка в строках (по вертикали) совпадает с направлением потока блоков (это, кстати, означает, что в некоторых восточно-азиатских языках строки и столбцы могут поменяться местами).

Управление слоями

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

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

grid items overlaping

Чтобы управлять порядком отображения таких слоев, текущая версия спецификации расширяет возможности z-index, позволяя управлять слоями элементов внутри сетки.

Замечание: в предыдущей версии спецификации, на которую опирается текущая версия IE10 (platform preview 5), для этих целей было определено отдельное свойство grid-layer, чтобы не конфликтовать с z-index, однако, далее в процессе обсуждений в рабочей группе это решение было пересмотрено.

Пример использования:

#grid {
    display: grid;
    grid-columns: (1fr)[3];
    grid-rows: (1fr)[4];
}

#A {
    grid-column:1;
    grid-row:3;
    grid-column-span:2;
}

#B {
    grid-column:1;
    grid-row:1;
    grid-row-span:2;
    /* grid-layer: 10; */
    z-index:10;    
    margin-top:10px;
}

#C {
    grid-column:1;
    grid-row:1;
    grid-column-span:2;
    margin-left:50px;
}

#D {
    grid-column:2;
    grid-row:3;
    grid-row-span:2;
    grid-column-span:2;
    margin:10px 0 0 10px;
}

#E {
    grid-column:2;
    grid-row:2;                
    /* grid-layer: 5; */
    z-index:5; 
    margin: -20px;
}

grid layers sample

css3 grid library

Чтобы начать работать с сетками в браузерах, (еще) не поддерживающих модуль CSS Grid Layout, можно воспользоваться js-библиотекой eCSStender и расширением CSS3 Grid Alignment, обеспечивающими поддержку следующих свойств:

  • display: grid
  • grid-columns & grid-rows
  • grid-column & grid row
  • grid-column-span & grid-row-span

Пример работы можно посмотреть тут.

Немного о будущем, или какие еще возможности описаны в модуле

Наконец, давайте немного приоткроем двери в будущее и подглядим, какие еще возможности готовятся в модуле CSS3 Grid Layout.

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

Указание конечной линии для привязки элемента

Опционально для привязки элемента к сетке можно указать не только начальную, но и конечную линию.

#item {
    grid-column: 2;
    grid-row: 2 4;
}

grid lines with end option

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

Именованные линии сетки

Для удобства линиям можно давать названия. Это делается вставкой в соответствующих местах строковых значений при описании треков (можно давать несколько имен, если это имеет практический смысл, например, с точки зрения семантики разметки):

#grid {
    display: grid;
    grid-columns: "nav" "first" 150px "content" 1fr "last"; 
    grid-rows: "header" 50px "content" 1fr "footer" 50px; 
}

Далее при описании привязки элементов можно ссылаться на эти имена:

#menu {
    grid-column: "nav";
    grid-row: "content";
}

#header {
    grid-column: "content";
    grid-row: "header";
}

#article {
    grid-column: "content";
    grid-row: "content";
}

named grid lines

Также спецификация вводит 4 заранее именованных линии — вертикальные и горизонтальные start и end, фактически, обрамляющие всю сетку. Это полволяет, к примеру, расположить элемент “от второго столбца и до последнего”, не задумываясь об общем количестве столбцов.

#menu {
    grid-column: 1;
    grid-row: start end;
}

#footer {
    grid-column: 2 end;
    grid-row: 3;
}

start and end grid lines

Именованные ячейки и шаблоны

Еще один способ размещения элементов по сетке — заключается в использовании шаблонов, позволяющих описать виртуальную структуру блоков:

#grid {
     display: grid;
     grid-template: "ln"
                    "ma"
                    "ba"
                    "ff";

     grid-columns: auto minmax(min-content, 1fr); 
     grid-rows: auto minmax(min-content, 1fr) auto auto;
}

grid template sample

При этом для размещения элемента с прявязкой к той или иной виртуальной ячейке, достаточно, сослаться на нее соответствующим правилом:

#article {
     grid-cell: "a";
}

Такой подход оказывается особенно удобным, если в зависимости от различных условий, например, разрешения экрана, вам нужно менять расположение элементов и даже переделать саму сетку. В подобной ситуации Grid Layout хорошо сочетается с Media Queries:

@media (orientation: portrait) {
    #grid {
        display: grid;
        grid-template: "ln"
                       "ma"
                       "ba"
                       "ff";

        grid-columns: auto minmax(min-content, 1fr); 
        grid-rows: auto minmax(min-content, 1fr) auto auto;
    }
}

@media (orientation: landscape) {
    #grid {
        display: grid;
        grid-template: "ln"
                       "ma"
                       "mb"
                       "sf";

        grid-columns: auto minmax(min-content, 1fr); 
        grid-rows: auto minmax(min-content, 1fr) auto auto;
    }
}

#article {
     grid-cell: "a";
}

grid template for landscape

Обратите внимание, что привязка самой статьи к именованной ячейке при этом не меняется. (Ручки уже чешутся начать использовать, однако, ждем, когда это все реализуют в браузерах.)

Заключение

Я постарался дать в статье обзор ключевых возможностей модуля CSS3 Grid Layout. Надеюсь, они вдохновляют вас не менее, чем меня. :)

Напомню, что модуль продолжает развиваться, редакторы собирают отзывы от внешнего сообщества и внутри рабочей группы CSS. На действующую реализацию основного функционала можно посмотреть в Internet Explorer 10. Несколько интересных примеров можно найти на ietestdrive.com: The Grid System и Hands On: CSS3 Grid Layout:

Hands On: CSS3 Grid Layout

Также еще раз отмечу, что возможности Grid Layout, уже поддерживаемые движком IE10, также можно использовать при разработке приложений в стиле Metro на HTML/JS для Windows 8.