Опубликовано 

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

Существует множество подходов для вёрстки современных сайтов, а также их вариации. Подробнее об этом можно почитать в статье MDN. Подытожу, что всё сводится к использованию брейкпоинтов через медиавыражения и резиновому лейауту, а также их комбинации.

Однако существуют конкретные практические подходы для достижения адаптивности у фронтендеров, и чаще всего это css-фреймворки, либо же устоявшиеся техники.

Проблематика при проектировании таких систем кроется в том, что у каждого разработчика есть своё виденье адаптивности, когда дело доходит до переноса дизайна из картинки на устройство. Разработчику дают макет, состоящий из страниц сайта. Каждая страница скорее всего имеет десктопную версию и мобильную. Допустим, 1440px и 375px соответственно.

Изображение: Пример дизайна одной страницы сайта в FigmaПример дизайна одной страницы сайта в Figma

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

Дальше перед разработчиком встаёт дилемма, как макет должен отображаться под при изменении разрешения. Кто-то использует брейкпоинты, кто-то резинит макет, кто-то и то, и другое. Кто-то делает mobile first, кто-то предпочитает показать сначала готовую десктопную версию менеджеру и затем возиться с мобилкой.

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

Принципы

Deskbile first

Мобильные устройства это обыденность, они есть у любого. Мобильный трафик на сайтах составил 60% в 2022 году и его доля будет только увеличиваться.

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

Резиновый > отзывчивый

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

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

Например, самый популярный css фреймворк Tailwind работает с фиксированными размерами по брейкпоинтам с использованием единицы измерения rem. Подход, который я презентую, будет несколько другим и базируется именно на резиновости нежели чем на отзывчивости.

Изображение: Поиск по ключевому слову «fluid» на сайте css-фреймворка TailwindПоиск по ключевому слову «fluid» на сайте css-фреймворка Tailwind

Rem hell

Прекрасная статья «Don’t use rem/em for paddings, margins and more» расскажет вам, почему rem для отступов и всего прочего это плохо, какие проблемы с адаптивностью и доступностью там есть. Я лишь дополню, что работать с rem крайне неудобно, ты постоянно обязан что-то высчитывать, переводить пиксели из дизайна в rem. Tailwind, о котором я говорил ранее, использует базовые 16px для html, которые затем высчитывать бывает проблематично и не думаю, что кто-то радовался от этого процесса. А смена базы на 10px для html несёт другие свои проблемы для доступности. Поэтому rem я не использую.

Изображение: Вы точно не получите удовольствие от адаптации стилей и заучивании значенийВы точно не получите удовольствие от адаптации стилей и заучивании значений

Реализация

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

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

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

        clamp({мин. значение}, {вычисляемая функция}, {макс. значение})

      

Минимальное и максимальное значение — это ограничитель для вычисляемой функции.

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

Как подобрать эту вычисляемую функцию? Посмотрим сначала на графический смысл, как это будет работать:

Изображение: Уравнение прямой с угловым коэффициентом для поиска fluid значения по viewport’уУравнение прямой с угловым коэффициентом для поиска fluid значения по viewport’у

Всё предельно просто. Ищем функцию от 14px до 22px при изменениях экрана с 375px до 1440px. Обычное уравнение прямой с угловым коэффициентом имеет вид . Как найти k? Как найти b?

Для этого легче всего воспользоваться каноническим уравнением прямой:

Выражаем :

Подставляем наши значения:

Вычисляем:

Вычисляем ещё и получаем то, что можно использовать в css calc()

Собственно, это и есть искомое уравнение вида . Но что здесь будет ? Нам ведь нужна зависимость от вьюпорта? Так как вьюпорт по умолчанию поделён на 100 (1vw = 1% от ширины окна), значит нам надо умножить на 100. Получаем:

        font-size: calc(0.75vw + 11.18px);

      

Используем функцию clamp (calc можно опустить внутри неё):

        font-size: clamp(14px, 0.75vw + 11.18px, 22px);

      

Супер. Теперь у нас есть резиновая типография, которая резинится от 14px до 22px при разрешениях 375px до 1440px.

Но я говорил про адаптивный дизайн, который будет смотреться прекрасно для всех устройств и размеров экрана. Сейчас, если ты верстаешь в rem или px (по сути то же самое), ты получаешь дизайн, который поддерживается до того значения, которое ты проконтролировал. Как правило, это дизайнерские 1440px. Иногда макет дизайнят до 1920px.

Так в чём проблема? Вот в этом:

Изображение: «Вот как YouTube хочет, чтобы я потреблял контент на своем 32-дюймовом мониторе 4k»
[@TylerGlaiel на сайте twitter.com]«Вот как YouTube хочет, чтобы я потреблял контент на своем 32-дюймовом мониторе 4k» [@TylerGlaiel на сайте twitter.com]

Проблема в том, что в мире существуют мониторы больше 1920px. Откуда дизайнеры берут 1440px? Я думаю, что из Figma.

Изображение: Desktop пресеты размеров экранов в Figma при создании нового фреймаDesktop пресеты размеров экранов в Figma при создании нового фрейма

Забавно тут то, что другие типы пресетов, в точности — мобильные, Figma обновляет оперативно. Но для обычного Desktop они выбирают 1440px. Что странно, ведь глобальная доля распространения экранов 1920px составляет наибольшие 23.22%, а 1440px — 5.93%.

Поэтому дизайнить макеты надо до 1920px, а Figma, скорее всего, будет обновлять в скором времени свои пресеты.

Пользователи активно переходят на мониторы и телевизоры 4К. Уже сейчас есть мониторы 5К и 6К, и будут ещё больше. Наивно предполагать, что прогресс остановится на 1920px мониторах будучи дизайнером интерфейсов и сайтов. С телевизоров смотрят сайты через браузеры Smart TV и приставки (xbox, steam deck). Вы действительно хотите забить на эти устройства и говорите себе «мне всё равно, что будет с сайтом на экранах больше 1920px»?

Вот ещё несколько примеров сайтов, которые не адаптируются под Apple Studio Display 27″ 5K и его нативное разрешение 5120×2880. Можете открыть во весь экран и посмотреть, удобно ли пользователям будет работать с таким интерфейсом 😅

Изображение: instagram.cominstagram.com

Изображение: google.comgoogle.com

Изображение: twitter.comtwitter.com

Но на самом деле не всё так плохо. По умолчанию 4К телевизоры, в том числе и UltraWide, устанавливают масштаб всего интерфейса системы на 130%. Можно и самому поставить больше, и всё в системе подстроится к масштабу, в том числе и браузер. А Smart TV сами устанавливают масштаб браузера побольше, как раз по этой причине.

Однако пользователь может хотеть поставить масштаб 100% или 120% для системы, но при таком масштабе в браузере всё будет мелким. Так что в этом плане это не вы решаете как будет выглядеть ваш сайт, а система пользователя (даже не он сам).

А ведь будут ещё другие устройства, VR-гарнитуры! Умные часы становятся умнее, их экран становится детализированнее и больше! И кто знает, где сайты ещё будут просматриваться.

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

Так как дизайнер верстает макет до 1920px и никто не будет его продумывать на разрешениях 4К или выше, то можно ведь просто скейлить размеры дизайна, взятые на макете 1920px до любого другого большего разрешения. Как это сделать?

Изображение: Обновлённый графический смыслОбновлённый графический смысл

Синяя линия показывает графический смысл адаптивности, который можно использовать на разрешениях более 1440px (в наших примерах 1440px, но как я сказал, правильнее верстать макеты сайтов сразу до 1920px).

Всё то же самое, что и с каноническим уравнением, но мы опускаем max значение для clamp, а после 1920px брейкпоинта всё должно скейлится равномерно, чтобы на 2К и на 5К сайт выглядел одинаково, как на разрешении 1920px.

Ищем значение прямой, проходящей через точку и точку . Через начало координат прямая пропускается по той причине, что если этого не сделать и, например, дальше использовать на отрезке функцию calc(0.75vw + 11.18px), то скейлинг размеров будет происходить неравномерно.

Подставляем наши значения:

Получаем простое значение для нашего свойства:

        font-size: 1.5277vw

      

Помните, что мы используем это значение для ширины экрана > 1440px.

Теперь, если вы изменяете разрешение экрана с 1440px на 4К или 5К, да хоть 400К, всё будет смотреться прекрасно.

Изображение: Переключаем туда-сюда и смотрим, что масштаб текста и отступов сохраняется — gifПереключаем туда-сюда и смотрим, что масштаб текста и отступов сохраняется — gif

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

Мы абстрагируемся от того, какой dpi у пользователя, какой масштаб установлен у него в системе, сколько дюймов у него экран — всё будет выглядеть именно так, как в дизайне.

Полностью резиновый дизайн

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

Остаётся только применить этот подход ко всем размерам, отступам, текстам и получить полностью резиновый дизайн для всех устройств и всех экранов.

Доступность и костыли

У подхода размеров по vw есть одна существенная проблема: доступность. Вернее, её нет вообще. Мы не можем корректно менять масштаб страницы в браузерах привычными способами (ctrl + +), а ведь это входит в требование WCAG 2.1 Resize text — Level AA.

Подробно исследование этой проблемы написана в статье «Responsive Type and Zoom». Изначально опубликована в декабре 2019 и её дополнение ведётся по сей день!

Коротко говоря, использование vw в fluid-системе дизайна сайта всегда проваливает проверку WCAG 2.1 Resize text — Level AA, потому что если размер текста составляет 50px на 100% увеличении, то при 200% увеличении он не будет равен 100px, хотя и должен бы.

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

Изображение: Пример вёрстки настроек сайтаПример вёрстки настроек сайта

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

Как правило, пользователь устанавливает эти настройки один раз и навсегда.

Вот мой sass-миксин, которым я пользуюсь, когда определяю fluid-систему:

fluid-typo
.sass
        @use 'sass:math'
@use '@sass-collective/strip-unit'

@mixin fluid-typo($property, $font-max, $font-min, $screen-max: 1920, $screen-min: 320)
    $font-max: strip-unit.strip($font-max)
    $font-min: strip-unit.strip($font-min)

    $k: math.div($font-max - $font-min, $screen-max - $screen-min)
    #{$property}: calc((#{$k * 100}vw + #{$font-min - $k * $screen-min}px) * var(--font-scale, 1) * var(--site-scale, 1))

    @media (max-width: #{$screen-min}px)
        #{$property}: calc(#{$font-min}px * var(--font-scale, 1) * var(--site-scale, 1))

    @media (min-width: #{$screen-max}px)
        #{$property}: calc(#{math.div($font-max, $screen-max) * 100}vw * var(--font-scale, 1) * var(--site-scale, 1))

      

И использовать его можно таким образом:

        h1
  @include fluid-typo('font-size', 48px, 30px)

      

В css переменных font-scale и site-scale, которые применяются к тегу html указываются целые числа: 1, 1.2, 1.4, на которые умножается значение свойства.

К сожалению, браузеры не поддерживают отслеживание изменения масштаба по событиям и мы не можем это контролировать. Поэтому собственные настройки сайта/приложения являются приоритетным вариантом при использовании vw единиц, если вы думаете о доступности.

Можно подписаться на событие нажатия ctrl + и ctrl mouseUp, чтобы показывать модальное окно с настройками.

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

Изображение: Готовый результат главной страницы моего сайта, который резинется абсолютно на всех разрешениях — gifГотовый результат главной страницы моего сайта, который резинется абсолютно на всех разрешениях — gif

Спасибо за внимание!