Maximum adaptability with a minimum number of media queries, or the inverse adaptivity method
" Every problem always has a solution - simple, convenient, and of course erroneous ." - Henry Louis Mencken.
The essence of the problem
At first glance, the implementation of an adaptive layout may seem like a “linear quest” with a rather small field for maneuvers.
We assign the necessary classes, change the dimensions, position or order of elements as needed, and the work seems to be done.
But from time to time analyzing the layout in fully completed projects, you unwittingly catch yourself thinking that in theory everything looks much better than in practice. At the start, css looks as well-groomed and logical as possible, but the bigger it is, the more it is usually more neglected, especially after several edits with large time intervals.
When confronted with unusual design solutions, media queries become “thicker”, non-standard breakpoints appear, and when changing design details, making edits to the layout becomes quite hard work.
Any amendment from the client or the designer, and the css code must be edited in all media queries (especially if it is someone else’s css and they are scattered throughout the code in different places with an illogical sequence).
Which often leads to a situation where you no longer completely control the situation and you are tempted to resort to "hard" methods, such as the! Important directive, or nesting. The code becomes even less customizable and somewhere among thousands of lines there appear lines that are no longer needed and only (albeit slightly) slow down the browser.
Decision
Part 1. Absolute relativity
The main and most important idea of this article is that the less css code we write, the easier it is to control.
The essence of the inverse adaptivity method is to make each element as adaptive as possible, and then gradually reduce its ability to adapt to the screen size.
So the main step towards this is the use of absolute units of measurement: px, em, rem only within media queries (with rare exceptions).
Outside media queries, we'd rather use only relative viewport units of measure: vw, vh, vmax and percent%.
We will measure the root tags of blocks and text in viewport units, for children it is more convenient to consider the size as a percentage of the parent.
It sounds logical to allow the elements to adapt to the screen size on their own, without overwriting the settings for each breakpoint.
Each time, work should begin with preparation regardless of the size of the project.
The first thing we do is measure our sample layout and write down all the dimensions we need.
1920 is the main width of our layout, all other horizontal dimensions will depend on it.
930 is the main height of our layout (the estimated height of the page area that is simultaneously visible on the screen), all dimensions along the vertical will depend on it.
1400 is the width of the container into which the entire contents of the page will be packed.
Next, create the main classes for the container and the text, as follows:
(Calculated width / width of the layout) * 100, i.e. in our case
(1400/1920) * 100 = 72.9
The result as planned above will be written in viewport units namely view width:
.container {
width: 72.91vw;
}
Let's do the same for the text, except that instead of vw we use vmax - to use the maximum screen size and not width.
(55/1920) * 100 = 2.86
.page__title {
font-size: 2.86vmax;
}
Also for elements for which the value of height and width coincides (square and round elements), you also need to use vmax units to keep the proportions. Then you can proceed to the layout and sketch a grid.
For blocks that need to specify the height, we use the same recalculation formula in the viewport, but now instead of the width we will build on the height of the screen and add vh (view height) to the result. We will also use vh for the upper and lower indents.
(300/1920) * 100 = 15.62;
(60/1920) * 100 = 3.12;
.main__block {
width: 15.62vmax;
height: 15.62vmax;
margin-top: 3.12vh;
margin-right: 3.12vw;
}
And as I said earlier, we calculate the width of nested blocks as a percentage using flex-basis.
(970/1920) * 100 = 50.52;
(16/1920) * 100 = 0.83;
.main-menu {
width: 50.52vw;
}
.main-menu__item {
flex-basis: calc(100% / 4 - 0.83vw);
}
Part 2. Backward adaptability
Blocks behave as adaptively as possible, but they are adaptive excessively: the
text becomes unreadable on small screens, and the blocks are ready to taper to infinity on any screen.
Now is the time for inverse adaptability.
Using media queries, we replace relative units with absolute ones.
Em for Font Size;
Px for block heights;
For the width of the container and some blocks, we will continue to use relative units but change them to%:
@media (max-width: 767px) {
.page__title {
font-size: 4em;
}
.main__block {
width: 300px;
height: 300px;
}
.some__block {
width: 100%;
height: 300px;
}
....
}
Thus, with a single media request, we changed the view port units of measurement to absolute ones, thereby partially stopping the adaptation process.
An important plus is that now due to the relative units of measurement, the layout will look the same on the laptop screen as on the screen of the huge plasma panel.
Part 3. Convenience and pinch programming
With all the versatility of this method, we continue to do a lot of work “behind the scenes”, namely, to use the calculator indefinitely to convert pixels into the viewport units “manually”. To automate this process, we need to perform several simple steps using Scss:
1. Write the main dimensions to variables
$full-width: 1920;
$work-width: 80;
$screen-height: 720;
2. Write a function for automatic conversion of pixels in the viewport
@functionvmax($pixels, $context: $full-width){
@return#{($pixels/$context)* 100}vmax
}
and two similar for vw and vh.
Now we can boldly write all the dimensions in the form in which they are indicated in the model of the example and not consider it “manually”:
.main__block {
width: vmax(300);
height: vmax(300);
margin-top: vh(60);
margin-right: vw(60);
}
Thus, we save time and effort.
Above, in media queries, we used em units to specify font sizes,
so it would be nice to write a function for them that follow clarity and order:
$browser-context: 16;
@functionem($pixels, $context: $browser-context){
@return#{$pixels/$context}em
}
I think it is quite obvious that these functions will be written once and then they can “move” from one project to another, together with the created variables, as well as some classes depending on them.
The only thing you have to do when starting work on a new project is to “take the measure” again from the layout and substitute values in these variables.
Conclusion
- We get a minimum of extra code scattered in different ends and files.
- We increase our control over it.
- Speeding up the process of writing and editing code.
- Tritely simplify your life, because, as practice shows, less code = less problems.