- How to create responsive typography using CSS — Three different methods explained
- Why should you use responsive typography?
- Method 1: Responsive typography using only media queries
- Method 2: Responsive typography using a CSS custom property as a multiplier
- Method 3: Responsive typography using the CSS clamp() function
- Wrap up
- Thank you 🙏
- Отзывчивый размер шрифта
- Коротко о viewport-зависимых единицах
- Задача
- Путь к решению
- Используем Sass/SCSS
- Расширяем возможности нашей функции
- Результат
How to create responsive typography using CSS — Three different methods explained
TL;DR: Check out the final Codepen with all the methods. Resize the viewport to see the effect.
Why should you use responsive typography?
The web is used on various screen sizes. Your site has to work on mobile, tablet, laptop, desktop and basically everything in between so there’s a lot to take into consideration.
All of the method examples below are simplified as they only cover three selectors and one breakpoint.
You can use this simple HTML to try out all the methods in this tutorial:
How to create responsive typography using CSS Vestibulum molestie sapien eget orci pellentesque, et aliquam lectus convallis. Phasellus neque velit, ultricies ut lacus at, finibus lobortis dui. Proin eget diam elit. Three different methods explained Pellentesque sagittis nisl nec rhoncus porta. Quisque luctus turpis nec turpis consequat fermentum et vitae diam.
Method 1: Responsive typography using only media queries
This is the most basic method in our list.
Simply, declare your text styles and increase the font-size on bigger screens using a breakpoint:
/* Declare text styles */ h1 font-size: 2em; > h2 font-size: 1.5em; > p font-size: 1em; > /* Increase font sizes by 1.5x on bigger screens */ @media (min-width: 48rem) h1 font-size: 3em; > h2 font-size: 2.25em; > p font-size: 1.5em; > >
Check out the result from this Codepen. Resize the viewport to see the effect.
Using this method is totally fine and it does the job very well.
The downside is the large amount of code you have to write. The codebase gets larger with every new selector you need to add. You have to write the styles separately for every selector in each breakpoint.
In a real world you would probably have text selectors h1, h2, h3, h4, h5, h6, p and at least two breakpoints. At that point, this code is would’ve already gone from 20 lines to 67 lines. That’s 3.35x increase in the amount of code.
The next method will show how to decrease the code amount and make it easier to maintain!
Method 2: Responsive typography using a CSS custom property as a multiplier
CSS custom properties (variables) are super powerful. In the previous method, we increased the font sizes manually one selector at a time. Now, we’ll declare a multiplier variable for the font-sizes and only increase the value of the multiplier inside a breakpoint.
Let’s look at the code example in full and then break it down:
/* Declare a font size multiplier variable */ :root --text-multiplier: 1; > /* Increase the size of the multiplier on bigger screens */ @media (min-width: 48rem) :root --text-multiplier: 1.5; > > /* Declare text styles using calc() function and the multiplier */ h1 font-size: calc(2em * var(--text-multiplier)); > h2 font-size: calc(1.5em * var(--text-multiplier)); > p font-size: calc(1em * var(--text-multiplier)); >
Check out the result from this Codepen. Resize the viewport to see the effect.
The first step is to declare a variable that we use to control our font size later on. We set the default value to 1.
The second step is to increase the multiplier value inside a breakpoint. Unlike SASS variables, CSS custom properties can be used inside media queries.
@media (min-width: 48rem) :root --text-multiplier: 1.5; > >
The last step is to declare our font-sizes but this time we use the calc() function so we can utilise our multiplier.
h1 font-size: calc(2em * var(--text-multiplier)); > h2 font-size: calc(1.5em * var(--text-multiplier)); > p font-size: calc(1em * var(--text-multiplier)); >
As you can see, we only need to declare our font sizes once. This is a huge improvement compared to the first method.
Compared to the first method, we only have 17 lines of code compared to 20. If we would add four more selectors and one breakpoint, our code would only increase from 17 lines to 34. Remember that using the first method it would require 67 lines. Almost twice as much.
The decrease of code is due to our elegant way of using a CSS custom property as a multiplier. Adding another breakpoint takes only three lines of code because we don’t have to modify our font sizes at all. The code is also a lot easier to maintain and modify.
Adding breakpoints is as simple as:
/* Add more breakpoints */ @media (min-width: 64rem) --text-multiplier: 1.75; > @media (min-width: 80rem) --text-multiplier: 2.0; >
To the user, this methods looks exactly the same as the first but the code is much better.
Next up, we’ll take a look at a new CSS clamp() function.
Method 3: Responsive typography using the CSS clamp() function
The clamp() function is pretty powerful! You can set a value using three parameters: a minimum value, a preferred value and a maximum value. Basically you’ll have a 3-in-1 value!
Example declaration for responsive text using the clamp() function looks like this:
font-size: clamp(2rem, 5vw, 3rem);
This allows us to create responsive typography without any media queries. With only one line of code we set the minimum value (mobile), the maximum value (desktop) and the preferred value.
The font size will become fluid as it will always be the preferred value inside the min-max range. Very powerful!
To set the responsive typography for our demo, it is as simple as this:
/* Declare text styles */ h1, h2, p /* Font minimum, preferred and maximum value */ font-size: clamp(var(--min), var(--val), var(--max)); > /* Font size variables */ h1 --min: 2em; /* minimum value */ --val: 5vw; /* preferred value = 5% viewport width */ --max: 3em; /* maximum value */ > h2 --min: 1.5em; /* minimum value */ --val: 4vw; /* preferred value = 4% viewport width */ --max: 2.25em; /* maximum value */ > p --min: 1em; /* minimum value */ --val: 2.5vw; /* preferred value = 2.5% viewport width */ --max: 1.5em; /* maximum value */ >
Check out the result from this Codepen. Resize the viewport to see the effect.
If you look at the code, you’ll see that by using CSS custom properties together with the clamp() function we are able to declare all the text styles at the same time with only on line of code.
After that, we just set the —min, —val and —max custom properties for each selector and we are done!
CSS custom properties can be scoped inside selectors so you can use the same custom properties with multiple selectors just like we have done here with our h1, h2 and p. These custom properties will not clash together even though they have the same name.
This method takes up 18 lines of code and with four more selectors and one breakpoint it would be 38 lines. So, it’s slightly larger than on the second method but we get fluid typography in return.
Note: The clamp() function is pretty new, so you should check if it has the browser support you need from caniuse.com.
Wrap up
CSS is powerful!
In many cases, there’s more than one way to achieve the desired result.
If you read this far, thank you! I hope you learned something new.
Here’s the final Codepen with all the methods once more. Resize the viewport to see the effect.
Thank you 🙏
I hope you found this article helpful 🔥
You can follow me on Twitter for more CSS, Design System and Figma content 💪
Отзывчивый размер шрифта
Уверен, что никому не нужно объяснять почему сайты должны быть гибкими и адаптивными. Все используют проценты и медиа-запросы в своей верстке. Сейчас это уже стандарт.
Но типографика до недавнего времени не была столь гибкой. Все что мы могли — изменять размеры шрифта от брейкпоинта к брейкпоинту. В таком случае мы получали скорее адаптивную типографику нежели отзывчивую. Для каждого медиа-запроса нужно задавать свои значения. Можно конечно использовать компонентный подход с относительными размерами шрифта, что может существенно ускорить процесс редактирования, но принципиально это ничего не меняет. При перемещении компонента в другое место нужно будет опять пробегать по всем медиа запросам и подставлять новые значения.
Но потом появились они — vw, vh, vmin, vmax — единицы измерения, которые базируются на viewport. У нас появился шанс на отзывчивую типографику.
Коротко о viewport-зависимых единицах
100vw равно ширине (100vh — высоте) экрана в пикселях (или будет приведено к другим величинам если использовать в calc()). 100vmax — равно большему из пары высота/ширина, 100vmin — меньшему. Сразу же начали появляться рецепты использования этих чудо величин в типографике.
Но в таком виде их сложно контролировать. Иногда попадались рецепты с использованием каких-то магических процентных величин. Но меня не устраивал вариант разбираться откуда они, а потом считать или подбирать вручную их для получения нужных мне размеров в моих условиях.
Задача
Хотелось иметь универсальный рецепт, который позволял бы задавать минимальное и максимальное значения для размера шрифта, да еще и привязать эти цифры к конкретным размерам viewport. Например, при ширине экрана в 480px иметь ровно 16px, а при 1280px — 24px.
Путь к решению
Вспомнив курс школьной математики пришел к выводу что моя задача не что иное, как уравнение прямой проходящей через 2 точки. Первая точка — меньшая ширины экрана и размер шрифта на ней, вторая — бОльшая ширина и соответствующий ей размер шрифта.
На оси X — ширина окна браузера, на оси Y — наш размер шрифта. Получаем уравнение с двумя неизвестными:
y — наш неизвестный размер шрифта, x — текущий размер экрана. x1 — минимальное значение шрифта, x2 — максимальное значение шрифта. y1 и y2 минимальное и максимальные значение ширины экрана соответственно.
А теперь самое интересное: имея CSS функцию calc() и текущее значение вьюпорта благодаря vw можно получить динамический размер шрифта:
font-size: calc( (100vw - Vmin)/(Vmax - Vmin) * (Fmax - Fmin) + Fmin);
Эту формулу уже можно использовать в чистом CSS. Только нужно помнить, что при операциях умножения/деления хотя бы один из аргументов должен быть без единиц измерения, а при сложение/вычитании — оба с единицами. Подставив наши значения получим рабочий вариант (обратите внимание на использование единиц измерения):
font-size: calc( (100vw - 480px)/(1280 - 480) * (24 - 16) + 16px);
Единицы измерений можно использовать любые, главное, чтобы они совпадали. Тоже самое, только в rem:
font-size: calc( (100vw - 30rem)/(80 - 30) * (1.5 - 1) + 1rem);
Используем Sass/SCSS
Имея под рукой такой инструмент как Sass можно большую часть вычислений спрятать от браузера.
Для этого больше подойдет следующее уравнение прямой:
Коеффициенты k и b можно получить из уравнения прямой по двум точкам:
@function calcFluidFontSize($f-min, $f-max, $v-min, $v-max) < $k: ($f-max - $f-min)/($v-max - $v-min); $b: $f-min - $k * $v-min; $b: $b * 1px; @return calc( #* 100vw + # ); > .fluid-font-size
В случае, если мы хотим иметь возможность использовать не только px но и другие единицы измерений, можно передавать их одним из параметров:
@function calcFluidFontSize($f-min, $f-max, $v-min, $v-max, $units: px) < $k: ($f-max - $f-min)/($v-max - $v-min); $b: $f-min - $k * $v-min; $b: $b + $units; @return calc( #* 100vw + # ); > .fluid-font-size
Для защиты от неверно введенных данных, можно обрезать единицы измерений при получении данных. Для этого можно использовать функцию strip-unit или взять ее из библиотеки bourbon:
@function strip-unit($number) < @if type-of($number) == 'number' and not unitless($number) < @return $number / ($number * 0 + 1); >@return $number; > @function calcFluidFontSize($f-min, $f-max, $w-min, $w-max, $units: px) < $f-min: strip-unit($f-min); $f-max: strip-unit($f-max); $w-min: strip-unit($w-min); $w-max: strip-unit($w-max); $k: ($f-max - $f-min)/($w-max - $w-min); $b: $f-min - $k * $w-min; $b: $b + $units; @return calc( #* 100vw + # ); >
Расширяем возможности нашей функции
Функцию для вычисления размера шрифта мы имеем. Теперь на ее основе можно создать миксин, который можно расширять как угодно.
Например, мы хотим фолбек для старых браузеров:
@mixin fluidFontSize($f-min, $f-max, $w-min, $w-max, $fallback: false) < @if ($fallback) < font-size: $fallback; >font-size: calcFluidFontSize($f-min, $f-max, $w-min, $w-max, px); > .fluid-font-size
Теперь можно в качестве фолбека передавать оптимальный размер шрифта на случай, когда vw не поддерживается.
Можно ограничить размеры шрифта нашим минимальным и/или максимальным значением:
@mixin fluidFontSize($f-min, $f-max, $w-min, $w-max, $fallback: false) < font-size: $f-min; @media (min-width: $w-min) < @if ($fallback) < font-size: $fallback; >font-size: calcFluidFontSize($f-min, $f-max, $w-min, $w-max, px); > @media (min-width: $w-max) < font-size: $f-max; >> .fluid-font-size
Теперь при ширине экрана меньше 480px шрифт всегда будет 16px, после 480 он станет резиновым, а после 1280px всегда будет 24px.
Результат
Мы получили базовую функцию, которая выполняет весь основной функционал:
@function calcFluidFontSize($f-min, $f-max, $w-min, $w-max, $units: px) < $f-min: strip-unit($f-min); $f-max: strip-unit($f-max); $w-min: strip-unit($w-min); $w-max: strip-unit($w-max); $k: ($f-max - $f-min)/($w-max - $w-min); $b: $f-min - $k * $w-min; $b: $b + $units; @return calc( #* 100vw + # ); >
Для ее расширения использовались миксины, с помощью которых можно сделать фоллбеки, ограничения, конвертацию единиц измерения и многое другое. Поиграться со всем этим можно на codepen. Если Вы не хотите использовать Sass здесь вы найдете способ вычислений на чистом CSS.