Современная отзывчивая типографика при помощи CSS clamp

  alexei 08/05/2022 - 10:10
Современная отзывчивая типографика при помощи CSS clamp

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

Концепция отзывчивой типографики в веб-разработке появилась уже давно, и разработчикам часто приходилось полагаться на различные обходные пути, чтобы заставить ее работать должным образом. С новой CSS функцией clamp создание отзывчивой типографики стало простым как никогда ранее.

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

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

Пример типографики на трех разных устройствах с разными размерами экрана
Хотя типографика на всех устройствах выглядит хорошо, размер заголовка при ширине экрана близкой к точке останова (изображение в середине) выглядит немного неаккуратно

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

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

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

Заголовки гладко масштабируются под ширину окна просмотра, и у нас нет несоответствий около точек останова
Заголовки гладко масштабируются под ширину окна просмотра, и у нас нет несоответствий около точек останова

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

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

Первые попытки реализовать отзывчивую типографику

Разработчики часто используют JavaScript, чтобы дополнить недостающие функции CSS, пока они не будут полноценно реализованы в основных браузерах. В первые дни адаптивного веб-дизайна для реализации отзывчивой типографики использовались библиотеки JavaScript, такие как FlowType.JS.

Первая реальная реализация отзывчивой типографики на CSS стала возможной с введением CSS функции calc и относительных единиц измерения области просмотра (vw и vh).


/* Фиксированное минимальное значение при ширине области просмотра
    меньше минимальной точки останова */
.fluid {
  font-size: 32px;
}

/* Отзывчивое значение при ширине области просмотра от 568px до 768px */
@media screen and (min-width: 568px) {
  .fluid {
    font-size: calc(32px + 16 * ((100vw - 568px) / (768 - 568));
  }
}

/* Фиксированное максимальное значение при ширине области просмотра
    больше максимальной точки останова */
@media screen and (min-width: 768px) {
  .fluid {
    font-size: 48px;
  }
}

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


.fluid { /* Мин. значение */ }

@media screen and (min-width: [breakpoint-min]) {
.fluid { /* Предпочтительное значение между мин. и макс. границами */ }

@media screen and (min-width: [breakpoint-max]) { /* Макс. значение */ }

При подходе "сначала мобильный" первый селектор фиксирует значение до минимальной границы. Первый медиа-запрос обрабатывает отзывчивое поведение между двумя точками останова. Конечная точка останова фиксирует значение после максимальной границы. Теперь, когда мы знаем, что делает каждый селектор и медиа-запрос, давайте посмотрим, как применяется минимальная и максимальная границы и как вычисляется отзывчивое значение.


.fluid {
 font-size: [value-min];
}

@media (min-width: [breakpoint-min]) {
  .fluid {
    font-size: calc([value-min] + ([value-max] - [value-min]) * ((100vw - [breakpoint-min]) / ([breakpoint-max] - [breakpoint-min])));
  }
}

@media (min-width: [breakpoint-max]) {
  .fluid {
    font-size: [value-max]
  }
}

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

Несмотря на большое количество требуемых данных, этот способ стал настолько популярным для обработки отзывчивых размеров в целом, что стало ясно, что необходим более упорядоченный подход. Именно здесь появляется CSS функция clamp.

CSS функция clamp

CSS функция clamp в качестве параметров принимает три значения - минимальную границу, предпочтительное значение и максимальную границу, и она закрепляет текущее значение между этими границами. Предпочтительное значение используется для определения значения между границами. Обычно, в качестве предпочтительного значения используются относительные единицы ширины окна просмотра, проценты или другие относительные единицы для достижения эффекта отзывчивости. Это настолько надежная и гибкая функция, что наряду с фиксированными значениями она может принимать даже математические выражения, а также значения из функции attr.


clamp([value-min], [value-preferred], [value-max]);

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

В момент написания этой статьи данную CSS функцию поддерживало более 90% браузеров. Таким образом, функция имеет довольно широкую поддержку. Для не поддерживающих же браузеров, таких как Internet Explorer, достаточно определить фиксированное значение.


font-size: [value-fallback]; /* Значение для обратной совместимости */
font-size: clamp([value-min], [value-preferred], [value-max]);

Отзывчивая типографика с CSS функцией clamp

Давайте используем CSS функцию clamp и передадим ей следующие значения:

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

Давайте посмотрим на следующий пример. Установим размер шрифта в диапазоне от 32px до 48px. Таким образом, свойство font-size получит значения минимум 32px и максимум 48px. Текущее значение определяется относительными единицами ширины окна просмотра или, точнее, 4% от текущей ширины окна просмотра, если это значение находится между минимальной и максимальной границами.


font-size: clamp(32px, 4vw, 48px);

Чтобы понять, как работает CSS функция clamp, давайте посмотрим, исходя из нашего примера, какое значение будет применяться в зависимости от ширины окна просмотра.

Ширин окна просмотра (px) Предпочтительное значение (px) Применяемое значение (px)
500 20 32 (прижато к нижней границе)
900 36 36 (предпочтительное значение между границами)
1400 56 48 (прижато к верхней границе)

Можно заметить некоторые проблемы с этими значениями функции clamp:

  • Пиксельные значения для минимума и максимума недоступны.
    Минимальные и максимальные границы выражаются пиксельными значениями, поэтому они не будут масштабироваться, если пользователь изменит предпочитаемый размер шрифта.
  • Значение окна просмотра для предпочтительного значения недоступно.
    То же, что и в предыдущем случае. Это значение зависит исключительно от ширины окна просмотра и не учитывает предпочтения пользователя.
  • Предпочтительное значение неясно.
    Мы используем значение 4vw, которое поначалу может выглядеть китайской грамотой. Нам нужно знать, когда начинается и заканчивается отзывчивое поведение, чтобы мы могли синхронизировать различные изменения отзывчивого размера шрифта.

Мы можем легко решить первую проблему, преобразовав значения для минимальной и максимальной границ в px в значения в rem. Для этого нужно разделить значения в пикселях на 16 (размер шрифта браузера по умолчанию). При этом минимальное и максимальное значения будут адаптироваться к предпочтениям браузера пользователя.


font-size: clamp(2rem, 4vw, 3rem);

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


font-size: clamp(2rem, 4vw + 1rem, 3rem);

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

Тем не менее, мы до сих пор не знаем, как мы получили предпочтительное значение из примера (4vw + 1rem) для достижения требуемого отзывчивого поведения, поэтому давайте посмотрим, как мы можем точно настроить предпочтительное значение и полностью понять вычисления, стоящие за ним.

Формула отзывчивого изменения размера

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

Например, нам может потребоваться, чтобы отзывчивое поведение начиналось при 1200px и заканчивалось при 800px ширины окна просмотра. Обратите внимание, что для разных минимальных и максимальных границ требуются разные предпочтительные значения (значение окна просмотра и относительного размера), чтобы синхронизировать различные состояния отзывчивой типографики.

Например, обычно нам не нужно, чтобы у нас при ширине окна просмотра от 1200px до 800px было одно отзывчивое поведение, а при ширине от 1000px до 750px другое. Это может привести к несогласованности размеров.

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

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


font-size: clamp([min]rem, [v]vw + [r]rem, [max]rem);

$$y=\frac{v}{100}*x + r$$

  • x - текущая ширина окна просмотра (px).
  • y - рассчитанный отзывчивый размер шрифта (px).
  • v - ширина окна просмотра, влияющая на степень изменения отзывчивого значения (vw).
  • r - относительный размер равный размеру шрифта браузера. Значение по умолчанию 16px.

С помощью этой формулы мы можем легко рассчитать начальную и конечную точки отзывчивого поведения. Рассмотрим наш пример, где минимальное значение 2rem (32px) является постоянным до ширины окна просмотра 400px.

$$32=\frac{4}{100}*x + 16$$

$$16=\frac{1}{25}*x$$

$$x=400$$

Использовав ту же формулу для максимального значения мы увидим, что оно достигает 3rem (48px) при ширине окна просмотра 800px.

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

РАСЧЕТ ПАРАМЕТРОВ ПРЕДПОЧТИТЕЛЬНОГО ЗНАЧЕНИЯ НА ОСНОВЕ КОНКРЕТНЫХ НАЧАЛЬНЫХ И КОНЕЧНЫХ ТОЧЕК

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

  • Минимальный размер шрифта 36px (y1)
  • Максимальный размер шрифта 52px (y2)
  • Минимальное значение должно действовать до ширины окна просмотра в 600px (x1)
  • Максимальное значение должно действовать начиная с ширины окна просмотра в 1400px (x2)
Визуализация отзывчивой типографики по требованиям из примера
Визуализация отзывчивой типографики по требованиям из примера

Давайте возьмем заданные значения и подставим их в приведенную ранее формуле.

$$y=\frac{v}{100} \cdot x + r$$

Мы получаем два уравнения с двумя параметрами, которые нам нужно рассчитать - значение ширины окна просмотра v и относительный размер r.

$$(1)\;\;\; y_1=\frac{v}{100} \cdot x_1 + r$$

$$(2) \;\;\; y_2 =\frac{v}{100} \cdot x_2 + r$$

Мы можем взять первое уравнение и превратить его в следующее выражение, которое мы можем использовать.

$$(1) \;\;\; r=y_1 - \frac{v}{100} \cdot x_1$$

Заменив r во втором уравнении на это выражение, мы получим формулу для расчета v.

$$v=\frac{100 \cdot (y_2-y_1)}{x_2 - x_1}$$

$$v=\frac{100 \cdot (52-36)}{1400 - 600}$$

$$v=2$$

Мы получаем значение ширины окна просмотра 2vw. Аналогичным образом мы можем ввести r и рассчитать его значение, используя доступные параметры.

$$r=\frac{x_1y_2 - x_2y_1}{x_1 - x_2}$$

$$r=\frac{600 \cdot 52 - 1400 \cdot 36}{600 - 1400}$$

$$r=24$$

Примечание: Это значение выражено в пикселях, а относительное значение должно быть выражено в rem, поэтому мы делим значение в пикселях на 16 и получаем 1.5rem.

Нам также нужно преобразовать крайние значения 36px и 52px в rem и добавить все значения в CSS функцию clamp.


font-size: clamp(2.25rem, 2vw + 1.5rem, 3.25rem);

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

$$v=\frac{100 \cdot (y_2-y_1)}{x_2 - x_1}$$

$$r=\frac{x_1y_2 - x_2y_1}{x_1 - x_2}$$

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

Использование отрицательного значения ширины окна просмотра для отзывчивого изменения размера

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


font-size: clamp(3rem, -4vw + 6rem, 4.5rem);

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

ИНСТРУМЕНТ ВИЗУАЛИЗАЦИИ ОТЗЫВЧИВОЙ ТИПОГРАФИКИ

В процессе работы над проектами с отзывчивой типографикой разработчики могут воспользоваться специальным сервисом Modern Fluid Typography Tool.

Графический инструмент позволяет разработчикам увидеть отзывчивое поведение при разных параметрах
Графический инструмент позволяет разработчикам увидеть отзывчивое поведение при разных параметрах

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

Проблемы доступности

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

Эдриан Розелли (Adrian Roselli) в своем блоге тщательно протестировал и задокументировал эти проблемы.

"Когда вы используете единицы измерения vw или ограничиваете размер текста при помощи clamp(), есть вероятность того, что пользователь не сможет масштабировать текст до 200% от его первоначального размера. Если это произойдет, то это будет сбоем WCAG по пункту 1.4.4 Resize text (AA), поэтому обязательно проверьте результаты."

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


/* Отзывчивая типографика для масштаба по умолчанию (без увеличения) */
.title {
  font-size: clamp(2rem, 4vw + 1rem, 3rem);
}

/* Возвращаемся к адаптивной типографике при увеличении масштаба */
body.zoom-active .title {
  font-size: 2rem;
}

@media screen and (min-width: 768px) {
  body.zoom-active .title {
    font-size: 3rem;
  }
}

При этом мы не можем надежно обнаружить событие масштабирования с помощью JavaScript, как мы можем сделать с любым другим событием окна просмотра, вроде изменения размера (resize).

На момент написания этой статьи существует спецификация API Visual Viewport с поддержкой у браузеров в 92%, но значение масштаба (уровень увеличения/уменьшения) просто не работает - оно возвращает одно и то же значение независимо от изменения масштаба. Не говоря уже о том, что нет документации, рабочих примеров или вариантов использования. Это немного странно, учитывая, что этот API имеет такую высокую поддержку браузеров. Безусловно, есть определенные обходные пути, но они также не полностью надежны и не могут идентифицировать, была ли страница увеличена при первой загрузке или же, когда произошло событие.

Если бы API Visual Viewport работало должным образом, мы могли бы легко переключать CSS классы по событию изменения масштаба.


/* Этот код не будет работать, так как visualViewport.scale забагован и
 * всегда возвращает одно и то же значение. Возможно в будущем это исправят.
 */

function checkZoomLevel() {
  if (window.visualViewport.scale === 1) {
    document.body.classList.remove("zoom-active");
  } else {
    document.body.classList.add("zoom-active");
  }
}

window.addEventListener("resize", checkZoomLevel);

К сожалению, применяя отзывчивое изменение размеров, мы рискуем сделать контент недоступным для некоторых пользователей, которые во время просмотра используют функцию масштабирования. До тех пор, пока не будет создан надежный способ обратной совместимости, старайтесь не злоупотреблять отзывчивым изменением размеров и проверяйте, соответствуют ли уровни масштабирования Рекомендациям по доступности веб-контента (WCAG).

Рекомендуемые варианты использования

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

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

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

Заключение

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

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

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

Для решения этой проблемы необходимо переопределять значения отзывчивой типографики при возникновении события масштабирования. Однако в настоящее время это сделать невозможно, поскольку Visual Viewport API не работает должным образом и не реагирует на события пользовательского масштабирования.