Простая адаптивная типографика при помощи clamp() - Используем возможности SASS

  alexei 27/10/2022 - 09:21
Простая адаптивная типографика при помощи clamp() - Используем возможности SASS

Адаптивная типографика становится все более популярной, тем более что CSS функция clamp() теперь доступна в каждом браузере. Но, если честно, для достижения нужного результата все еще требуется провести много вычислений. Конечно, для облегчения жизни мы можем воспользоваться такими инструментами как utopia.fyi. Но в больших проектах все это дело очень быстро превратится в кашу. Гораздо лучше иметь дело с легко читаемыми и простым в обслуживании кодом, когда с одного взгляда видно, что он делает. Наверняка, большинство из вас тоже предпочитает видеть такой код. Поэтому вместо того, чтобы добавлять полную функцию clamp() непосредственно в наш код, возможно, нам удастся сделать его немного более читабельным с помощью Sass.

Зачем нам использовать адаптивную типографику?

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

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

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

Как именно функция clamp() работает с типографикой?

Если коротко, то синтаксис функции clamp() выглядит так:


clamp([мин-предел], [предп-значение], [макс-предел]);

Функция принимает три параметра: минимальный предел, предпочтительное значение и максимальный предел. Используя значения rem, мы можем немного повысить доступность, но это все равно не на 100% надежно, особенно при использовании внешних инструментов браузера.

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

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


font-size: clamp(1.44rem, 3.44vw + 0.75rem, 2.81rem);

Однако при помощи Sass мы можем сделать подобные определения более понятными.

Что мы хотим достичь при помощи Sass?

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

Минимальный и максимальный размеры шрифта определяются довольно легко. Если нам нужен минимальный размер шрифта 16px (или 1rem) и максимальный размер шрифта 32px (или 2rem), у нас уже есть две части нашей функции clamp:


clamp(1rem, [?], 2rem)

Создание базовой автоматической функции

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

Если же говорить коротко, то вычисления приблизительно будут следующими:

(макс размер шрифта - мин размер шрифта) / (макс контрольная точка - мин контрольная точка)

Давайте подготовимся к математическим вычислениям в Sass. Для начала создадим файл fluid-typography.scss и добавим в него для использования блок sass:math, а также определение пользовательской функции с нужными нам аргументами:


@use "sass:math";

@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
 
}

Теперь давайте проведем вычисление коэффициента уклона внутри нашей функции с использованием возможностей sass:math:


@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
  $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
}

Чтобы получить значение, с которым можно будет работать, нам нужно умножить наш коэффициент на 100:


$slope-to-unit: $slope * 100;

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


$intercept: $min-size - $slope * $min-breakpoint;

И, наконец, возвращаем результат:


@return clamp(#{$min-size}, #{$slope-to-unit}#{$unit} + #{$intercept}, #{$max-size});

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


h1 {
   font-size: #{fluid(1rem, 2rem, 25rem, 62.5rem)}
}

ЗАМЕЧАНИЕ ПО ПОВОДУ ЕДИНИЦ ИЗМЕРЕНИЯ

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


h1 {
   font-size: #{fluid(1rem, 2rem, 25rem, 62.5rem, vh)}
}

Обновление базовой функции, чтобы вычисления были более естественными

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

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


@function px-to-rem($px) {
    $rems: math.div($px, 16px) * 1rem;
    @return $rems;
}

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


@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: $slope * 100;
    $intercept-rem: px-to-rem($min-size - $slope * $min-breakpoint);
    $min-size-rem: px-to-rem($min-size);
    $max-size-rem: px-to-rem($max-size);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}

Теперь мы можем использовать такой ввод:


font-size: #{fluid(16px, 32px, 320px, 960px)}

Это даст такой результат:


font-size: clamp(1rem, 2.5vw + 0.5rem, 2rem);

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

Ввод:


font-size: #{fluid(16px, 31px, 320px, 960px)}

Вывод:


font-size: clamp(1rem, 2.34375vw + 0.53125rem, 1.9375rem);

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

Округление значений и добавление значений по умолчанию

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


@function round($number, $decimals: 0) {
    $n: 1;
    @if $decimals > 0 {
        @for $i from 1 through $decimals {
            $n: $n * 10;
        }
    }
    @return math.div(math.round($number * $n), $n);
}

Теперь на выходе мы получим округленные числа. Мы бы посоветовали округлять выходные значения как минимум до двух десятичных знаков. Обновленная функция:


@function fluid($min-size, $max-size, $min-breakpoint, $max-breakpoint, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: round($slope * 100, 2);
    $intercept-rem: round(px-to-rem($min-size - $slope * $min-breakpoint), 2);
    $min-size-rem: round(px-to-rem($min-size), 2);
    $max-size-rem: round(px-to-rem($max-size), 2);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}

Теперь, тот же пример даст нам более чистый результат.

Ввод:


font-size: #{fluid(16px, 31px, 320px, 960px)};

Вывод:


font-size: clamp(1rem, 2.34vw + 0.53rem, 1.94rem);

ДОБАВЛЕНИЕ КОНТРОЛЬНЫХ ТОЧЕК ПО УМОЛЧАНИЮ

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


$default-min-bp: 320px;
$default-max-bp: 960px;

@function fluid($min-size, $max-size, $min-breakpoint: $default-min-bp, $max-breakpoint: $default-max-bp, $unit: vw) {
    // ...
}

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


font-size: #{fluid(16px, 31px)};

А результат будет таким:


font-size: clamp(1rem, 2.34vw + 0.53rem, 1.94rem);

Полная функция имеет следующий вид:


@use 'sass:math';

$default-min-bp: 320px;
$default-max-bp: 960px;

@function round($number, $decimals: 0) {
    $n: 1;
    @if $decimals > 0 {
        @for $i from 1 through $decimals {
            $n: $n * 10;
        }
    }
    @return math.div(math.round($number * $n), $n);
}

@function px-to-rem($px) {
    $rems: math.div($px, 16px) * 1rem;
    @return $rems;
}

@function fluid($min-size, $max-size, $min-breakpoint: $default-min-bp, $max-breakpoint: $default-max-bp, $unit: vw) {
    $slope: math.div($max-size - $min-size, $max-breakpoint - $min-breakpoint);
    $slope-to-unit: round($slope * 100, 2);
    $intercept-rem: round(px-to-rem($min-size - $slope * $min-breakpoint), 2);
    $min-size-rem: round(px-to-rem($min-size), 2);
    $max-size-rem: round(px-to-rem($max-size), 2);
    @return clamp(#{$min-size-rem}, #{$slope-to-unit}#{$unit} + #{$intercept-rem}, #{$max-size-rem});
}

Последнее замечание: Разумно используйте функцию clamp()

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

Если это происходит, то это ошибка WCAG.

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