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

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


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

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

Но время от времени анализируя вёрстку в полностью завершенных проектах, невольно ловишь себя на мысли, что в теории все выглядит гораздо лучше чем на практике. На старте 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;
  margin-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);
  margin-right: vw(60);
}

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

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

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

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

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

Заключение


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

Комментарии (17)


  1. Qumbeez
    13.08.2018 21:58

    Это всё хорошо и классно до тех пока дизайнер вам не выкатил требования к элементам для каждого экрана.


    1. JohnyScript Автор
      13.08.2018 22:10

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


      1. Qumbeez
        13.08.2018 22:21

        Сохранение пропорций да, но вы думаете что люди с экранами 30 дюймов видят всё в уменьшенном варианте, что при ваших сохранённых пропорциях это будет удобно? Для телевизоров да, но я думаю если думать так, то для этого есть в css есть указатель на тип устройства для которого будут данные стили применяться. Там есть в том числе и tv устройства.


        1. JohnyScript Автор
          13.08.2018 22:43

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


          1. Qumbeez
            13.08.2018 22:47

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


            1. JohnyScript Автор
              13.08.2018 23:01

              Мест где что то может пойти не так, за время использования замечено было не так много, но за счет меньшей разбросанности сss кода вносить правки стало гораздо легче. Поэтому «получили тоже самое x2» не совсем верное замечание.
              В любом деле всегда приятно иметь альтернативные пути решения задач, трудно спрогнозировать когда они смогут пригодиться.


  1. demimurych
    14.08.2018 01:51

    А каким образом Вы боритесь с тем, что единица vh ведет себя по разному на Android и IOS устройствах? А если еще точнее, с «особым» пониманием от Apple того ЧТО должна давать эта единица на мобильном Сафари?


    1. profesor08
      14.08.2018 02:23
      +1

      Она ведет себя по разному от браузера к браузеру.


    1. JohnyScript Автор
      14.08.2018 08:58

      В медиа запросах отвечающих за мобильную версию вестки, я использую только абсолютные единицы измерения и проценты.


  1. franzose
    14.08.2018 08:06

    А как же mobile first? Ведь если есть макеты мобильной версии, то проще верстать как раз с неё.


    1. JohnyScript Автор
      14.08.2018 09:13

      Если мы начинаем верстку с мобильной версии то можем использовать метод описанный выше реверсивно. Постепенно переходя от абсолютных к относительным единицам измерения.


      1. franzose
        14.08.2018 09:28

        Видимо, мне просто привычнее «думать» от меньшего к большему, т.е. использовать min-width вместо max-width.


  1. Evgeniy_junior
    14.08.2018 09:59

    Кроссбраузерность?)


    1. JohnyScript Автор
      14.08.2018 10:06

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


  1. jimmyjonezz
    14.08.2018 13:32

    Все идет к тому, что составлять css будут не верстальщики, а программисты…


    1. franzose
      14.08.2018 14:54

      Ну, fullstack никто не отменял) К тому же давно уже существуют препроцессоры LESS, SASS, Stylus. Уже забыл, когда использовал чистый CSS последний раз.


  1. noodles
    14.08.2018 22:18

    К миксинам и функциям в sass лучше пишите сразу комментарии. Потому что вам очевидно, другим нет.

    vh, vmin, vmax — неадекватные единицы, лучше их избегать.

    При вёрстке, удобно быть главным над дизайнером) Обычно говорю ему — мне два макета пожалуйста — минимальный 320px*550px, и максимальный 1366px*650px. Остальное додумаю сам. Если сомневаюсь над визуальным видом какого-то компонента при какой-то определённой ширине/высоте, зову дизайнера и вместе думаем как лучше. В итоге удобно обоим. Ему не нужно рисовать 100500 макетов, мне не нужно думать как реализовать избыточную креативность), которая зачастую ломает «очередность потока блоков» в документе.
    Иногда, если известно что заказчик смотрит результат на каком-то конкретном айпаде, и есть ресурсы — прошу отрисовать и вылизать макет по всем правилам конкретно под данное устройство.