
CSS Gateway Math
- Transfer

CSS-lock is a technique from adaptive web design that allows you not to jump from one value to another, but to switch smoothly, depending on the current size of the viewport. The idea and one of the implementations was proposed by Tim Brown in the article Flexible typography with CSS locks . When I tried to understand its implementation and create my own options, I could hardly understand what was happening. I did a lot of calculations and thought it would be useful to explain to others this whole math.
In the article I will describe the methodology itself, its limitations and the mathematics underlying it. Do not worry: there are mostly additions and subtractions. In addition, I tried to break everything down into stages and decorated them with graphs.
What is a CSS gateway?
Viewport Size Dependence
In my latest projects, I used a full-width banner with a headline and only “desktop” templates with large fonts. I decided that I needed small fonts for small screens and something for intermediate sized screens. So why not make the font size dependent on the width of the viewport?
Previously, they did something like this:
h1 { font-size: 4vw; /* Бум! Готово. */ }
This approach has two problems:
- On very small screens, the text becomes tiny (12.8 pixels high with a screen width of 320 pixels), on large screens it becomes huge (64 at 1600);
- Custom font size settings are not taken into account.
CSS gateways can get rid of the first problem. Great CSS gateways will also try to take into account user preferences.
CSS Gateway Idea
CSS gateway is a special type of CSS value calculation in which:
- there is a minimum and maximum value,
- there are two breakpoints (usually dependent on the width of the viewport)
- between these points, the value varies linearly from minimum to maximum.

"With a width of less than 320 pixels, we will use 20px fonts, over 960 pixels - 40px, and between 320 and 960 - from 20px to 40px."
On the CSS side, it might look like this:
h1 { font-size: 1.25rem; }
@media (min-width: 320px) {
h1 { font-size: /* волшебное значение от 1.25 rem до 2.5 rem */; }
}
@media (min-width: 960px) {
h1 { font-size: 2.5rem; }
}
The first task is to realize the magical meaning . I’ll spoil your pleasure a bit and say right away that it looks like this:
h1 {
font-size: calc(1.25rem + viewport_relative_value);
}
Here it
viewport_relative_value
can be a single value (for example 3vw
) or can be a more complex calculation (based on the unit of view of the viewing area vw
or some other unit).Limitations
Because CSS gateways are bound to viewport units, gateways have a number of important limitations. They can only accept numerical values, use
calc()
and accept values in pixels . Why is that? Because the units of the viewport (
vw
, vh
, vmin
and vmax
) are always defined in pixels. For example, if the width of the viewport is 768 pixels, then it 1vw
is determined to be 7.68 pixels. (There is a mistake in Tim’s article: he writes that the calculations seem to
100vw - 30em
give a value em
. This is not so. The browser counts 100vw
in pixels and subtracts the value 30em
for this element and property from it.) Some examples of what does not work:
- CSS gateway for a property
opacity
because itopacity: calc(.5+1px)
is a mistake; - CSS gateway for most functions
transform
(for examplerotate
: the gateway cannot rotate based on the value in pixels).
So we have a pixel limit. Maybe there are courageous people and they will calculate all the properties and techniques that can be used in CSS gateways.
First, take the properties
font-size
and line-height
see how you can create CSS gateways for them with control points based on pixels or em.CSS gateways with pixel breakpoints
Demos
- CSS calc lock for font-size (rem + px, px MQ)
- CSS calc lock for line-height (% + px, px MQ)
- Combined font-size and line-height lock (px-based)
Next, we will look at how to get CSS code for each of these examples.
Font size as a linear function
We need the font-size to increase proportionally from 20px with a width of 320px to 40px with a width of 960px. We reflect this on the graph: The

red line is a graph of a linear function. You can write it as
y = mx + b
:y
- font size (vertical axis),x
- width of the viewing area in pixels (horizontal axis),m
- slope of the function (how many pixels we add to the font size when the width of the viewing area is increased by 1 pixel),b
- font size before we begin to increase the size of the viewing area.
We need to calculate
m
and b
. In the equation they are constants. First, let's figure it out with
m
. For this, only coordinates are needed (x,y)
. This is similar to calculating speed (distance traveled per unit of time), but in this case, we calculate the font size depending on the width of the viewport:m = font_size_increase / viewport_increase
m = (y2 - y1) / (x2 - x1)
m = (40 - 20) / (960 - 320)
m = 20 / 640
m = 0.03125
Another form:
The overall font-size increase is 20 pixels (
40 - 20
). The overall reduction in the viewing area is 640 pixels (
960 - 320
). If the width of the area grows by 1 pixel, how much will the font-size increase?
20 / 640 = 0.03125 px.
Now let's calculate
b
.y = mx + b
b = y - mx
b = y - 0.03125x
Since our function is checked using both of these points, we can use the coordinates of
(x,y)
any of them. Take the first one:b = y1 - 0.03125 × x1
b = 20 - 0.03125 × 320
b = 10
By the way, you could calculate these 10 pixels just by looking at the graph. But we don’t always have it :-)
Now our function looks like this:
y = 0.03125x + 10
Convert to CSS
y
- the size of the font-size, and if we want to perform basic operations in CSS, we need calc()
.font-size: calc( 0.03125x + 10px );
Of course, this is pseudo CSS, therefore
x
it is not a valid syntax. But in our linear function, it x
represents the width of the viewport, which in CSS can be expressed as 100vw
.font-size: calc( 0.03125 * 100vw + 10px );
Now we have working CSS. If you need to express more briefly, we perform the multiplication. Since
0.03125 × 100 = 3.125
, then:font-size: calc( 3.125vw + 10px );
Now limit the width of the viewport to 320 and 960 pixels. Add some media queries:
h1 { font-size: 20px; }
@media (min-width: 320px) {
h1 { font-size: calc( 3.125vw + 10px ); }
}
@media (min-width: 960px) {
h1 { font-size: 40px; }
}
Now our graph looks as it should:

Nice, but I don’t really like the pixel values when declaring font-size. Is it possible to do better?
Apply custom settings
Almost every browser allows users to set the default text size. Most often, it is initially equal to 16 pixels, but sometimes it is changed (usually increased).
I want to insert custom settings into our formula and for this I will pay attention to the values
rem
. For em
percentages, the same principle applies. First, check that the base (root) font-size is not assigned an absolute value. For example, if you use CSS from Bootstrap 3, there are a lot of such things:
html {
font-size: 10px;
}
Never do that! (Fortunately, Bootstrap 4 fixed this.) If you really need to change the base em (
1rem
) value , use:/*
* Меняет значение rem с соблюдением пропорциональности.
* При размере по умолчанию font-size 16 пикселей:
* • 62.5% -> 1rem = 10px, .1rem = 1px
* • 125% -> 1rem = 20px, .05rem = 1px
*/
html {
font-size: 62.5%;
}
Nevertheless, let’s leave the basic font-size alone, let it be 16 pixels by default. Let's see what happens if we replace pixel values with
rem
values in our font-size gateway :/*
* С пользовательскими настройками по умолчанию:
* • 0.625rem = 10px
* • 1.25rem = 20px
* • 2.5rem = 40px
*/
h1 { font-size: 1.25rem; }
@media (min-width: 320px) {
h1 { font-size: calc( 3.125vw + .625rem ); }
}
@media (min-width: 960px) {
h1 { font-size: 2.5rem; }
}
If you run the code with the default browser settings, then it will behave like the previous code that used pixels. Wonderful!
But since we did this in order to support user-made changes , we need to check how everything works. Suppose a user sets the font size to 24 pixels instead of 16 (50% more). How will the code behave?

Blue line: font-size is 16 pixels by default.
Red line: font-size is 24 pixels by default.
As the viewing area is enlarged to 320 pixels, the font becomes smaller (from 30 pixels decreases to 25), and when the second control point is reached, it increases spasmodically (from 45 to 60 pixels). Oh.
The same user-configurable baseline value for all three sizes will help to fix this. For example, choose
1.25rem
:h1 { font-size: 1.25rem; }
@media (min-width: 320px) {
h1 { font-size: calc( 1.25rem + 3.125vw - 10px ); }
}
@media (min-width: 960px) {
h1 { font-size: calc( 1.25rem + 20px ); }
}
Pay attention to
3.125vw - 10px
. This is our old linear function (in the form mx + b
), but with a different value b
. Let's call him b′
. In this case, we know that the base value is 20 pixels, so we can get the value by b′
simple subtraction:b′ = b - baseline_value
b′ = 10 - 20
b′ = 10
Another way is to select the base value from the very beginning , and then look for a linear function that describes the increase in font-size (let's call it
y′
so as not to be confused with the value of the font-size itself y
).x1 = 320
x2 = 960
y′1 = 0
y′2 = 20
m = (y′2 - y′1) / (x2 - x1)
m = (20 - 0) / (960 - 320)
m = 20 / 640
m = 0.03125
b′ = y′ - mx
b′ = y′1 - 0.03125 × x1
b′ = 0 - 0.03125 × 320
b′ = -10
We got a function
y′ = 0.03125x - 10
that looks like this: 
With the base value in
rem
and additional values in vw
and / or px
we can finally create a full-fledged working gateway for font-size. When the user changes the default font size, the system adjusts to it and does not break. 
Magenta line: degree of increase in font-size.
Blue line: font-size is 16 pixels by default.
Red line: font-size is 24 pixels by default.
Of course, this is not exactly what the user asked: he wanted to increase the font by 50%, and we increased it by 50% in small viewing areas and by 25% in large. But this is a good compromise.
Creating a gateway for line height
In this case, we will have this scenario: “I want paragraphs with a line height of 140% with a viewing area 320 pixels wide and 180% with 960”.
Since we will work with the base value plus a dynamically changing value expressed in pixels , we need to know how many pixels are the coefficients 1.4 and 1.8. That is, you need to calculate
font-size
for our paragraphs. Let's say the base font size is 16 pixels. We get:16 * 1.4 = 22.4
pixels with a lower viewport size (320 px
)16 * 1.8 = 28.8
pixels at top viewport size (960 px
)
We will take as a base value
140% = 22.4px
. It turns out that the overall font increase is 6.4 pixels. We use our linear formula:x1 = 320
x2 = 960
y′1 = 0
y′2 = 6.4
m = (y′2 - y′1) / (x2 - x1)
m = (6.4 - 0) / (960 - 320)
m = 6.4 / 640
m = 0.01
b′ = y′ - mx
b′ = y′1 - 0.01 × x1
b′ = 0 - 0.01 × 320
b′ = 3.2
y′ = 0.01x - 3.2
Convert to CSS:
line-height: calc( 140% + 1vw - 3.2px );
Note : the base value must be expressed as 140% or
1.4em
; dimensionless 1.4
will not work inside calc()
. Then we add media queries and check that all ads
line-height
use the same base value ( 140%
).p { line-height: 140%; }
@media (min-width: 320px) {
p { line-height: calc( 140% + 1vw - 3.2px ); }
}
@media (min-width: 960px) {
p { line-height: calc( 140% + 6.4px ); }
}
I remind you: for a large viewing area, you can’t just use it
180%
, we need the part expressed in pixels, which is added to the base value. If you take it 180%
, then with a basic font size of 16 pixels, everything will be fine until the user changes it. Let's plot and check the code for different base font-size values.

Blue line: font-size is 16 pixels by default.
Red line: font-size is 24 pixels by default.
Now that our formula
line-height
depends on the font-size of the element itself, changing the font size will lead to a change in the formula. For example, this demo shows a paragraph with enlarged text defined as:.big {
font-size: 166%;
}
This changes our breakpoints:
16 * 1.66 * 1.4 = 37.184
pixels with a lower viewport size (320px
)16 * 1.66 * 1.8 = 47.808
pixels at top viewport size (960px
)
We draw calculate and obtain the updated formula
y′ = 0.0166x - 5.312
. Then combine this and the previous styles in CSS:p { line-height: 140%; }
.big { font-size: 166%; }
@media (min-width: 320px) {
p { line-height: calc( 140% + 1vw - 3.2px ); }
.big { line-height: calc( 140% + 1.66vw - 5.312px ); }
}
@media (min-width: 960px) {
p { line-height: calc( 140% + 6.4px ); }
.big { line-height: calc( 140% + 10.624px ); }
}
You can also assign computations to CSS. Since we use the same control points and relative line-heights as for the standard paragraph, we only need to add a coefficient of 1.66:
p { line-height: 140%; }
.big { font-size: 166%; }
@media (min-width: 320px) {
p { line-height: calc( 140% + 1vw - 3.2px ); }
.big { line-height: calc( 140% + (1vw - 3.2px) * 1.66 ); }
}
@media (min-width: 960px) {
p { line-height: calc( 140% + 6.4px ); }
.big { line-height: calc( 140% + 6.4px * 1.66 ); }
}
Combining font-size and line-height gateways
Now let's try to put everything together. Scenario: There is an adaptive text column (fluid column) with H1 and several paragraphs. We need to change the font-size and line-height using the following values:
Element and Property | Value at 320px | Value at 960px |
H1 font-size | 24 pixels | 40 ppi |
H1 line-height | 133.33% | 120% |
Font size | 15 ppi | 18 ppi |
P line-height | 150% | 166.67% |
You will see that with the height of the line we do two things. There is a general rule: when the text grows, the row height needs to be reduced, and when the column becomes wider - increase. But in our scenario, both situations are contradictory at the same time! Therefore, you need to choose priorities:
- For H1, increasing the font-size will be more critical than increasing the width of the column.
- For paragraphs, increasing the width of a column will be more critical than a small increase in font-size.
Now we’ll select two control points - the viewing area with a width of 320 and 960 pixels. Let's start by writing a gateway for font-size:
h1 { font-size: 1.5rem; }
/* .9375rem = 15px с настройками по умолчанию */
p { font-size: .9375rem; }
@media (min-width: 320px) {
h1 { font-size: calc( 1.5rem + 2.5vw - 8px ); }
/* .46875vw - 1.5px равно от 0 до 3px */
p { font-size: calc( .9375rem + .46875vw - 1.5px ); }
}
@media (min-width: 960px) {
h1 { font-size: calc(1.5rem + 16px); }
p { font-size: calc( .9375rem + 3px ); }
}
Nothing new here, only the values have changed.
Now we count gateways for
line-height
. It will be much more difficult than last time. Let's start with the element H1. To
line-height
use the relative base value - 120%. Since the font size of the element is changeable, these 120% allow us to describe the dynamic and linear value determined by two points:24 × 1.2 = 28.8px
at the bottom control point,40 × 1.2 = 48px
at the upper control point.
At the bottom control point, we need to have a value
line-height
of 133.33%, which is about 32 pixels. We find a linear function that describes "what is added to the base value of 120%." If we remove these 120%, we get two modified values:
24 × (1.3333 - 1.2) = 3.2px
at the bottom control point,40 × (1.2 - 1.2) = 0px
at the upper control point.
You should get a negative slope.
m = (y′2 - y′1) / (x2 - x1)
m = (0 - 3.2) / (960 - 320)
m = -3.2 / 640
m = -0.005
b′ = y′ - mx
b′ = y′1 - (-0.005 × x1)
b′ = 3.2 + 0.005 × 320
b′ = 4.8
y′ = -0.005x + 4.8
Convert to CSS:
h1 {
line-height: calc( 120% - .5vw + 4.8px );
}
Let's look at the graph:

Blue line: decrease line-height.
Red line: The base value of line-height (120% of the font-size of the header).
Magenta line: final line-height.
The graph shows that the resulting row height (magenta line) is equal to the base value of 120% plus a decrease in row height (blue line). You can check the calculations on GraphSketch.com yourself .
For paragraphs we will use the base value
150%
. Increase line-height:(1.75 - 1.5) × 18 = 4.5px.

My calculator says that the formula will be like this:
y′ = 0.00703125x - 2.25
To see the full CSS code, take a look at the source code of the demo, which combines font-size and line-height . By changing the size of the browser window, you will see that there is an effect, albeit a weak one.
I also recommend testing this demo by changing the default font size. Please note that here the line-height ratios will be slightly different, but quite acceptable. There is nothing wrong with line-height becoming less than the base value.
Automation Computing
In preparing this section, I performed all the calculations manually or using the Soulver calculator. But it is rather laborious, and the probability of errors is high. To eliminate the human factor, it would be nice to introduce automation.
The first way is to transfer all the calculations to CSS. This is a variant of the formula used in the examples with font-size, when all the values were examined in detail:
@media (min-width: 320px) and (max-width: 959px) {
h1 {
font-size: calc(
/* y1 */
1.5rem
/* + m × x */
+ ((40 - 24) / (960 - 320)) * 100vw
/* - m × x1 */
- ((40 - 24) / (960 - 320)) * 320px
);
}
}
But it turns out too many letters, you can write much more concise:
@media (min-width: 320px) and (max-width: 959px) {
h1 {
font-size: calc( 1.5rem + 16 * (100vw - 320px) / (960 - 320) );
}
}
Coincidentally, this formula was used by Tim Brown in the article Flexible typography with CSS locks , however, with pixels instead of
em
in the value of the variable. This works for the combined version with font-size and line-height, but it may not be so obvious, especially with a negative slope.@media (min-width: 320px) and (max-width: 959px) {
h1 {
font-size: calc( 1.5rem + 16 * (100vw - 320px) / (960 - 320) );
/* При отрицательном наклоне нужно инвертировать контрольные точки */
line-height: calc( 120% + 3.2 * (100vw - 960px) / (320 - 960) );
}
}
The second way is to automate the calculations using the Sass plugin or PostCSS mixin.
CSS gateways with em checkpoints
New demos
I took the first three demos and instead of the pixel values of the control points and increments, I inserted the values based on
rem
.- CSS calc lock for font-size (rem + rem, em MQ)
- CSS calc lock for line-height (% + rem, px MQ)
- Combined font-size and line-height lock (em / rem-based)
In the next section, we will consider the operation of specific syntax in these demos.
The m × 100vw syntax for em-based media queries is not a good idea
We used the syntax above
m × 100vw
(for example, here calc(base + 2.5vw)
). It cannot be used with media-based queries em
. It's all about the context of media queries. Units
em
and rem
refer to the same thing: the base font size in the User Agent. And it, as we have already seen several times, is usually 16 pixels, but the value may be different. Why?- By the will of the browser or OS (mainly in specific cases like TV browsers and readers).
- By the will of the user.
So if we have control points
20em
and 60em
, then they will correspond to the actual CSS width:- 320 and 960 pixels with a base font size of 16 pixels,
- 480 and 1440 - at 24 pixels, etc.
(Note that these are CSS pixels , not hardware pixels . In this article, we do not consider hardware pixels, since they do not affect our calculations.) The
above examples of code like this:
font-size: calc( 3.125vw + .625rem );
If in this syntax we replace all control points with
em
, assuming that in the media request 1 em is 16 pixels, then we get:h1 { font-size: 1.25rem; }
/* Не делайте так :((( */
@media (min-width: 20em) {
h1 { font-size: calc( 1.25rem + 3.125vw - 10px ); }
}
/* Или так. */
@media (min-width: 60em) {
h1 { font-size: calc( 1.25rem + 20px ); }
}
This will work if the OS, browser, and user never change the default font size. Otherwise it will be bad:

Blue line: font-size is 16 pixels by default.
Red line: font-size is 24 pixels by default.
What's going on here? When we change the base font-size, the control points on the basis
em
are shifted to higher pixel values. The only true value for specific points will be 3.125vw - 10px
!- At 320 pixels, it
3.125vw - 10px
equals 0 pixels, as it should be. - At 480 pixels
3.125vw - 10px
equals 5 pixels.
At high control points, worse:
- At 960 pixels
3.125vw - 10px
is equal to 20 pixels, as it should be. - At 1440 pixels it
3.125vw - 10px
is equal to 35 pixels (15 more).
If you want to use checklists based on
em
, then you need to do otherwise.We do the calculations again
This technique is demonstrated in an article by Tim Brown . It implies that most calculations are done in CSS using two variable parts:
100vw
- width of the viewing area;- lower reference point expressed in
rem
.
We use the formula:
y = m × (x - x1) / (x2 - x1)
Why exactly her? Let's go through the steps. Above, we saw that font-size and line-height can be described by a linear function:
y = mx + b
In CSS you can work with
x
(this 100vw
). But it can not be set m
and b
the exact values in pixels or vw
because it is constant, expressed in pixels, and can be confused with our reference points expressed in em
, if the user changes the size of the default font. Let's try to replace
m
, and b
other known values, namely (x1,y1)
and (x2,y2)
. We find
b
using the first pair of coordinates:b = y - mx
b = y1 - m × x1
Putting it all together:
y = mx + b
y = mx + y1 - m × x1
We have excluded
b
from the formula! Also, we saw above that in fact we did not need the full value of
font-size
or line-height
, but only the dynamic part that we add to the base value . We named it y′
and can express it like this:y = y1 + y′
y′ = y - y1
Replace
y
using the derived equality:y′ = mx + y1 - m × x1 - y1
y′ = mx + y1 - m × x1 - y1
We can get rid of pieces
+ y1
and - y1
!y′ = m × x - m × x1
y′ = m × (x - x1)
Now we can replace it
m
with the known values:m = (y2 - y1) / (x2 - x1)
Then:
y′ = (y2 - y1) / (x2 - x1) × (x - x1)
It can also be written like this:
y′ = max_value_increase × (x - x1) / (x2 - x1)
Convert to CSS
We can use this value in CSS. Let's go back to our example “from 20 to 40 pixels”:
@media (min-width: 20em) and (max-width: 60em) {
h1 {
/* ВНИМАНИЕ: это пока не работает! */
font-size: calc(
1.25rem /* базовое значение */
+ 20px /* разница между максимальным и базовым значениями */
* (100vw - 20rem) /* x - x1 */
/ (60rem - 20rem) /* x2 - x1 */
);
}
}
The code is not working yet. It seems like it could work, but
calc()
CSS has a number of limitations related to multiplication and division. Let's start with the fragment
100vw - 20rem
. This part works as is and returns a value in pixels. For example, if the default font-size is 16 pixels and the viewport width is 600 pixels, then the result is 280 pixels (
600 - 20 × 15
). If the default font-size is 24 pixels and the width of the viewport is 600 pixels, then the result is 120 pixels ( 600 - 20 × 24
). 
Note that we use a unit to express our breakpoints
rem
. Why not em
? Because the CSS value doesn’tem
referenceon the base font-size, and on the element’s own font-size (in general) or on its parent font-size (when the property is used font-size
). Ideally, we need CSS, a unit of measure that refers to the default browser font size. But such a unit does not exist. The closest is one
rem
that refers to the base font-size only if it hasn't changed at all . That is, in our CSS, in no case should there be such code:
/* Плохо */
html { font-size: 10px; }
/* Достаточно плохо */
:root { font-size: 16px; }
/* Удовлетворительно, но придётся прописать все
ключевые точки, например 20rem/1.25,
40em/1.25 и т. д. */
:root { font-size: 125%; }
Dimensionless denominators and factors of calc
I would like to result
60rem - 20rem
in a width in pixels. This would mean that the fraction (x - x1) / (x2 - x1)
would give a value in the range from 0 to 1. Let's call it n
. With the default font size of 16 pixels and the width of the viewing area of 600 pixels, we get:
n = (x - x1) / (x2 - x1)
n = (600 - 320) / (960 - 320)
n = 280 / 640
n = 0.475
Unfortunately, not quite that.
The problem is that when dividing in,
calc()
we cannot use pixels or some CSS unit as the denominator. The value must be dimensionless. So what do we do? But what if you simply remove the units in the denominator? What will be the result of the calculation
calc((100vw - 20rem)/(60 - 20))
?The default font size is 16 pixels. | ||
Viewing area | CSS division | Result |
20em (320px) | (320px - 16px × 20) / (60 - 20) | = 0px |
40em (640px) | (640px - 16px × 20) / (60 - 20) | = 8px |
60em (960px) | (960px - 16px × 20) / (60 - 20) | = 16px |
The default font size is 24 pixels. | ||
Viewing area | CSS division | Result |
20em (480px) | (480px - 24px × 20) / (60 - 20) | = 0px |
40em (960px) | (960px - 24px × 20) / (60 - 20) | = 12px |
60em (1440px) | (1440px - 24px × 20) / (60 - 20) | = 24px |
As you can see, in the range between the control points (from
20em
to 60em
) we get a linear change from 0rem
to 1rem
. Good for you! The first time we tried to get our CSS to work, we used a multiplier
20px
. It is necessary to delete it. First try code:
font-size: calc( 1.25rem + 20px * n );
Here it
n
took a value from 0 to 1. But because of the limitations of the division syntax in, calc()
we could not get the result we needed from 0 to 1. Then we got the pixel equivalent for the range
0rem — 1rem
; let's call this value r
. Another limitation
calc()
relates to multiplication. If you write calc(a * b)
, then a
or b
should be a dimensionless number. Since
r
there is a dimension (these are pixels), the second factor must be dimensionless. We want to increase by 20 pixels at the top breakpoint. 20 pixels is this
1.25rem
, so the multiplier will be 1.25
:font-size: calc( 1.25rem + 1.25 * r );
It should work. But keep in mind that the value
r
will change depending on the default font size:- 16 pixels:
1.25 * r
equals 0 to 20 pixels. - 24 pixels:
1.25 * r
equals 0 to 30 pixels.
Let's now write the entire CSS gateway in its entirety, with media queries, upper and lower values:
h1 {
font-size: 1.25rem;
}
@media (min-width: 20em) {
/* Результат (100vw - 20rem) / (60 - 20) в диапазоне 0-1rem,
в зависимости от ширины области просмотра (от 20em до 60em). */
h1 {
font-size: calc( 1.25rem + 1.25 * (100vw - 20rem) / (60 - 20) );
}
}
@media (min-width: 60em) {
/* Правая часть дополнения ДОЛЖНА быть rem.
В нашем примере мы МОЖЕМ заменить всё объявление
на font-size: 2.5rem, но если наше базовое значение
выражалось не в rem, то придётся использовать calc. */
h1 {
font-size: calc( 1.25rem + 1.25 * 1rem );
}
}
In this case, unlike the font-size gateway that uses pixels, when the user increases the default font size by 50%, everything else also increases by 50%: the base value, the variable, and control points. We get a range of 30-60 pixels instead of the required 20-40.

Blue line: font-size is 16 pixels by default.
Red line: font-size is 24 pixels by default.
You can check it yourself in the first demo using em .
Line-height gateways with em / rem
In our second demo, we will change the
line-height
paragraph from 140% to 180%. We will use it 140%
as a base value, and the same formula will act as a variable part as in example c font-size
.p {
line-height: 140%;
}
@media (min-width: 20em) {
p {
line-height: calc( 140% + .4 * (100vw - 20rem) / (60 - 20) );
}
}
@media (min-width: 60em) {
p {
line-height: calc( 140% + .4 * 1rem );
}
}
For the variable part,
line-height
we need a rem value, because it (100vw - 20rem) / (60 - 20)
gives the result in pixels in the range from 0rem
to 1rem
. Since
font-size
our paragraph remains equal 1rem
, increasing the height of the line is still 40%
equal .4rem
. We will use this value in two calc()
expressions. Now let's take an example from the third demo
line-height
. We need to reduceline-height
H1 from 133.33% to 120%. And we know what will change font-size
. For the same example, we have already determined that a decrease in the height of a line can be expressed using two control points:
24 × (1.3333 - 1.2) = 3.2px
with the lower size of the scope,40 × (1.2 - 1.2) = 0px
at the top size of the scope.
Take the base value
120%
, and the variable part will be from 3.2 to 0 pixels. If the default font size is 16 pixels, then 3.2 pixels = 0.2rem
, so the multiplier is equal .2
. Finally, since the variable part must be zero at the top point, you need to invert the control points in the formula:
h1 {
line-height: calc( 120% + 0.2 * 1rem );
}
@media (min-width: 20em) {
h1 {
line-height: calc( 120% + 0.2 * (100vw - 60rem) / (20 - 60) );
}
}
@media (min-width: 60em) {
h1 {
line-height: 120%;
}
}
Two points:
- The value is
.2rem
only true if there is also a font-size gateway in the range of 24 to 40 pixels. This is not shown here, but there is a demo in the source code. - Since we invert the values of control points, both parts of the fraction
(100vw - 60rem) / (20 - 60)
will be negative for the widths of the visibility region less60em
and more20em
(inclusive). For example, at the lower control point, with a default font size of 16 pixels, we get-640px / -40
. And if in the fraction the denominator and numerator are negative, then the result will be positive, so we do not need to change the sign in front of the factor0.2
.
Conclusion
Brief summary. We looked at two forms of CSS gateways:
- for properties that can use dimensions,
- with examples for
font-size
andline-height
, - for control points measured in pixels and
em
.
The determining factor is the type of control point. In most projects, you will use the same points, say, for the gateway
font-size
and for template changes. Key points can be measured in pixels or, depending on the project or your habit em
. Personally, I prefer pixels, but both options have their advantages. Let me remind you: if you are dealing with media queries in
em
, then avoid pixel dimensions when determining container sizes. Also, you cannot ignore the base font-size
element and you can use a single form of CSS gateway:@media (min-width: 20em) and (max-width: 60em) {
selector {
property: calc(
baseline_value +
multiplier *
(100vw - 20rem) / (60 - 20)
);
}
}
Here
multiplier
is the expected overall increase in value, expressed in rem
, but without dimension. For example: 0.75 to maximize by 0.75rem
. If you use media queries in pixels, then you can ignore the underlying
font-size
element. But then I recommend percentage values. You can also apply two different forms of CSS gateways. The first is similar to a em/rem
gateway, only with values in pixels:@media (min-width: 320px) and (max-width: 960px) {
selector {
property: calc(
baseline_value +
multiplier *
(100vw - 320px) / (960 - 320)
);
}
}
Here
multiplier
is the expected total increase in value , expressed in pixels, but without dimension. For example: 12
to maximize by 12px
. The second form of the gateway in the calculation is not so dependent on the browser. Everything that we can, we calculate independently, before passing these values to the browser:
@media (min-width: 320px) and (max-width: 960px) {
selector {
property: calc(
baseline_value + 0.25vw - 10px;
);
}
}
Here, the values of
0.25vw
and are -10px
calculated in advance, probably using Sass or PostCSS. The latter form may be more difficult to implement (if you do not use mixin), but thanks to more obvious values, it can facilitate style checking and debugging.