Адаптивная веб-типографика

Как выбрать оптимальный размер шрифта, установить интерлиньяж и определить допустимую ширину линии, чтобы улучшить восприятие содержимого сайта и сделать текст визуально гармоничным? И чем в этом может помочь золотое сечение? Крис Пирсон в своей статье «The Ultimate Guide to Readable Web Typography» делится математическими секретами удобочитаемости текста, а на основе данного перевода вниманию читателя представляется туториал о том, как адаптивная веб-типографика, построенная на пропорциях золотого сечения, может быть реализована средствами CSS и JS.

Предисловие

Статья Криса Пирсона «Тайная симфония: подробное руководство по удобочитаемой типографике» написана в далёком 2011 году и уже была переведена на русский язык, поэтому целесообразно выделить лишь основные её тезисы и понятия, важные с теоретической точки зрения. На базе этих материалов автором рассматривается практический аспект — создание адаптивной веб-типографики, при которой текстовый контент сайта был бы максимально удобочитаемым на всех разрешениях экрана. Описанные принципы не претендуют на универсальность, однако на примере публикаций зарубежных блогеров можно убедиться в том, как типографика, в основе которой лежит золотое сечение, способствует росту читателей.

Три ключевых типографических измерения

Каждый блок текста на сайте имеет три главных для типографики измерения, два из которых вертикальные по своей природе — размер шрифта (свойство font-size, если выражаться языком CSS) и высота строки (line-height), и одно горизонтальное — длина строки (как такового аналога «line-width» в CSS нет, но есть обычное width).

Веб-типографика: высота строки, ширина линии и размер шрифта Веб-типографика: высота строки, ширина линии и размер шрифта
Ширина линии напрямую зависит от размера блока, в которую помещается текст, а также от расставленных в нём переносов на следующую строку. Высота линии при отсутствии прочих свойств автоматически рассчитывается на основе метрик шрифта.

Очевидно, что между font-size и line-height существует пропорциональная зависимость: чем больший размер текста используется, тем большей должна быть высота его строки, иначе нарушаются геометрические пропорции параграфа и снижается удобочитаемость.

На удобство восприятия текстового блока аналогичным образом влияет и длина строк: чем она больше при неизменном line-height, тем большее время требуется человеческому глазу, чтобы перевести взгляд от конца одной строки до начала следующей. Иными словами, интерлиньяж в тексте с большой длиной строки должен позволять глазам читателя легко переключаться на следующую строку после прочтения текущей.

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

Золотое сечение в типографике

Для того, чтобы соотнести font-size (F) и line-height (L), используется коэффициент подобия, и значение высоты строки будет наиболее оптимальным, когда этот коэффициент будет равен золотому числу. В статье про адаптивный вертикальный ритм уже рассматривалось указанная пропорция:

F x 1.618 L

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

W = L 2

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

Веб-типографика и особенности округления

Главная проблема при использовании данных уравнений заключается в слишком большой точности результата, который впоследствии необходимо округлять. Однако простое округление до ближайшего целого может привести к нарушению типографических пропорций. Например, согласно формулам, для шрифта размером 18px идеальной высотой строки будет 29.124px, а длиной строки — 848.207376px:

( 18 x 1.618 ) 2 848.207376

В результате получается соответствующий CSS:

p {
  font-size: 18px;
  line-height: 29.124px;
  width: 848.207376px;
}

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

  1. Значение интерлиньяжа в виде десятичной дроби серьёзно затруднит вёрстку страницы по вертикальной сетке.
  2. Аналогичным образом вычисления по горизонтальной оси приобретут визуально некрасивый формат, если потребуется, например, использовать указанные величины в запросах @media или задействовать их для дальнейших расчётов — width: calc(100% - 848.207376px) и т. п.
  3. Далеко не все браузеры работают с дробными пикселями максимально точно: например, Internet Explorer 11 будет округлять их до двух цифр после запятой, и конечные пропорции уже не будут «золотыми».

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

Следовательно, округление line-height: 29.124px до 29px потребует уменьшения длины строки, в то время как округление line-height: 25.88854px до 26px — увеличения.

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

«Золотые» типографические пропорции

Под шириной контента подразумевается конечная ширина текстового блока, исключающая поля, отступы и границы. Проще говоря, это область содержимого элемента. Именно для неё рассчитываются «золотые» соотношения размера шрифта и его интерлиньяжа.

К размеру шрифта кроме основных параграфов предлагаются размеры для вспомогательного текста меньшего размера <small>, подзаголовков <h3>, <h2> и заголовков <h1>, аналогичным образом вычисленные калькулятором.

Высоты строк для соответствующих размеров шрифта рассчитаны на базе интерлиньяжа основного текста. Для вспомогательного текста используется множитель 0.75, для заголовков <h2> — 1.25, для заголовков <h1> — 2, в то время как интерлиньяж обычного текста и подзаголовков <h3> остается без изменения.

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

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

Данные таблицы приведены в пикселях, через дробь указаны пары font-size и line-height.

Ширина контента Небольшой текст Основной текст <h3> <h2> <h1>
1211−1325 17 / 28 22 / 36 28 / 36 36 / 45 58 / 72
1101−1210 17 / 25.5 21 / 34 27 / 34 34 / 42.5 55 / 68
996−1110 16 / 24 20 / 32 25 / 32 32 / 40 52 / 64
897−995 15 / 23.25 19 / 31 24 / 31 31 / 38.5 50 / 62
802−896 14 / 21.75 18 / 29 23 / 29 29 / 36.25 47 / 58
713−801 13 / 21 17 / 28 22 / 28 28 / 35 45 / 56
629−712 13 / 19.5 16 / 26 20 / 26 26 / 32.5 42 / 52
551−628 12 / 18 15 / 24 19 / 24 24 / 30 39 / 48
478−550 11 / 17.25 14 / 23 18 / 23 23 / 28.75 37 / 46
410−477 10 / 15.75 13 / 21 17 / 21 21 / 26.25 34 / 42
347−409 9 / 14.25 12 / 19 15 / 19 19 / 23.75 31 / 38
289−346 9 / 13.5 11 / 18 14 / 18 18 / 22.5 29 / 36
237−288 8 / 12 10 / 16 13 / 16 16 / 20 26 / 32

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

Создание адаптивной веб-типографики

Чтобы обеспечить масштабирование типографики и сохранить «золотые» пропорции на любом разрешении экрана следует сверять ширину текстового контента с его оптимальной, «золотой» шириной и при необходимости изменять соответствующие типографические параметры. Если макет документа имеет всего одну колонку фиксированной ширины (например, как на данном сайте), типографические изменения можно реализовать через @media-запросы, скомпенсировав лишь поля и отступы от контента. Напротив, при множестве элементов разной ширины, расположенных по горизонтали, невозможно автоматически сравнить ширину каждого блока с её оптимальным эквивалентом. Возникает вопрос, каким образом можно вычислять ширину отдельных текстовых элементов и менять необходимый CSS, если запросы @media привязываются к ширине окна браузера и не поддерживают calc()? В данном случае не обойтись без помощи JS.

Запросы @media… к контейнеру элемента

Наиболее простым и удобным решением для создания адаптивной веб-типографики с «золотыми» пропорциями без редактирования @media будет библиотека «Container Queries Prolyfill», которая позволяет применить стиль к элементу в зависимости от того, какими свойствами обладает его контейнер. Использование библиотеки существенно упрощается благодаря готовому миксину SCSS, который можно найти в репозитории. Для начала работы необходимо подключить библиотеку к каждой странице сразу перед закрывающим тегом </body>:

<script src="//unpkg.com/cq-prolyfill@0.4.0/cq-prolyfill.min.js"></script> <!-- 5.64 kb gzipped -->

Например, следующий SCSS устанавливает золотые пропорции line-height и font-size, если ширина родительского элемента для параграфа <p> составляет как минимум 713px:

 p { // компилируется в валидный CSS селектор: p.\:container\(width\>\=713px\) { ... }
  @include cq-prolyfill("width >= 713px") {
    font-size: 17px;
    line-height: 28px;
  }
}

Подобные запросы к родительскому контейнеру невероятно удобны, если приходится иметь дело со сложным макетом. Таким образом можно установить желаемые пропорции для текстового элемента, просто добавив необходимое количество синтаксических правил с указанием «границ» оптимальной ширины контента. Автоматизировать этот процесс поможет цикл SCSS, для компилирования которого для начала следует задекларировать несколько переменных и функцию вычисления квадратного корня sqrt(), требуемых для дальнейших расчётов. Разумеется, вычислить некоторые значения можно более «автоматизированно», но для упрощения кода они приведены как окончательный результат:

// вычисление квадратного корня
@function sqrt($r) {
  $x0: 1; $x1: $x0; @for $i from 1 through 10 { $x1: $x0 - ($x0 * $x0 - abs($r)) / (2 * $x0); $x0: $x1; }
  @return $x1;
}

// золотое число
$golden-ratio: (1 + sqrt(5)) / 2;

// оптимальные высоты линий (минимальная - 16) для соответствующей максимальной ширины контента (px) 
$ratio-pairs: ( 0: 16, 346: 18, 409: 19, 477: 21, 550: 23, 628: 24, 712: 26, 801: 28, 896: 29, 995: 31, 1110: 32, 1210: 34, 1325: 36 );

// максимальная ширина основного текстового элемента (px)
$wrap-max: 720;

// множители line-height
$line-heights-factors: ( "h1": 2, "h2": 1.25, "h3": 1, "small": .75, "default": 1);

// селектор для будущего блока с золотыми пропорциями текста
$block-selector: ".golden";

Расчет необходимых font-size и line-height для элементов потребует уже небольшой функции (стоит заранее отметить, что представленный далее код для большей наглядности носит тривиальный весьма характер). Размер шрифта каждого элемента считается в соответствии с формулой калькулятора золотого сечения. В число данных элементов входят заголовки <h1>, <h2>, <h3> и вспомогательный текст <small>. Как можно заметить, оптимальный размер шрифта для <h2> считать не требуется, т.к. он всегда будет равен высоте линии для соответствующей ширины контента. В функции используется три аргумента: $line-height — оптимальная высота линии из массива $ratio-pairs, $type — элемент, для которого рассчитывается размер шрифта на основе этой линии, а также $prop — указание на то, для какого свойства возвращается вычисляемое значение.

@function typo($line-height,$type,$prop) {
  $font-size: round($line-height / $golden-ratio); // "золотой" размер шрифта основного текста
  $result: $font-size;
  @if $type == "h1" { // (оптимальный размер шрифта) x (квадрат золотого числа)
    $result: ($font-size * $golden-ratio * $golden-ratio);
  }
  @if $type == "h2" { // оптимальная ширина линии
    $result: $line-height;
  }
  @if $type == "h3" { // (оптимальный размер шрифта) x (квадратный корень из золотого числа)
    $result: ($font-size * sqrt($golden-ratio));
  }
  @if $type == "small" { // ((оптимальный размера шрифта) x (золотое число)) / 2
    $result: ($font-size * $golden-ratio) / 2;
  }
  @if $prop == "font-size" { // результат для font-size
    @if $type == "default" {
      @return #{$result}px;
    } @else {
      @return #{round($result) / $font-size}em;
    }
  } @else { // результат для line-height
     @return #{(map_get($line-heights-factors,$type) * $line-height) / round($result)}em;
  }
}

Наконец, осталось вывести данные результаты для каждой возможной ширины блока, который будет обозначаться выбранным классом $block-selector. Чтобы избежать лишних запросов к элементу, максимум оптимальной ширины блока ($optimal-max-width) будет сравниваться с максимальной шириной верстки ($wrap-max).

@each $optimal-max-width, $line-height in $ratio-pairs {
  @if $optimal-max-width < $wrap-max {
    #{$block-selector} {
      @include cq-prolyfill("width >= #{$optimal-min-width}px") {
        font-size: typo($line-height,"default","font-size");
        line-height: typo($line-height,"default","line-height");
        @each $el in h1, h2, h3, small {
          #{$el} {
            font-size: typo($line-height,"#{$el}","font-size");
            line-height: typo($line-height,"#{$el}","line-height");
          }
        }
      }
    }
  }
}

Косметические исправления

Изменение CSS посредством JS, если поместить скрипт в конец документа, как того требует оптимизация, а не в секцию <head>, несёт в себе видимое глазу изменение габаритов блока и параметов его шрифта при загрузке страницы. Это «мерцание» можно устранить, если добавить к искомому блоку свойство min-height, которое придется вычислять вручную (вертикальный ритм облегчит этот процесс) и устанавливать для каждого запроса к контейнеру элемента; а также создать эффект плавной загрузки — transition при изменении шрифта. Варианты переходов зависят от фантазии разработчика.

#{$block-selector} {
  * { transition: font-size .1s ease-in, line-height .1s ease-in; }
}

Пример адаптивной «золотой» веб-типографики

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

В заключение

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