[Из песочницы] Максимальная адаптивность с минимальным количеством медиа запросов, или метод обратной адаптивности

»У всякой проблемы всегда есть решение — простое, удобное, и конечно ошибочное». — Генри Луис Менкен.


Суть проблемы


На первый взгляд реализация адаптивной верстки может показаться «линейным квестом » с довольно небольшим полем для маневров.

Назначаем нужные классы, меняем по мере надобности размеры, положение или порядок элементов и дело вроде бы сделано.

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

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

Любая поправка от клиента или дизайнера, и css код нужно редактировать во всех медиа запросах (особенно если это чужой css и они разбросаны по всему коду в разных местах с нелогичной последовательностью).

Что часто приводит к ситуации когда вы уже не совсем контролируете ситуацию и появляется соблазн прибегнуть к «жёстким» методам, таким как директива ! important, или вложенность. Код становится ещё менее настраиваемым и где-то там среди тысяч строк появляются строки которые уже не нужны и только (пусть и незначительно) замедляют работу браузера.

Решение


Часть 1. Абсолютная относительность


Основная и важнейшая мысль этой статьи в том что чем меньше css кода мы пишем тем легче его контролировать.

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

Итак главный шаг к этому — это использование абсолютных единиц измерения: px, em, rem только внутри медиа запросов (за редкими исключениями).

Вне медиа запросов нам лучше пользоваться только относительными viewport единицами измерения: vw, vh, vmax и процентами %.

Корневые теги блоков и текст мы будем измерять во viewport единицах, для дочерних же, размер удобнее считать в процентах от родительского.

Звучит логично — позволить элементам адаптироваться к размерам экрана самостоятельно, не переписывая настройки для каждого брейкпоинта.

Каждый раз работу стоит начинать с подготовки в независимости от размеров проекта.
Первое что мы сделаем это измерим наш пример макета и запишем все нужные нам размеры.

image

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;
  margi-right: 3.12vw;
}


А ширину вложенных блоков как я говорил ранее мы посчитаем в процентах используя flex-basis.

image

(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)
  margi-right: vw(60);
}


Тем самым мы экономим время и силы.

Выше в медиа запросах мы использовали единицы em для указания размеров шрифта,
поэтому неплохо было бы написать функцию и для них, что соблюдать четкость и порядок:

$browser-context: 16; 
@function em($pixels, $context: $browser-context) {
  @return #{$pixels/$context}em
}


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

Единственное что придется делать начиная работу над новым проектом это снова «снять мерку» с макета и подмена значений в этих переменных.

Заключение


  • Мы получаем минимум лишнего кода разбросанного в разных концах и файлах.
  • Увеличиваем свой контроль над ним.
  • Ускоряем процесс написания и редактирования кода.
  • Банально упрощаем себе жизнь, ведь как показывает практика — меньше кода = меньше проблемм.

© Habrahabr.ru