Использование viewport-единиц в типографике

Перевод статьи Zell Liew «Viewport Unit Based Typography».

UPDATE 21.11.2016

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

Чтоб избежать данной проблемы нужно заменить проценты на rem-ы (чтоб размер высчитывался не относительно родителя, а относительно корневого элемента). То есть, если размер шрифта стоит по умолчанию (16px), а размер какого-то блока нужен, например, 24px, то в формуле указываем не 150%, а 1.5rem.

Что такое viewport-юниты?

Есть четыре разных viewport-юнитов на сегодня:

  • vw — процент от ширины вьюпорта
  • vh — процент от высоты вьюпорта
  • vmax — наибольшее значение из vw и vh
  • vmin — наименьшее значение из vw и vh

В данном случае, под вьюпортом подразумевается окно браузера. То есть 1vw — это 1% от ширины окна. 100vw, соответственно, — полная ширина окна.

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

Итак, если viewport-юниты автоматически пересчитываются, то это даёт нам возможность легко создать элемент, который всегда занимает четверть от размера окна.

.component {
  width: 50vw;
  height: 50vh;
  background: rgba(255, 0, 0, 0.25)
}

Итак, это был раздел «viewport для чайников». А теперь вперёд, к самой мякотке!

Использование viewport-единиц для адаптивной типографики

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

Давайте рассмотрим пример, чтобы понять о чём идёт речь.

Рассмотрим код, в котором мы меняем размер шрифта с 16px на 20px, при достижении размером окна 800px:

// Здесь и далее стили написаны на SCSS

html {
  font-size: 16px;

  @media (min-width: 800px) {
    font-size: 20px;
  }
}

В коде вы видите, что размер шрифта прыгает с 16px до 20px, когда размер окна становится больше 800px. Так мы делали долгое время и это нормально.

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

html {
  font-size: 16px;

  @media (min-width: 600px) {
    font-size: 18px;
  }

  @media (min-width: 800px) {
    font-size: 20px;
  }
}

Обычно, мы ограничиваемся тремя-четырьмя промежуточными медиа-запросами. Можно использовать больше, но это уже будет перебор.

Но что, если вы хотите получить тот же результат, но без указания лишних медиа-запросов и лишнего изменения размера шрифта?

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

Посмотрим на следующий код:

html { font-size: 3vw; }

Круто, да?

Но проблема в том, что viewport-единицы слишком чувствительны к изменению размера окна.

Если вы укажете размер шрифта 3vw, как в примере выше, то получите размер шрифта в 10px на экране шириной в 320px (мобильные) и 43px на экране шириной в 1440px (ноутбук). Это слишком маленькое и слишком большое значение.

Теперь перед нами стоит интересная задача — укрощение вьюпорта.

К счастью, есть простой способ решения данной проблемы: мы установим минимальное значение размера шрифта и будем его увеличивать с помощью небольшого viewport-значения, используя calc().

Вот как это выглядит:

html { font-size: calc(18px + 0.25vw) }

Круто, не так ли? Я узнал об этом способе от Mike Riethmuller в его статье «Precise control over responsive typography».

К сожалению, я понял, что этот код не работает в некоторых браузерах. Например, это не работает на Safari для Mac.

Исправление, на удивление, просто. Мы совмещаем использование процентов и vw и это позволяет данному коду работать в Safari:

html { font-size: calc(112.5% + 0.5vw) }

Тадам!

Отлично! Теперь мы реально можем перестать использовать em-ы, rem-ы и медиа-запросы? Я не могу дождаться момента, когда попробую это!

Следующая проблема, которую я должен был решить, — указание в viewport-единицах размеров заголовков (h1-h6).

Установка размера других элементов типографики в viewport-единицах

Первое что я попробовал — это создание заголовка h1, который в два раза больше основного шрифта. Оказалось, что не всё так просто

Я просто взял значение, указанное в font-size для html, и умножил его на два. Но получилось, что размер заголовка стал больше, чем он должен был быть:

html { font-size: calc(112.5% + 0.25vw) }
h1 { font-size: calc((112.5% + 0.25vw) * 2); }

Это случилось потому что я использовал font-size для h1 основанный на процентах. То есть, я пересчитал font-size для h1, после того, как он унаследовал уже пересчитанный размер у html.

Давайте рассмотрим это на примере и, для удобства, используем другие числа. Представим, что мы сейчас размер нашего окна составляет 800px , а значение шрифта — 16px.

  • Сначала вычисляется значение font-size для html: 112.5% от 16px = 18px (112.5/100 * 16px)
  • Затем вычисляется значение, указанное в vw: 800px * 0.25 ÷ 100 = 2px
  • И, наконец, значения складываются: 18px + 2px = 20px

Пока всё идёт хорошо. Отлично!

Мы будем использовать тот же метод для вычисления размера h1. Обратите особое внимание на значение 112.5% в вычислении.

  • 112.5% для h1 превращается в 22.5px (112.5/100 * 20px)
  • 0.25vw, всё также, равно 2px (800px * 0.25 ÷ 100)
  • В итоге получаем 49px ((22.5px + 2px) * 2)

Значение h1 должно было быть равным 40px, так как оно должно быть в два раза больше основного текста. А 49px не равно 40px

Проблема вызвана наследование значения font-size для h1 от html. Есть два решения этой проблемы.

Первое: просто заменить 112.5% на 100% для h1:

h1 { font-size: calc((100% + 0.25vw) * 2) }

Второе: убедитесь, что размер шрифта не наследуется элементами.

h1 { font-size: calc((100% + 0.25vw) * 2) }
p { font-size: calc((100% + 0.25vw)) }

Оба способа не очень хороши и меня это не устраивало.

В итоге, я решил, что лучший способ — использование rem и em. Не стоит забывать о старых друзьях, когда появились новые.

html { font-size: calc(112.5% + 0.25vw) }
h1 { font-size: 2em; }

Так как мы говорим об изменении размера шрифта, следующий вопрос, который мы зададим себе, будет «А что там с межстрочными интервалами?».

Ну что ж, давайте теперь поговорим об этом.

Межстрочные интервалы при использовании viewport-юнитов

Ответ прост. Вы заметили, что viewport-юниты используются только для установки значения для корневого элемента (html)? Во всех остальных случаях мы всё ещё используем rem-ы и em-ы! Это означает, что вы можете использовать rem-ы и em-ы так, как я описывал в статье «Everything I know about Responsive Web Typography». Ничего не изменилось!

Чуть не забыл! Ещё одна вещь, о которой стоит сказать до того, как мы закончим статью.

Была ещё одна проблема, которую мне пришлось решить: «Как рассчитать так, чтобы размер шрифта был 20px при достижении окном размера 800px?». Вопрос можно сократить до одного слова — «Точность». Проще говоря, как я могу быть точно уверен, что размер шрифта станет таким, каким надо, при увеличении окна до 800px?

Точность

Оказывается, Майк решил эту проблему. Давайте просто возьмём его формулу и разберём её.

Итак, что мы хотим:

  • При viewporte в 600px font-size равен 18px
  • При viewporte в 1000px font-size равен 22px

Первое что мы делаем — преобразуем меньшее значение (18px) в проценты: 18/16 * 100% = 112.5%.

Следующее: мы вычисляем количество vw. Здесь чуть сложнее.

Для этого мы возьмем разницу между font-size (22px — 18px), поделим на разницу viewport-а (1000 — 600) и умножим на 100vw — меньшее значение viewport-а (100vw — 600px).

Вот как это выглядит:

html {
  font-size: calc(112.5% + 4 * (100vw - 600px) / 400)
}

Это может показаться немного сложным, но как только вы поймете из чего он состоит, то сможете упростить его в SASS-миксин.

Indrek Paas уже сделал это здесь.

Супер точность

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

Вот один из ответов:

html {
  font-size: 100%;

  // Увеличиваем шрифт на 1px каждые 100px от 600px до 1000px
  @media (min-width: 600px) {
    font-size: calc(112.5% + 4 * (100vw - 600px) / 400)
  }

  // Увеличиваем шрифт на 0.5px каждые 100px от 1000px до 2000px
  @media (min-width: 1000px) {
    font-size: calc(137.5% + 5 * (100vw - 1000px) / 1000)
  }
}

Но в реальности вы, скорее всего, не будете увеличивать шрифт с разной скоростью.

Поэтому более реалистичным будет следующий код:

html {
  font-size: 100%;

  // Увеличиваем размер шрифта на 1px каждые 100px от 600px и выше
  @media (min-width: 600px) {
    font-size: calc(112.5% + 4 * (100vw - 600px) / 400)
  }

  // Устанавливаем размер шрифта в 22px после 1000px
  @media (min-width: 1000px) {
    font-size: calc(137.5%)
  }
}

Всё, парни, закругляемся!

И, под конец, главный вопрос: «Я могу это всё использовать на реальных проектах?»

Возможно. Я работал с viewport-юнитами достаточное время, чтобы сделать какой-то вывод. Прежде чем пускать это всё в продакшен нужно:

  • Сделать SASS-миксин
  • Протестировать в разных браузерах на наличие каких-нибудь багов

Будет круто, если вы нашли какие-то ошибки и сообщите о них в комментариях.

Итак, мы поговорили об использовании viewport-единиц для указания размера шрифтов. Viewport-единицы могут быть очень полезны, так как они автоматически пересчитываются при изменении размера окна.

В ходе реализации, я заметил, что лучше всего их использовать только для html. Для всего остального есть MasterCard лучше использовать rem-ы и em-ы.

Michellini Seminioni:
после 3 слова поставь пробел
Michellini Seminioni:
А вообще статья во время на меня вышла. Как раз болит в одно месте от фиксированного font-size
Бобр Добр:
Michellini Seminioni, спасибо. Поправил
Mary Lobareva:
vertical rhythm это из compass для sass, набор миксинов.
Бобр Добр:
Mary Lobareva, спасибо. Не знал.
Данте:
Как вы получили 112.5%?
Бобр Добр:
Данте, вы про font-size: calc(112.5% + 0.5vw)? Стандартный размер шрифта 16px. Нужно получить 18px, но в процентах. А 18px — это 112.5% от 16px.
Mary Lobareva:
Бобр Добр, в Compass есть реализация инструментов для работы с вертикальным ритмом, а сам вертикальный ритм это, так сказать, система пропорций высот всех элементов страницы. Вот тут можете почитать более подробно: https://peredelka.wordpress.com/tag/вертикальный-ритм/
Alexis Meltom:
"К счастью, есть простое способ" Тут ошибка в окончании. Спасибо за статью!
Бобр Добр:
Alexis Meltom, поправил, благодарю!