Зачастую, когда говорят об адаптиве, подразумевают сужение большого экрана до мобильного размера без потери функциональности. Однако, на самом деле, адаптив также существует между разными десктопными версиями. Обычно разработчики не очень стараются применять адаптивные подходы для больших экранов, ведь если интерфейс поместился на HD разрешении, то он точно поместится на 2K разрешении и больше. Однако контент никак не масштабируется, поэтому имеем на больших разрешениях маленькие элементы, которые трудно уловить взглядом. В этой статье я покажу, как сделать интерфейсы пропорционально одинаковыми на разных десктопных разрешениях.

Проблема

Давайте сделаем совсем примерную верстку онлайн-кинотеатра Кекфликс. Не буду вдаваться в подробности, как именно это сверстано, тут нет особых сложностей, я детально буду рассматривать лишь стили. Сначала необходимо договориться о некоторых нерушимых постулатах:

  • Базовый размер шрифта равен 16px.

  • Эталонный размер десктопного экрана 1600х900. Это означает, что мы будем стараться делать другие десктопные экраны максимально пропорционально похожими на экран 1600х900.

Вот что у нас получилось:

Базовая верстка. Экран 1600х900
Базовая верстка. Экран 1600х900

Полный скриншот страницы выглядит так:

Полный скриншот базовой верстки. Экран 1600х900
Полный скриншот базовой верстки. Экран 1600х900

В данной верстке у нас все задано в пикселях: размер шрифта, размер скруглений, ширины, высоты и другое. Именно поэтому данный интерфейс будет выглядеть следующим образом на разрешении iMac 2560х1664:

Базовая верстка. Экран 2560x1664
Базовая верстка. Экран 2560x1664

Интерфейс никак не заскейлился: например, как был размер шрифта у названия фильма 16px на экране 1600х900, так и остался. Стало вмещаться больше контента, вследствие чего глазу очень тяжело остановиться на чем-то одном, а мышкой тяжело дотянуться до маленьких элементов. Как выглядит данный интерфейс на 4K разрешении можете предугадать: все заметно уменьшилось.

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

Базовая верстка с боковыми распорками. Экран 2560х1660
Базовая верстка с боковыми распорками. Экран 2560х1660

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

Наивное решение

Самый простой способ заскейлить интерфейс — это прописать размеры относительно базовой величины 1rem, которую впоследствии заадаптивить. Можно также использовать величину em, но будьте аккуратны, ведь это значение высчитывается от размера шрифта родителя, поэтому могут быть проблемы при наследовании стилей у вложенных элементов. Величина rem фиксирована размером шрифта, указанном на теге html. По умолчанию во всех современных браузерах 1rem=16px, что нам подходит согласно нашим постулатам.

html
	// ... some styles ...
	font-size: 16px
	// ... some styles ...

Чтобы нам проще было переписать стили относительно базовой величины, создадим вспомогательную SASS функцию, которая будет переводить количество пикселей в rem-ы:

@use 'sass:math'
@function rem(value, $delimeter) * 1rem

Таким образом, например, стили красного квадрата размером 200х200 (width: 200px; height: 200px) перепишутся следующим образом:

.box
	width: rem(200)
	height: rem(200)
	background-color: red

Что после компиляции примет вид:

.box {
	width: 12.5rem;
	height: 12.5rem;
	background-color: red;
}

После того как мы перевели все пиксели на rem, необходимо приступать к адаптиву этой величины. Мы бы могли наивно написать следующее медиа-выражение, которое пропорционально экрану 1600x900 увеличит базовую величину 1rem=16px до значения 1rem=19.2px для разрешения 1920х1080.

@media (min-width: 1920px)
	html
		font-size: 19.2px

Тогда получим, что на экране шириной 1920 действительно все пропорционально увеличилось, но на всех разрешениях до величины 1920 не было никаких изменений. Это видно, если ресайзить окно:

Базовая верстка. Ресайз окна с 1600 до 1920 по ширине
Базовая верстка. Ресайз окна с 1600 до 1920 по ширине

Это уже вариант, который можно развивать. Мы бы могли наплодить брейкпоинтов, на каждый из которых повесить свои медиа-выражения. Но такой подход далек от идеала, ведь хочется получить сразу готовый результат одной строчкой. Перед тем как показать такой вариант решения, необходимо немного раскрыть тему CSS шлюзов.

Миксин адаптивности через CSS шлюзы

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

Давайте попробуем создать СSS шлюз для изменения базовой величины rem. То есть создадим шлюз для управления размером шрифта для тега html. Согласно нашим постулатам мы договорились, что у нас базовый шрифт равен 16px, а базовый экран имеет разрешение 1600х900. Стоит отметить, что мы будем добавлять резиновости нашему интерфейсу лишь по ширине экрана, но при необходимости такие же манипуляции можно сделать и с высотой.

Если для ширины 1600 мы имеем размер шрифта 16px, то прибегнув к несложным математическим операциям можно выяснить, что для ширины экрана 1920 размер шрифта должен составлять 19.2px. Это можно увидеть на графике для прямой y=0.01x, где по оси X у нас находятся ширины экранов, а по оси Y размер шрифта:

График функци y=0.01x
График функци y=0.01x

Давайте абстрагируемся от конкретных значений и построим общую прямую по двум точкам (x1, y1) и (x2, y2) согласно линейной функции y=kx+b, где k — это наклон функции, а b — смещение по оси ординат. Коэффициент k вычисляется как первая производная функции (или как тангенс угла наклона, если так удобнее):

k = \frac{(y_2 - y_1)}{(x_2-x_1)}.

Коэффициент b можно вычислить, если подставить любую точку, например, (x1, y1) и найденный коэффициент k. Не буду тратить ваше время на математические манипуляции и покажу сразу исходный вид нашей функции, построенной для любых двух точек:

y = \frac{y_2 - y_1}{x_2 - x_1}(x - x_1) + y_1.

Давайте преобразуем это в SASS функцию: y — это то, что мы ищем, поэтому воспользуемся функцией calc(), а x заменим на ширину всего вьюпорта 100vw:

@function calc-between-width($width-start, $value-start, $width-end, $value-end)
  @return calc(#{$value-start} * 1px + (#{$value-end} - #{$value-start}) * (100vw - #{$width-start} * 1px) / (#{$width-end} - #{$width-start}))

Получили SASS функцию calc-between-width, которая выставит пропорциональное значение в пикселях любому CSS свойству в зависимости от переданных величин в промежутке указанных ширин.

Время магии

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

html 
	font-size: calc-between-width(1600, 16, 1920, 19.2)

Тогда получим желанный результат: все десктопные экраны стали выглядеть одинаково согласно нашему базовому разрешению 1600х900:

Верстка через CSS шлюзы. Экран 1920х1080
Верстка через CSS шлюзы. Экран 1920х1080
Верстка через CSS шлюзы. Экран 2560х1664
Верстка через CSS шлюзы. Экран 2560х1664

А вот как это выглядит в динамике:

Базовая верстка через CSS шлюзы. Ресайз окна с 1600 до 1920 по ширине
Базовая верстка через CSS шлюзы. Ресайз окна с 1600 до 1920 по ширине

Заключение

Если не забывать думать о больших экранах пользователей, то можно сделать интерфейс одинаковым для любого десктопного разрешения через CSS шлюзы. Это позволит правильно масштабировать сайт, что сделает его похожим на полноценное веб-приложение.

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

В данной статье мы адаптивили CSS свойство font-size, что позволило добавить нам резиновость для всех разрешений. Но мы сделали это самым простым способом через линейную функцию. Однако нам никто не запрещает использовать другие виды функций. Например, при необходимости можно построить кусочно-линейную функцию, которая будет увеличивать размер шрифта от ширины экрана 1600 до 1800, далее уменьшать размер шрифта до ширины 2000 и после снова увеличивать. Или можно написать новую SASS функцию, которая будет вычислять значение согласно квадратичной функции. К тому же, функция calc-between-width применятся к любому CSS свойству, которое может содержать пиксели. Поэтому вы можете, таким же образом через CSS шлюзы добавлять резиновости любым таким свойствам. К примеру, вы можете резиново манипулировать скруглениями через border-radius. Вы ограничены лишь своей фантазией и возможностями браузера!


Ссылка на github.

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


  1. HawkeyePierce89
    23.11.2022 11:42
    +5

    Люто бесили такие фичи на сайтах, когда был моник 2k+. Почему-то вдруг менеджеры проекта решают, что если у меня большой монитор, то я слепой и мне надо большие элементы выводить на экран.

    Самое смешное что зазумить это не получалось: размеры оставались такими же даже если поставить масштаб в 50%.

    Спасибо разработчикам что хотя бы не запрещают менять размеры окна браузера


    1. mishqua Автор
      23.11.2022 11:49
      +3

      Это дело вкуса. Меня, например, бесят большие боковые распорки, которые светят белым светом и выжигают глаза)

      Я продемонстрировал подход, который помогает резиново масштабировать интерфейс. Причем функция адаптивности может быть любой, это настраиваемый момент. То есть если уменьшить наклон прямой, то можно масштабировать не совсем пропорционально и не так сильно. Я хочу сказать, что это можно отладить/подкрутить и сделать интерфейс адекватным на очень больших разрешениях.


  1. consalt
    23.11.2022 13:03
    +2

    Если я покупаю ноут 14 1920×1080, то я не хочу, чтобы дизайнеры его превращали в аналог 1024×768. Чем больше инфы вмещается на одном экране, тем лучше. Не надо растягивать плиточки в разы. У вас ошибка в рассуждениях «вследствие чего глазу очень тяжело остановиться на чем-то одном, а мышкой тяжело дотянуться до маленьких элементов»


    1. mishqua Автор
      23.11.2022 13:11

      Не вижу никакой ошибки. Вы можете как эталонное разрешение взять 1920х1080 и поддержать резиново только бОльшие разрешения. Для меньших экранов - не использовать данный функционал.

      Когда я говорил «вследствие чего глазу очень тяжело остановиться на чем-то одном, а мышкой тяжело дотянуться до маленьких элементов», я имел ввиду действительно большие десктопные экраны 2k и больше.


      1. Denai
        23.11.2022 13:21
        +3

        Так ваш сайт выглядит на моём мониторе

        Мышкой то мне дотянуться до элементов легко. Но их 4 с половиной на экране 5120х1440

        Если проскроллить вниз, то и вовсе 3 карточки на весь экран


        1. mishqua Автор
          23.11.2022 13:27
          +1

          Я для примера сделал резиновость по линейной функции, никак не ограничивая эту функциональность. В продакшине, конечно, так делать не стоит. В самом простом случае, эту функциональность, надо, ограничивать через медиа-выражения, как минимум, до планшетных разрешений. Также через медиа-выражения вы можете менять наклон этой прямой на промежуточных брейкпоинтах.

          Статья для примера применения резиновости. На каких разрешениях и как добавлять резиновость -- дело за вами!


  1. apxi
    23.11.2022 13:17
    -2

    Охренеть открытие сделали! Увеличение размера элементов пропорционально размерам экрана!


  1. Denai
    23.11.2022 13:31
    +4

    демка одной страницы на 13К файлов, которая ещё и просит 60+ пакетов в сисему установить - моё почтение