Original article: Building Robust Layouts With Container Units

Когда мы исследуем сетку (grids) в веб-инспекторе, мы можем заметить что ширина колонок зависит от ширины родительских элементов. Эта статья поможет вам разобраться с тем как преодолеть эти ограничения, используя переменные CSS, и как начать создавать макеты с помощью контейнерных единиц измерения (container units).
Контейнерные единицы (container units) — это специализированный набор CSS переменных, который позволяет создавать сетку, макеты страниц и компоненты, используя колонки (columns) и зазоры (gutters). По функциональности они подобны возможности создавать макет (layout), существующей в программах для UI дизайнеров, когда настройка всего трех значений глобально устанавливает в макете колонки и зазоры, на которые можно ориентироваться при расчетах и измерениях.
Они также обеспечивают последовательные ширины везде в документе, независимо от глубины вложенности, ширины родительского элемента или их соседних (родственных) элементов. Таким образом вместо необходимого повторения набора .grid и .row для родительских элементов, контейнерные единицы измеряются от корневого элемента :root в документе. По аналогичному принципу работают единицы измерения rem.

В чем же отличие контейнерных единиц?
Сетки (grids), которые существуют в популярных фреймворках, таких как Bootstrap или Bulma, имеют общее фундаментальное ограничение: при создании колонок и зазоров они полагаются на относительные единицы, такие как проценты.
Такой подход обязует разработчиков использовать конкретную HTML структуру всякий раз, когда они используют относительные единицы измерения и требует вложенности родитель > потомок>, чтобы правильно расчитать ширину.
Свобода перемещения
Контейнерные единицы дают больше свободы в задании размера элементов, используя набор глобальных единиц измерения. Если вам нужно создать боковое меню (sidebar) с шириной в три колонки, то всё, что вам нужно, это следующее:
.sidebar {
width: calc(3 * var(--column-unit));
/* or columns(3) */
}
Ваш class="sidebar" может находиться где угодно внутри документа без конкретного родительского элемента или вложенности.

Измеряем три колонки и используем их для sidebar
Инструмент, которым пользуются и разработчки и дизайнеры
Цифры – это отличная область компромисса между дизайнерами и разработчиками. Они помогают преобразовать дизайн во фронтэнд шаблон.
Модульное масштабирование прекрасно не только потому, что помогает дизайнерам привнести гармонию в их типографию, но и потому что помогает разработчикам повторить это в виде простой системы. То же самое можно сказать и о Baseline Grids: великолепная, самодокументируемая система с минимальной конфигурацией (одна базовая цифра) и полной согласованностью
Контейнерные единицы настраиваются точно таким же образом, как дизайнеры выставляют настройки макета в Sketch.

Layout settings

Sketch gridlines
Возможность для дизайнеров и разработчиков создавать макеты, используя один и тот же инструмент, способствует увеличению продуктивности и формированию нового способа мышления для обеих сторон.
Разрабатываем при помощи контейнерных единиц
Сначала определяем размеры грида, используя три значения:
:root {
--grid-width: 960;
--grid-column-width: 60;
--grid-columns: 12;
}
Эти три значения определят ширину колонки соразмерно с сеткой (grid). В приведенном выше примере ширина колонки составляет 60 / 960, а зазоры рассчитываются автоматически исходя из оставшегося места.
В заключение устанавливаем ширину контейнера:
:root {
--container-width: 84vw;
}
Примечание: --container-width должно быть задано абсолютной величиной. Я рекомендую использовать viewport units или rem
Вы можете обновить--container-width в любом брейкпоинте (все контейнерные единицы измерения обновятся соответствующе).
@media (min-width: 800px) {
--container-width: 90vw;
}
@media (min-width: 1200px) {
--container-width: 85vw;
}
/* what about max-width? */
@media (min-width: 1400px) {
--container-width: 1200px;
}

Breakpoints
Теперь вы открыли для себя две очень мощные единицы измерения для создания макетов.
01 --column-unit
02 --gutter-unit
Column Spans: третье и финальное оружие
Более распространенным чем разработка с использованием либо колонок, либо зазров, является растягивание через них.

6 column span = 6 columns + 5 gutters
Column spans очень легко расчитать, но не так просто написать в виде красивого кода, поэтому я бы рекомендовал использование препроцессора:
.panel {
/* vanilla css */
width: calc(6 * var(--column-and-gutter-unit) - var(--gutter-unit));
/* pre-processor shortcut */
width: column-spans(6);
}
Конечно, вы можете использовать препроцессорные сокращения для каждой контейнерной единицы, которую я уже упоминал. Давайте рассмотрим всё на примере.
Разрабатываем компоненты с использованием контейнерных единиц
Давайте возьмем конкретный дизайн и разберем его:

В этом примере используются колонки, зазоры и column spans. Так как мы просто сохраняем значение, контейнерные единицы могут использоваться для других CSS свойств, таких как указание высоты или отступов.
.background-image {
width: column-spans(9);
padding-bottom: gutters(6);
/* 6 gutters taller than the foreground banner */
}
.foreground-banner {
width: column-spans(8);
padding: gutters(2);
}
.button {
height: gutters(3);
padding: gutters(1);
}
Код для скачивания
- CSS: container-units-css.css
- SCSS Functions: container-units-css-fuctions.scss
- Примеры и документация: Container Units
:root {
/* Grid proportions */
--grid-width: 960;
--grid-column-width: 60;
--grid-columns: 12;
/* Grid logic */
--grid-gutters: calc(var(--grid-columns) - 1);
/* Grid proportion logic */
--column-proportion: calc(var(--grid-column-width) / var(--grid-width));
--gutter-proportion: calc((1 - (var(--grid-columns) * var(--column-proportion))) / var(--grid-gutters));
/* Container Units */
--column-unit: calc(var(--column-proportion) * var(--container-width));
--gutter-unit: calc(var(--gutter-proportion) * var(--container-width));
--column-and-gutter-unit: calc(var(--column-unit) + var(--gutter-unit));
/* Container Width */
--container-width: 80vw;
}
@media (min-width: 1000px) {
:root {
--container-width: 90vw;
}
}
@media (min-width: 1400px) {
:root {
--container-width: 1300px;
}
}
Зачем использовать CSS переменные
"$variables, которые есть в препроцессорах, позволяли это делать на протяжении многих лет, зачем тогда нам нужны CSS переменные?"
Не совсем так... Хотя и можно использовать переменные, чтобы производить вычисления, невозможно избежать компиляции лишнего кода, когда одна из переменных обновляет свое значение.
Давайте рассмотрим следующий пример:
.grid {
$columns: 2;
$gutter: $columns * 1rem;
display: grid;
grid-template-columns: repeat($columns, 1fr);
grid-gap: $gutter;
@media (min-width: $medium) {
$columns: 3;
grid-template-columns: repeat($columns, 1fr);
grid-gap: $gutter;
}
@media (min-width: $large) {
$columns: 4;
grid-template-columns: repeat($columns, 1fr);
grid-gap: $gutter;
}
}
Этот пример иллюстрирует, как каждая ссылка на SASS/LESS переменную должна быть перекомпилирована, если переменная меняется; код дублируется снова и снова для каждого (экземпляра) случая.
Но CSS Variables полагаются на браузер для выполнения логики, доверяя ему совершать обновления за нас.
.grid {
--columns: 2;
--gutter: calc(var(--columns) * 1rem);
display: grid;
grid-template-columns: repeat(var(--columns), 1fr);
grid-gap: var(--gutter);
@media (min-width: $medium) {
--columns: 3;
}
@media (min-width: $large) {
--columns: 4;
}
}
Эта концепция помогает сформировать логику контейнерных единиц. Сохраняя логику в корне, каждый элемент в документе следит за обновлениями значений и реагирует соответствующе.
Попробуйте!