Как настроить высоту строки в CSS

Межстрочный интервал в CSS – наверное, одно из самых непонятных, но широко используемых свойств. Дизайнеры и разработчики, услышав слово «межстрочный интервал» могут подумать о таком интересном понятии, как интерлиньяж. Раньше так называли процесс вставки свинцовых полос между строками текста.

Несмотря на то, что интерлиньяж и свойство line-height довольно похожи, у них есть некоторые важные различия. Чтобы понять эти различия, нам нужно для начала немного углубиться в типографику.

Краткий гид по типографическим терминам

В традиционном дизайне шрифта строка состоит из нескольких элементов:

  • Базовая линия (Baseline): Это воображаемая линия, на которой располагается текст. Когда вы пишете в тетради в линейку, строка на которой вы пишете – и есть базовая линия.
  • Нижний выносной элемент (Descender): Это линия, которая находится чуть ниже базовой. В ней оказываются части букв, – например, строчных р, у, ф – которые выходят за пределы базовой линии.
  • X-высота (X-height): Это (кто бы мог подумать) высота обычной маленькой буквы x в строке текста. Как правило, эту высоту имеют и другие строчные буквы, хотя некоторые из них выходят за пределы x-высоты. В любом случае, x-высота является общепринятой высотой строчных букв.
  • Высота прописных (Cap-height): Это высота большинства заглавных букв на линии текста.
  • Верхний выносной элемент (Ascender): Это линия, которая обычно находится чуть выше линии роста прописных. Там оказываются части букв, которые выходят за пределы высоты прописных.
Краткий гид по типографическим терминам

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

Интерлиньяж обозначает установленное в настройках шрифта расстояние между двумя базовыми линиями.

Краткий гид по типографическим терминам - 2

Веб-разработчик может подумать: «Ладно, интерлиньяж – это line-height, давайте дальше». Но, несмотря на то, что эти два понятия связаны, они различаются в некоторых важных аспектах.

Давайте возьмем пустой документ и применим к нему классический сброс стилей.

* {
  margin: 0;
  padding: 0;
  }

Так убирается внешний и внутренний отступ для всех элементов.

Также мы используем шрифт Lato из Google Fonts для свойства font-family.

Нам нужно добавить содержимое, поэтому давайте создадим тэг <h1> с текстом и дадим line-height какое-нибудь безумно большое значение, например, – 300px. В результате получаем одну строку текста с невероятно большим отступом сверху и снизу.

Когда браузер сталкивается со свойством line-height, он помещает строку текста в середину «линейного блока», высота которого соответствует высоте элемента. Вместо интерлиньяжа для шрифта, мы получаем что-то вроде свойства padding с каждой стороны строки.

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

Но, что ещё более удивительно, если придать свойствам line-height (высота строки) и font-size (размер шрифта) одинаковую величину, то всё равно останется лишнее пространство над и под текстом. Мы можем это увидеть, изменив цвет фона элементов.

Это происходит потому что, хотя размер шрифта составляет 32px, реальный размер текста будет немного меньше этого значения из-за наличия отступов.

Делаем так, чтобы CSS принимал высоту строки за интерлиньяж

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

Можно научить CSS делать интерлиньяж, если приложить немного усилий. Майкл Таранто создал инструмент Basekick, который применяется для достижения именно этой цели. Он придает свойству margin псевдоэлемента ::before отрицательное значение, а к самому элементу применяет функцию translateY. В результате этого мы получаем строку текста, которая не имеет никаких лишних отступов.

Последнюю версию формулы инструмента Basekick можно найти в исходном кодедизайнерской системы Braid от компании SEEK. В примере, приведенном ниже, мы используем примесь Sass, которая сделает всю тяжелую работу за нас. Такую же формулу можно создать с помощью примесей в JavaScript, Less, PostCSS или в чем-то другом, имеющем подобные математические функции.

@function calculateTypeOffset($lh, $fontSize, $descenderHeightScale) {
  $lineHeightScale: $lh / $fontSize;
  @return ($lineHeightScale - 1) / 2 + $descenderHeightScale;
}


@mixin basekick($typeSizeModifier, $baseFontSize, $descenderHeightScale, $typeRowSpan, $gridRowHeight, $capHeight) {
  $fontSize: $typeSizeModifier * $baseFontSize;
  $lineHeight: $typeRowSpan * $gridRowHeight;
  $typeOffset: calculateTypeOffset($lineHeight, $fontSize, $descenderHeightScale);
  $topSpace: $lineHeight - $capHeight * $fontSize;
  $heightCorrection: 0;
  
  @if $topSpace > $gridRowHeight {
    $heightCorrection: $topSpace - ($topSpace % $gridRowHeight);
  }
  
  $preventCollapse: 1;
  
  font-size: #{$fontSize}px;
  line-height: #{$lineHeight}px;
  transform: translateY(#{$typeOffset}em);
  padding-top: $preventCollapse;


  &::before {
    content: "";
    margin-top: #{-($heightCorrection + $preventCollapse)}px;
    display: block;
    height: 0;
  }
}

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

  • $baseFontSize: Это стандартный размер шрифта в нашей системе, вокруг которого будет выстраиваться всё остальное. Мы установим 16px как значение по умолчанию.
  • $typeSizeModifier: Это коэффициент, применяемый к свойству font-size, который устанавливает правило размера шрифта. Например, если применить значение 2 к нашему стандартному размеру шрифта в 16px, то значение font-size будет равняться 32px.
  • $descenderHeightScale: Это высота нижнего выносного элемента, выраженная в пропорции. Для шрифта Lato она составляет приблизительно 0,11.
  • $capHeight: Это высота верхнего выносного элемента, выраженная в пропорции. Для шрифта Lato она равна примерно 0,75.
  • $gridRowHeight: Системы вёрстки обычно опираются на вертикальный ритм, установленный по умолчанию, чтобы сделать процесс чтения приятным и равномерным. Например, все элементы страницы могут быть равноудалены друг от друга на величины, кратные четырём или пяти пикселям. Мы будем использовать значение 4, потому что оно хорошо делится на наш $baseFontSize, составляющий 16px.
  • $typeRowSpan: Как и $typeSizeModifier, эта переменная служит коэффициентом для высоты ряда сетки и устанавливает правило высоты строки. Если по умолчанию величина ряда сетки равна 4, а в $typeRowSpan мы установим 8, то высота строки составит 32px.

Теперь мы можем вставить эти цифры в формулу Basekick, приведенную выше (с помощью функций и примесей SCSS). И это даст нам следующий результат:

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

Дорабатываем наш код

Вместо того чтобы запихивать весь наш код в одну SCSS примесь, давайте организуем его немного лучше. Если мы рассмотрим всё с точки зрения системы, то заметим, что существует три вида переменных, с которыми мы работаем:

Тип переменнойОписаниеПеременные @mixin (примеси)
Переменные системного уровняЭти значения относятся к свойствам дизайнерской системы, с которыми мы работаем.$baseFontSize
$gridRowHeight
Переменные шрифтаЭти значения относятся к шрифту, который мы используем. Возможно, придется действовать методом проб и ошибок, чтобы получить идеально подходящие цифры.$descenderHeightScale
$capHeight
Переменные правилаЭти значения относятся к правилу CSS, которое мы создаем.$typeSizeMultiplier
$typeRowSpan

Такой подход поможет нам лучше разобраться в нашей системе. Давайте рассмотрим каждую группу по очереди.

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

$baseFontSize: 16;
$gridRowHeight: 4;

@mixin basekick($typeSizeModifier, $typeRowSpan, $descenderHeightScale, $capHeight) {
  /* То же самое, что сверху */
   }

Мы также знаем, что переменные шрифта относятся только к заданному семейству шрифта. Это значит, что нам будет довольно просто создать примесь более высокого порядка, которая установит этим переменным постоянное значение:

@mixin Lato($typeSizeModifier, $typeRowSpan) {
  $latoDescenderHeightScale: 0.11;
  $latoCapHeight: 0.75;
  
  @include basekick($typeSizeModifier, $typeRowSpan, $latoDescenderHeightScale, $latoCapHeight);
  font-family: Lato;
  }

Теперь, на основе правила, мы без затруднений можем вызвать переменную Lato:

.heading--medium {
  @include Lato(2, 10);
  }

В итоге мы получаем правило, которое использует шрифт Lato с размером 32px и межстрочным интервалом 40px, включающим в себя все соответствующие повороты и отступы. Это позволяет нам создавать простые правила стилей и использовать постоянство сетки, привычное дизайнерам, работающим с такими инструментами, как Sketch и Figma.

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

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

Двигаемся к стандарту

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