"У всякой проблемы всегда есть решение — простое, удобное, и конечно ошибочное". — Генри Луис Менкен.
Суть проблемы
На первый взгляд реализация адаптивной верстки может показаться «линейным квестом » с довольно небольшим полем для маневров.
Назначаем нужные классы, меняем по мере надобности размеры, положение или порядок элементов и дело вроде бы сделано.
Но время от времени анализируя вёрстку в полностью завершенных проектах, невольно ловишь себя на мысли, что в теории все выглядит гораздо лучше чем на практике. На старте css выглядит максимально ухоженно и логично, но чем его больше тем он обычно запущенее, особенно после нескольких правок с большими временными промежутками.
При столкновении с необычными дизайнерскими решениями медиа запросы становятся «толще», появляются нестандартные брейкпоинты, а при смене деталей дизайна, внесение правок в вёрстку становится довольно тяжелой работой.
Любая поправка от клиента или дизайнера, и css код нужно редактировать во всех медиа запросах (особенно если это чужой css и они разбросаны по всему коду в разных местах с нелогичной последовательностью).
Что часто приводит к ситуации когда вы уже не совсем контролируете ситуацию и появляется соблазн прибегнуть к «жёстким» методам, таким как директива !important, или вложенность. Код становится ещё менее настраиваемым и где-то там среди тысяч строк появляются строки которые уже не нужны и только (пусть и незначительно ) замедляют работу браузера.
Решение
Часть 1. Абсолютная относительность
Основная и важнейшая мысль этой статьи в том что чем меньше css кода мы пишем тем легче его контролировать.
Суть метода обратной адаптивности в том — что бы сделать каждый элемент максимально адаптивным, после чего постепенно уменьшать его способность приспосабливаться к размерам экрана.
Итак главный шаг к этому — это использование абсолютных единиц измерения: px, em, rem только внутри медиа запросов (за редкими исключениями).
Вне медиа запросов нам лучше пользоваться только относительными viewport единицами измерения: vw, vh, vmax и процентами %.
Корневые теги блоков и текст мы будем измерять во viewport единицах, для дочерних же, размер удобнее считать в процентах от родительского.
Звучит логично — позволить элементам адаптироваться к размерам экрана самостоятельно, не переписывая настройки для каждого брейкпоинта.
Каждый раз работу стоит начинать с подготовки в независимости от размеров проекта.
Первое что мы сделаем это измерим наш пример макета и запишем все нужные нам размеры.
1920 — это главная ширина нашего макета, от нее будут зависеть все остальные размеры по горизонтали.
930 — это главная высота нашего макета (предполагаемая высота одновременно видимой на экране области страницы), от нее будут зависеть все размеры по вертикали.
1400 — это ширина контейнера, в который будет упаковываться всё содержимое страницы.
Далее создадим основные классы для контейнера и текста, следующим образом:
(Вычисляемая ширина / ширина макета) * 100, т.е. в нашем случае
(1400 / 1920) * 100 = 72.9
Результат как и планировалось выше запишем во viewport единицах а именно view width:
.container {
width: 72.91vw;
}
Тоже самое проделаем для текста за тем исключением, что вместо vw используем vmax — что бы использовать максимальный размер экрана а не ширину.
(55 / 1920) * 100 = 2.86
.page__title {
font-size: 2.86vmax;
}
Так же для элементов у которых совпадает значение высоты и ширины (квадратные и круглые элементы) тоже нужно использовать vmax единицы что бы сохранять пропорции. Далее можно приступить к верстке и набросать сетку.
Для блоков которым нужно задать высоту используем ту же формулу пересчёта во viewport, но теперь вместо ширины мы будем будем отталкиваться от высоты экрана и к результату дописывать vh(view height). Так же к верхним и нижним отступам мы будем применять vh.
(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;
}
А ширину вложенных блоков как я говорил ранее мы посчитаем в процентах используя 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);
}
Часть 2. Обратная адаптивность
Блоки ведут себя максимально адаптивно, но они адаптивны чрезмерно:
текст становиться нечитаемым на маленьких экранах, а блоки готовы сужаться до бесконечности, на любом экране.
Теперь настало время для обратной адаптивности.
Используя медиа запросы мы заменяем относительные единицы на абсолютные.
Em для Размера шрифта;
Px для высоты блоков;
Для ширины контейнера и некоторых блоков мы продолжим использовать относительные единицы но сменим их на %:
@media (max-width: 767px) {
.page__title {
font-size: 4em;
}
.main__block {
width: 300px;
height: 300px;
}
.some__block {
width: 100%;
height: 300px;
}
....
}
Таким образом при одном единственном медиа запросе мы сменили view port единицы измерения на абсолютные, тем самым частично остановив процесс адаптации.
Важный плюс — теперь за счёт относительных единиц измерения верстка будет одинаково выглядеть как на экране ноутбука, так на экране огромной плазменной панели.
Часть 3. Удобство и щепотка программирования
При всей универсальности данного метода мы продолжаем делать много работы «за кадром», а именно бесконечно пользоваться калькулятором для перевода пикселей во viewport единицы «вручную». Что бы автоматизировать этот процесс нам понадобиться выполнить несколько простых шагов с помощью Scss:
1. Записать главные размеры в переменные
$full-width: 1920;
$work-width: 80;
$screen-height: 720;
2. Написать функцию для автоматичемкого пересчёта пикселей во viewport
@function vmax($pixels, $context: $full-width) {
@return #{($pixels/$context)* 100}vmax
}
и две аналогичные для vw и vh.
Теперь мы можем смело писать все размеры в том виде в котором они указаны в макете примера и не считать это «вручную»:
.main__block {
width: vmax(300);
height: vmax(300);
margin-top: vh(60);
margin-right: vw(60);
}
Тем самым мы экономим время и силы.
Выше в медиа запросах мы использовали единицы em для указания размеров шрифта,
поэтому неплохо было бы написать функцию и для них, что соблюдать четкость и порядок:
$browser-context: 16;
@function em($pixels, $context: $browser-context) {
@return #{$pixels/$context}em
}
Думаю вполне очевидно что эти функции будут написаны один раз и далее могут «переезжать» из одного проекта в другой, вместе с созданными переменными, а так же некоторыми классами зависящими от них.
Единственное что придется делать начиная работу над новым проектом это снова «снять мерку» с макета и подмена значений в этих переменных.
Заключение
- Мы получаем минимум лишнего кода разбросанного в разных концах и файлах.
- Увеличиваем свой контроль над ним.
- Ускоряем процесс написания и редактирования кода.
- Банально упрощаем себе жизнь, ведь как показывает практика — меньше кода = меньше проблемм.