Адаптивный вертикальный ритм

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

Что такое вертикальный ритм?

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

Основой вертикального ритма является базовая линия, равная длине между двумя соседними линиями. Как правило, это стандартный интерлиньяж (свойство line-height, установленное для тела страницы). Поля, отступы, границы и высоты элементов на странице, включая текстовые, рассчитываются исходя из длины базовой линии, т. е. указанного интерлиньяжа, и могут составлять как эквивалент, так и, например, полтора, половину или четверть её размера в зависимости от необходимости в визуальном пустом пространстве между элементами.

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

Отображение вертикальной сетки

Наиболее простой способ наложения на страницу вертикальной сетки — это фоновое изображение, заданное для корневого элемента страницы <html> исключительно средствами CSS. Для этих целей идеально подходит градиент высотой в 1px, повторяющийся по вертикали через промежуток, равный высоте базовой линии. При необходимости в визуальном упрощении сетку можно разбить и на меньшее деление — половину базовой линии. Чтобы избежать конфликта фонов с <body>, к коревому элементу следует добавить псевдоэлемент. Например, для базовой линии в 24px и дополнительным делением в 12px потребуется следующий CSS:

:root {
  position: relative;
  min-height: 100%;
}

:root::before {
  content: "";
  width: 100%;
  height: 100%;
  background: linear-gradient(to top, #f00, transparent 1px) 0 0 / 100% 24px, /* основная сетка */
              transparent /* цвет фона */
              linear-gradient(to top, #f99, transparent 1px) 0 0 / 100% 12px; /* дополнительная сетка */
  position: absolute;
  z-index: 99;
  pointer-events: none;
}

Выбор корневого элемента для построения сетки также обусловлен тем, что для тела страницы на сайте уже могут существовать другие псевдоэлементы ::before или ::after. Проще говоря, это сводит к минимуму потенциальный конфликт стилей.

Оптимальный шаг вертикального ритма

Для определения оптимального значения базовой линии можно использовать стандартный размер шрифта основного контента (параграфов). Например, для ширины экрана более 1305px тегам <p> на данном сайте установлен font-size: 21px, которому соответствует line-height: 34px. Следовательно, 34px будет базовой линией, и все занимаемые элементами пространства по вертикали (которые включют в себя их содержимое, поля, границы и отступы), по возможности должны быть кратными этому числу. Как правило, значение свойства line-height устанавливается в 1.5 раза больше, чем размер шрифта, однако автор отдает предпочтение так называемому округленному золотому числу, в результате чего и появляется искомая высота базовой линии:

21 × 1.618 34

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

CSS-свойства и атрибуты элементов, важные для ритма

Применяя элементарные математические расчёты, необходимо привести в соответствие с линиями сетки занимаемое каждым элементом вертикальное пространство — высоту блоковой (боксовой) модели. Высота блока (бокса) вычисляется путем комбинирования соответствующих элементу CSS-свойств и указанных для него атрибутов среди четырех областей: внешних отступов, рамки, внутренних полей и содержимого. Таким образом, в число важных для вертикального ритма свойств и атрибутов как минимум входит:

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

Выравнивание по отличным от ритма значениям

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

Предположим, если шаг вертикального ритма равен 31px, и необходимо указать половину или четверть его размера, вполне допустимо использовать дробные значения в 15.5px или 7.75px. Применение относительных единиц em или rem в данном случае визуально упрощает понимание кода и расчёт, но в итоге получаемые абсолютные пиксели, разумеется, остаются таким же «некрасивыми». Феномен «пол-пикселя» обусловлен тем, что CSS-пиксели могут не совпадать с экранными, однако они вполне корректно обрабатываются большинством браузеров. Видимые несоответствия между указанным значением и полученным результатом могут возникнуть в Internet Explorer вплоть до 11-ой версии и Opera 12 (Presto), так как округление в данных браузерах происходит лишь до двух знаков после запятой, поэтому значения вроде 1,23456em или 12,3456% превращаются в неточные абсолютные пиксельные величины, что может сместить элементы относительно вертикальной сетки. Чтобы избежать подобных недоразумений, там, где это технически возможно, рекомендуется указывать для длинных относительных величин пиксельный fallback.

Интерлиньяж, разделенный на половину Интерлиньяж, разделенный на половину
Базовая линия равна 31px. Расстояние между заголовком и параграфом равняется двум интерлиньяжам, а между параграфом и кнопкой — половине интерлиньяжа.

Таким образом, импровизировать с размерами ритма довольно просто, главное — по возможности сохранять кратность вычисляемого вертикального пространства размеру базовой линии (её половине или иногда четверти). Однако в некоторых случаях от этого принципа стоит отступить.

Несоответствие базовой линии размеру шрифта

Отдельно стоит осветить проблему, когда размер базовой линии не подходит для заголовков и текстовых блоков с большим размеров шрифта, которые растягивается на несколько строк. Например, базовая линия в 26px, установленная в качестве интерлиньяжа, может оказаться слишком узкой, а двойная — line-height: 52px — слишком широкой для многострочного заголовка с размером шрифта в 26px. При этом не обязательно, чтобы заголовок изначально содержал несколько строк: аналогичным образом он может автоматически разбиться на строки при меньшем разрешении экрана.

Проблема применения вертикального ритма к заголовкам H1 и H2 Проблема применения вертикального ритма к заголовкам H1 и H2
В обоих случаях кратный сетке межстрочный интервал не подходит для многострочных заголовков и любого другого текста в несколько строк с большим размером шрифта.

Выбрать оптимальный межстрочный интервал заголовка и одновременно выровнять его относительно сетки можно двумя способами: увеличить тождественное базовой линии значение line-height ещё на половину (аналогично предыдущему примеру) или сделать смещение более «некрасивым» способом — посредством margin или padding, установив ширину линии на любое визуально подходящее значение и добавив поле или отступ для компенсации оставшегося пустого пространства до следующей линии сетки. С точки зрения чистоты CSS более приемлем первый способ, но иногда в зависимости от метрик шрифта лучшим вариантом будет второй.

Выравнивание содержимого нескольких колонок

Другой частный случай, когда вертикальный ритм приносит визуальный дисбаланс, нежели упорядоченность, — это верстка нескольких колонок, текст в которых может обладать разной высотой шрифта. Как известно, в CSS символы (т.е. область содержимого шрифта, которая определяется его метриками) располагаются примерно в вертикальном центре высоты линии, поэтому горизонтальное сопоставление двух текстовых элементов с разными значениями font-size при общем line-height приведет к тому, что основания их букв не будут соответствовать друг другу. Следующий пример наглядно демонстрирует такое несоответствие:

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

Может показаться, что сдвиг на пару пикселей незначителен и не влияет на визуальное восприятие контента. Однако с увеличением размера шрифта растёт и видимое расхождение между основаниями текста в колонках, поэтому потребуется выравнивание. Взамен классического способа через margin и padding предлагается использовать смещение посредством позиционирования относительно исходного места (position: relative) или трансформации (transform: translateY()). Величина смещения зависит от метрик конкретного шрифта, следовательно, если браузер пользователя по каким-то причинам не может отобразить текущий шрифт и заменяет его другим, смещение не даст ожидаемого эффекта.

Вертикальный ритм и адаптивный дизайн

Вёрстка макета под меньшие разрешения экрана также требует правильного расположения элементов на вертикальной сетке, притом нежелательно применять один и тот же размер базовой линии, например, для экранов шириной 1366px и 320px. Именно поэтому в процессе создания адаптивного дизайна может возникнуть ситуация, когда потребуется изменить значение базовой линии, а вместе с тем значения полей, отступов, границ и высот всех элементов для их соответствия новому вертикальному ритму.

Следуя логике построения вертикального ритма исходя из размера шрифта основного текста, высоту базовой линии следует менять каждый раз, когда меняется размер этого самого текста. Если для пары @media-запросов переписать указанные CSS-правила не представит труда, то для большого количества так называемых breakpoint со своими уникальными базовыми линиями подгонка всех элементов под новую кратную шагу сетки высоту становится кропотливым занятием даже с использованием препроцессоров (конечно, если не полагаться на готовые инструменты наподобие Sassline).

Например, интерлиньяж может поменяться пять раз с 26px до 19px. Чтобы подогнать элементы под новую вертикальную сетку с аналогичными шагами, понадобится указать новые значения margin, padding, height и т. д. для каждого элемента в каждом breakpoint. Однако, лишнего CSS можно избежать, сделав вертикальный ритм адаптивным (надо сказать, что используемое в статье понятие «адаптивный вертикальный ритм» является условным: фактически, адаптивной является не сетка, а высоты блоковых моделей элементов, которые будут автоматически рассчитываться в большинстве случаев кратными базовой линии; в свою очередь размер базовой линии, как было сказано выше, напрямую зависит от размера «стандартного» текста сайта).

Основа адаптивного вертикального ритма

Для визуального и математического упрощения, а главное — для привязки к размеру базовой линии цифровых значений важных для ритма CSS-свойств нельзя обойтись без относительных единиц измерения, поэтому реализовать указанную связь возможно благодаря rem. Алгоритм довольно прост:

  1. Определить стандартный размер шрифта и высоту его строки (font-size и line-height), которые будут использоваться на сайте в рамках подачи основного контента. Интерлиньяж можно вычислить, опираясь на золотое сечение. Подробную процедуру выбора этих параметров описывает туториал по идеальной веб-типографике.
  2. Добавить равное высоте базовой линии свойство font-size для корневого элемента — <html>, таким образом установив значение, которому будет соответствовать 1rem. Сделать это можно как в абсолютных (пиксели), так и в относительных (процент, em, rem и др.) единицах:
    1. В основе относительного значения лежит стандартный размер шрифта в браузере — 16px, который, соответственно, может быть изменён по желанию пользователя. Следовательно, относительное значение сделает величину rem (и по факту размер базовой линии) зависимой от пользовательских предпочтений, что может привести к автоматическому масштабированию страницы.
    2. Абсолютное значение в пикселях, напротив, напрямую привязывает 1rem к окончательному результату. Масштаб страниц в таком случае может быть изменён пользователем лишь принудительно в процессе просмотра, а не заранее исходя из преднастройки браузера.
  3. Продублировать свойство font-size для тела страницы — <body>, указав выбранный размер шрифта основного текста сайта (в удобных для работы единицах) и высоту строки — line-height: 1rem. Теперь страница будет иметь тождественный базовой линии интерлиньяж и стандартный размер текста.
  4. По возможности использовать rem для всех цифровых значений CSS-свойств: margin, padding, height и др.

В результате должен получиться CSS, аналогичный следующему образцу:

:root {
  font-size: 24px; /* базовая линия (1.5em, 1.5rem, 150% относительно 16px – стандартного размера шрифта браузера) */
}

body {
  font-size: 15px; /* размер шрифта основного контента (.625em, .625rem, 62.5% относительно родительского элемента) */
  line-height: 1rem; /* интерлиньяж равен базовой линии (24px) */
}

p, ul, ol {
  margin-bottom: 1rem; /* 24px – одна базовая линия */
}

.example {
  margin-bottom: .5rem; /* 12px – половина базовой линии */
  font-size: 1.25rem; /* 30px */
  padding-top: 2.5rem; /* 60px – две с половиной базовой линии */
}

Способ наложения визуальной вертикальной сетки, описанный в начале статьи, так же может быть модифицирован за счет относительных единиц измерения. Рекомендуется воспользоваться em, так как в определенных ситуациях браузер не успевает корректно вычислить величину rem для background-size псевдоэлементов <html>, и она так и остаётся равной базовым 16px, несмотря на иное значение, установленное для корневого элемента.

:root::before {
  background: linear-gradient(to top, #f00, transparent 1px) 0 0 / 100% 1em, /* основная сетка */
              transparent /* цвет фона */
              linear-gradient(to top, #f99, transparent 1px) 0 0 / 100% .5em; /* дополнительная сетка */
}

Изменение базовой линии через @media

Для изменения размера базовой линии достаточно указать для корневого элемента новое значение font-size. Соответственно, прочие величины, указанные или зависимые от корневого 1rem, браузер рассчитает автоматически, поэтому необходимость в повторном подстраивании элементов под вертикальный ритм отпадает. Таким образом можно масштабировать макет, внося при необходимости лишь «косметические» изменения в текущий @media-запрос.

@media (max-width: 640px) {
  :root {
    font-size: 23px; /* или 1.4375em, 1.4375rem, 143.75% от стандартных 16px  */
  }
  body {
    font-size: 14px; /* или .60869em, .60869rem, 60.86956% от новой базовой линии */
  }
}

@media (max-width: 480px) {
  :root {
    font-size: 19px; /* 1.1875em, 118.75% */
  }
  body {
    font-size: 12px; /* .63157em, .63157rem, 63.15789% */
  }
}

Но, к сожалению, не все элементы могут так легко менять свою высоту и автоматически подстраиваться под ритм.

Изображения и вертикальный ритм

В процессе верстки страницы по вертикальной сетке рано или поздно придётся столкнутся с тем, что изображения «ломают» ритм, так как их высота далеко не всегда может быть кратной значению (половине/четверти) базовой линии. Даже если удается подстроить картинки под сетку путем пропорционального манипулирования их габаритами (width и height в пикселях), изменение размера базовой линии посредством @media вновь приводит к нежелательному смещению элементов. Существует как минимум три варианта решения этой проблемы.

Решения на JavaScript

Первая страница результатов в Google по незамысловатому запросу «vertical rhythm images» предложит верстальщику насколько вариантов vanilla-js скриптов и плагинов JQuery примерно схожего принципа действия: пропорциональный resize с учетом высоты базовой линии. Однако все они требуют небольшой доработки в части вызова, если вертикальный ритм на странице создан при помощи rem. Например, библиотека «baseline-element» инициализацируется следующим образом:

var baseline = 24, element = document.getElementById('my-responsive-image');
window.baselineElement(element, baseline);

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

var baseline = function() { return parseFloat(getComputedStyle(document.documentElement).fontSize); }

Следует также предусмотреть, что пользователь способен изменить ширину окна браузера или, будучи с мобильного устройства, режим просмотра (landscape/portrait), поэтому целесообразно добавить соответствующие window.eventListener и при необходимости запускать функцию вновь:

var element = document.getElementById('img'), baseline = function() {
  return parseFloat(getComputedStyle(document.documentElement).fontSize);
};
("load resize orientationchange".split(" ")).forEach(function(e) {
  window.addEventListener(e,function() {
    window.baselineElement(element,baseline());
  });
});

Непрактичный CSS

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

  1. Зная размер базовой линии и ширину родительского элемента, габариты изображения можно пропорционально изменять через свойства width и height для каждого breakpoint, тем самым масштабируя картинку. Существенный недостаток в том, что при большом количестве изображений с разными соотношениями ширины и высоты расчет оптимального масштабирования будет необходим для каждого отдельного случая.
  2. Изображение помещается в блок заданной в rem высоты и подстраивается под него посредством object-fit: cover, но подобное обрамление не всегда уместно с точки зрения визуальной привлекательности.

Таблицы и вертикальный ритм

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

@media (max-width: 480px) {
  table {
    display: block;
    overflow-x: auto;
  }
}

Высота таблицы также может измениться, если для её ячеек установлены границы. Чтобы компенсировать смещение ритма из-за верхних и нижних границ ячеек (border-top-width и border-bottom-width), можно рассчитывать высоту ячейки через calc() (если она фиксирована) или имитировать верхнюю/нижнюю границы через градиент или псевдоэлементы:

td.example1 { /* вычитаем лишние пиксели от фиксированной высоты */
  border: 1px #000 solid;
  height: calc(Xrem - 2px); /* или line-height при необходимости */
}

td.example2 { /* имитируем border-bottom: 1px #000 solid; через градиент */
  background: linear-gradient(to right, #000, #000) repeat-x 0 100% / 100% 1px;
}

td.exapmle3 {
  position: relative;
}

td.example3::after { /* имитируем border-bottom: 1px #000 solid; через псеводэлемент */
  content: "";
  display: block;
  position: absolute;
  height: 1px;
  background: #000;
  bottom: 0;
  left: 0;
  right: 0;
}

Другой эффективный способ имитировать все четыре границы элемента, не влияя на габариты его блоковой модели, — это свойства box-shadow и outline:

.example1 {
  box-shadow: 0 0 0 1px #000 inset;
}

.example2 {
  outline: 1px solid #000;
  outline-offset: -1px;
}

Пример адаптивного вертикального ритма

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

В заключение

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