Back to all posts

Создание надежных макетов страниц при помощи контейнерных единиц измерения

Original article: Building Robust Layouts With Container Units

Coding

Когда мы исследуем сетку (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);
}
 

Код для скачивания

: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;
  }
}

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

Попробуйте!