Сегодня построение отзывчивых макетов уже не основывается на контрольных точках (breakpoints) с фиксированной шириной. Вместо этого современные макеты должны работать на устройствах практически любого размера. Однако, к своему удивлению, я всё ещё встречаю сайты, где используется паттерн отзывчивого дизайна – когда присутствует контейнер, получающий новое значение max-width в соответствии с шириной области просмотра.

Термин «отзывчивый» сегодня отражает уже очень многое. У нас есть медиа-запросы, которые проверяют пользовательские настройки, а также современные возможности CSS, которые помогают создавать отзывчивые макеты вообще без использования медиа-запросов. Отзывчивость нынче изменилась, и мы живём в поистине прекрасное время.

Содержание



Введение


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

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

Рассмотрите следующий дизайн. Здесь у нас типичный макет, который нужно сделать отзывчивым.



Обычный алгоритм действий дизайнера будет такой:

  • обернуть hero-изображение с переносом на новую строку;
  • уменьшить размер шрифта;
  • составить карточки столбцом.



Готово! Это отзывчивый дизайн. Хотелось бы мне, чтобы всё было столь просто, но здесь необходимо учитывать многие нюансы. Если я буду рассматривать десктопный дизайн, то у меня возникнет немало вопросов:

  • Когда нужно обёртывать hero-элементы (содержание и изображение) с переносом на новую строку?
  • Размер шрифта плавающий, или же это просто фиксированное значение, изменяемое вручную?
  • Нужно ли нам использовать отзывчивые изображения?
  • Нужны ли нам плавающие интервалы между элементами?
  • Расстановка карточек:
    • Существуют ли отдельные вариации карточек, которые должны отображаться для десктопных и мобильных устройств?
    • Имеет ли изображение карточки конкретное соотношение сторон?
  • Пользовательские настройки: присутствуют ли в UI детали, которые будут изменяться в зависимости от пользователя – темизация/цветовая схема, установки reduced motion и т.д.



Рассматривая этот пример, можно последовать двум разным подходам.

▍ С помощью современного CSS


  • Типографика адаптируется под ширину окна просмотра с помощью функции clamp();
  • Интервалы адаптируются под ширину окна просмотра с помощью функции clamp();
  • Hero-раздел адаптируется под содержание посредством обёртывания флекс-элементов;
  • Сетка карточек адаптируется под доступное пространство с помощью minimax() без медиа-запросов;
  • Компонент карточки адаптируется под свою обёртку при помощи запросов размера контейнера и стиля контейнера;
  • Поля (margins) и отступы (paddings) адаптируются под язык сайтов с помощью логических свойств.

▍ С помощью медиа-запросов


  • Навигация по сайту адаптируется под ширину области просмотра;
  • Темизация адаптируется под настройки пользователя в его операционной системе;
  • Эффект наведения на карточку адаптируется в соответствии с используемым устройством ввода (мышь или сенсор).

В списке выше темизация и навигация выполняются с помощью медиа-запросов. Остальное реализуется посредством возможностей CSS вроде функции сравнения clamp() и запросов к контейнерам.

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

Отзывчивый дизайн больше не опирается на медиа-запросы.

К сожалению, среди разработчиков ещё бытует тип мышления, который мне кажется ошибочным. Многие новички считают, что для построения отзывчивого сайта нужно использовать тот или иной фреймворк. Однажды мне довелось спорить с клиентом о том, что для построения отзывчивых сайтов нам не нужен фреймворк Х. Я сказал ему, что можно использовать медиа-запросы CSS, поскольку они и являются составляющими элементами упомянутого фреймворка.

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

Отзывчивый дизайн с течением времени


Мой путь в веб-разработке начался с середины 2014 года. В то время отзывчивый дизайн являлся очень актуальной темой, и о нём говорили повсюду.

▍ Фреймворк Bootstrap


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

▍ Медиа-запросы


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

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

▍ Мышление на основе контрольных точек с фиксированной шириной


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

Один из моментов, который мне лично не нравится, связан с использованием фиксированной ширины для контейнера, которая изменяется в зависимости от ширины области просмотра.

@media (min-width: 576px) {
  .container {
    max-width: 540px;
  }
}

@media (min-width: 768px) {
  .container {
    max-width: 720px;
  }
}

@media (min-width: 992px) {
  .container {
    max-width: 960px;
  }
}

@media (min-width: 1200px) {
  .container {
    max-width: 1140px;
  }
}

Такой вариант всегда будет вести к ограничению доступного для .container пространства.

Взгляните на следующее изображение:



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

max-width нам потребуется, только чтобы избежать получения на широких экранах очень большого контейнера.

@media (min-width: 1200px) {
  .container {
    max-width: 1140px;
  }
}



Далее я покажу более подробный пример этой проблемы.

Предположим, у нас есть сетка карточек. В первом случае они находятся в .container, который многократно изменяет свою max-width.



В планшетном представлении контейнер будет ограничен собственной max-width, оставляя немало пустого пространства.



Обратите внимание на пустое пространство с обеих сторон. Не будет ли лучше использовать его для контейнера? Представим, что просмотр происходит на планшете.

Если рассматривать этот дизайн на ещё меньшей области просмотра, в нём по-прежнему сохраняются пустые зазоры по сторонам.



Можно ли ситуацию улучшить? Всё же так мы впустую теряем немало пространства. Лично я не могу придумать, ради чего так можно делать в 2023 году.

Удаление max-width для меньших областей просмотра приведёт к тому, что контейнер будет занимать всю ширину области просмотра.



И для ещё меньших размеров:



Отзывчивый дизайн и скучные сайты


Нельзя игнорировать тот факт, что отзывчивый дизайн так или иначе связан с созданием скучных однообразных сайтов. Это касается построения тем на основе шаблона из Bootstrap.

В интернете стало появляться всё больше похожих сайтов. В начале 2016 года мне попался твит, в котором автор рассуждал о тогдашнем «отзывчивом дизайне». На мой взгляд, всё дело в популярности CSS Bootstrap.

Какой из этих двух макетов сайтов вы сейчас разрабатываете?



Забавно, но правдиво, не так ли?

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

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

Предлагаю взглянуть на отзывчивый дизайн под другим углом.

▍ Веб-среда отзывчива по умолчанию


Начнём с главного. Лично я считаю, что веб-среда отзывчива по умолчанию. Имеется ввиду, что добавление кучи HTML-элементов без какого-либо CSS работает на экране любого размера.

Вот пример добавления заголовка, абзаца и списка.



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

body {
    display: grid;
    grid-template-columns: 1fr 2fr;
    grid-gap: 1rem;
}

ul {
    grid-column: 2/3;
    padding-left: 1rem;
}



На картинке выше выглядит неплохо, не так ли? Самое же интересное начинается, когда мы уменьшаем размер.



Значит, веб-среда отзывчива по умолчанию, пока мы не начинаем применять к нашим макетам креативность.

Отзывчивый дизайн в 2023


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

▍ Отзывчивость к содержанию


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



▍ Отзывчивость к области просмотра


Должен ли компонент работать, отталкиваясь только от области просмотра? Это может касаться шапки сайта, футера и полноразмерных разделов. Они должны подстраиваться под размер области просмотра.



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

@media (min-height: 700px) {
  .site-header {
    /* position: fixed или position: sticky */
  }
}



▍ Отзывчивость к контейнеру


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



▍ Отзывчивость к пользовательским настройкам


Должен ли компонент меняться на основе конкретных установок пользователя? Например, при изменении темы, размера шрифта, контраста и прочих параметров.



Каким я вижу отзывчивый дизайн сейчас


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

  • запросы к контейнерам;
  • обёртывание;
  • размеры элементов;
  • размеры шрифтов;
  • интервалы;
  • доступное пространство;
  • логические свойства.

С помощью функционала CSS вроде flexbox, сетки и функции сравнения clamp() можно проинструктировать браузер о том, что и в каких ситуациях делать. Нам не нужно вручную прорабатывать в дизайне каждую деталь.



Создавая компонент, я предпочитаю ориентироваться на подвижность. Разберём пример.

Простой пример


Современный CSS даёт возможность писать отзывчивые стили, не опираясь исключительно на медиа-запросы. К примеру, свойство flex-wrap при наличии достаточного пространства позволяет обёртывать вместе родственные элементы.

.reaction-button {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}



Здесь есть три приятных детали:

  1. Оно производит обёртывание на основе условия. Не хватает пространства? Хорошо, помещаем элементы в обёртку.
  2. Сохраняет центрированность как в горизонтальных, так и в вертикальных стилях.
  3. В свою очередь, свойство gap (разрыв) работает по ситуации. Если элементы расположены горизонтально, оно действует только для строк. В случае же расположения элементов столбиком разрывы делаются вертикально.



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

Современные способы создания отзывчивых макетов


Сегодня CSS становится очень мощным. У нас есть отличная поддержка переменных CSS, flexbox и сетки. В новейших версиях браузеров поддерживаются почти все последние функции вроде селектора :has и запросов к контейнерам.

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

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

▍ CSS Flexbox


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

Это мощная возможность, которая помогает при создании основы отзывчивых компонентов.

▍ Создание макета компонента article

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



Как и в предыдущем примере, flexbox отлично подойдёт здесь в качестве основы. Предполагая, что я написал базовую стилизацию для заголовка, изображения, отступов и т.д., можно получить что-то вроде такого:



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

.c-card {
  display: flex;
}



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

.c-card {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
}

Мы вернулись к изначальному результату. Ничего, сейчас исправим.



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

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

.c-article__thumb {
  flex: 1 1 550px;
}

.c-article__content {
  flex: 1 1 350px;
}

Идея в том, что при помощи flex можно позволить элементам увеличиваться или уменьшаться на основе доступного пространства.

ishadeed.com/assets/responsive-design/rwd-card-resize-1.mp4
Прим. пер.: Для просмотра видео скачайте его через контекстное меню.
Да, в оригинале медиаэлемент также недоступен.

Видите? Отзывчивый дизайн больше не опирается на медиа-запросы.

.c-card {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
}

.c-article__thumb {
  flex: 1 1 550px;
}

.c-article__content {
  flex: 1 1 350px;
}



Также можно использовать wrap-reverse для обращения порядка размещения изображения и содержания.

.c-card {
  display: flex;
  flex-wrap: wrap-reverse;
  align-items: flex-start;
}



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

▍ Шапка раздела

Крис Куайе назвал это в своей прекрасной статье «выравниванием смещающейся обёртки».

Взгляните на следующий пример, вдохновлённый статьёй Криса.



Здесь у нас раздел с шапкой, в которой находится заголовок и ссылка.

В случае недостатка пространства нам нужно, чтобы заголовок обёртывался с переносом на новую строку. Вот всё, что для этого потребуется:

.section-header {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.section-header__title {
  flex: 1 1 400px;
}

Значение 400px для заголовка является кастомной контрольной точкой, которая обусловит выполнение обёртывания. В случае, когда заголовок имеет размер 400px или меньше, он будет обёртываться с переносом на новую строку.

С помощью медиа-запроса это можно было бы реализовать так:

@media (min-width: 650px) {
  .section-header {
    display: flex;
    /* Обёртывание не требуется */
  }
}

Такой вариант будет прекрасно работать, пока нам не потребуется использовать шапку раздела в других обёртках, например в основном разделе и в сноске.

В случае же flex-wrap шапка раздела будет работать, даже при использовании в небольших контейнерах вроде сносок.



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



В случае же медиа-запросов для размещения шапки раздела в элементе сноски нам придётся использовать отдельный класс.

@media (min-width: 800px) {
  .section-header--aside {
    display: flex;
    /* Обёртывание не требуется */
  }
}

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

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

▍ CSS Grid Layout


Сегодня мы можем создавать широко настраиваемые Grid layout. Я не буду раскрывать тему CSS Grid подробно, так как она потянет на целую книгу, но всё же расскажу о некоторых вещах, которые поддерживаются всеми современными браузерами.

Рассмотрим следующий пример.



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

<div class="c-newspaper">
  <article class="c-article c-article--1"></article>
  <article class="c-article c-article--2"></article>
  <article class="c-article c-article--featured"></article>
  <article class="c-article c-article--3"></article>
  <article class="c-article c-article--4"></article>
  <article class="c-article c-article--5"></article>
  <article class="c-article c-article--6"></article>
  <article class="c-article c-article--7"></article>
</div>

.c-newspaper {
  display: grid;
  grid-template-columns: 0.2fr 0.6fr 0.2fr;
  grid-template-areas:
    "item-1 featured item-2"
    "item-3 featured item-4"
    "item-5 item-6 item-7";
  grid-gap: 1rem;
}

.c-article--1 {
  grid-area: item-1;
}

.c-article--2 {
  grid-area: item-2;
}

/*..И так далее для других элементов.. у каждого есть своя область сетки..*/

.c-article--7 {
  grid-area: item-7;
}

.c-article--featured {
  grid-area: featured;
}



Во второй версии достаточно изменить области шаблона.

.c-newspaper.variation-1 {
  grid-template-areas:
    "featured featured item-3"
    "item-1 item-2 item-4"
    "item-5 item-6 item-7";
}



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

Ещё одной полезной возможностью CSS Grid является функция minimax(). Если коротко, то она позволяет создавать сетку, которая динамически изменяет ширину столбцов без использования медиа-запросов.

Рассмотрим следующий код CSS.

.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
  grid-gap: 1rem;
}

Здесь у нас сетка, состоящая из трёх столбцов. Нам нужно, чтобы они изменяли размер, когда уменьшается область просмотра. Для этого идеально подойдёт функция minimax(), совмещённая с auto-fill.

ishadeed.com/assets/responsive-design/minmax-resizing.mp4
Для просмотра видео скачайте его через контекстное меню.

Без minimax() у нас не останется вариантов, кроме как изменять столбцы в соответствии с шириной области просмотра при помощи медиа-запросов.

Простой пример:

@media (min-width: 992px)
    .wrapper__item {
        width: 33%;
    }
}

Более подробно почитать о minimax() можете в моей статье «A deep dive into CSS Grid minimax()».

▍ Плавающий размер


Один из моих любимых аспектов современного отзывчивого дизайна – это создание подвижных (fluid) макетов. Поначалу этого можно было добиться, используя единицы измерения областей просмотра, но это было не идеальное решение. Размер шрифта на больших экранах мог становиться огромным, поэтому нам требовался способ установки ограничения.

h2 {
  font-size: calc(1rem + 5vw);
}

/* Если ширина области просмотра составляет 2000px или больше, ограничить
* размер шрифта до 4rem.
*/
@media (min-width: 2000px) {
  font-size: 4rem;
}

Около трёх лет назад в CSS появилась поддержка функций сравнения. Эта возможность оказалась переломной в построении подвижных макетов без использования медиа-запросов.

Рассмотрим такой пример.

h2 {
  font-size: clamp(1rem, 0.5rem + 2.5vw, 3rem);
}

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



Если же мы захотим реализовать такое с помощью медиа-запросов, то будем вынуждены создать 9 подобных запросов. Совсем не практично. А представьте, что вам нужно проделать это в нескольких местах сайта. Просто кошмар.

Когда-то давно я делал, например, так:

h2 {
  font-size: 1rem;
}

@media (min-width: 800px) {
  h2 {
    font-size: 2.5rem;
  }
}

@media (min-width: 1400px) {
  h2 {
    font-size: 5rem;
  }
}

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



Разберём пару примеров, где плавающий размер показывает себя во всей красе.

▍ Динамические разрывы

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

.wrapper {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: clamp(1rem, 2vw, 24px);
}



Для hero-раздела нам может потребоваться изменять вертикальные отступы, исходя из размера области просмотра. Тут прекрасно подойдёт функция clamp() с единицами измерения этой области.

.hero {
  padding: clamp(2rem, 10vmax, 10rem) 1rem;
}



▍ Запросы размера контейнера


CSS-запросы к контейнеру сейчас поддерживаются всеми браузерами. В Chrome они получили стабильную поддержку в августе 2022 года. Называются эти запросы «Size», поскольку в работе опираются на ширину контейнера, а также в связи с тем, что теперь нам доступны ещё и запросы стиля контейнера.

Это переломный функционал, и моему восторгу от его появления нет предела. Если коротко, то он позволяет запрашивать ширину контейнера компонента.

Взгляните на следующее изображение.



Заметьте, что слева карточки изменяются на основе ширины области просмотра, а справа на основе ширины контейнера.

Разберём несколько примеров.

▍ Компонент article

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

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

Посмотрите демо.

▍ Компонент pagination

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



.wrapper {
  container-type: inline-size;
}

@container (min-width: 250px) {
  .pagination {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .pagination li:not(:last-child) {
    margin-bottom: 0;
  }
}

@container (min-width: 500px) {
  /* Стили при ширине контейнера >= 500px */
}

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

▍ Единицы измерения размеров контейнера в запросе


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

Для этого нужно будет просто заменить vw на cqw.

.c-article__title {
  font-size: clamp(1.25rem, 2.5cqw + 1rem, 2.5rem);
}

.c-article__content > * + * {
  margin-top: clamp(0.8rem, 1cqw + 0.8rem, 1.5rem);
}

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



Очень полезная возможность.



▍ Запросы стиля контейнера


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



▍ Переключение тем на уровне компонентов

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

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



Реализовать это можно так:

.special-wrapper {
  --theme: dark;
  container-name: stats;
}

@container stats style(--theme: dark) {
  .stat {
    /* Добавление тёмных стилей. */
  }
}

Подробнее о запросах стилей читайте в моей статье «CSS Style Queries».

▍ Компонент article

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



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

.o-grid__item {
  container-type: inline-size;
  --horizontal: true;
}

@container (min-width: 400px) and style(--horizontal: true) {
  /* Горизонтальный стиль. */
}

В итоге статья будет изменяться в соответствии с шириной своего контейнера, только если переменная --horizontal будет установлена как true.



▍ Медиа-запросы пользовательских настроек


В качестве примера медиа-запроса пользовательских настроек можно привести проверку установки предпочтительной цветовой схемы.

Этот запрос позволяет адаптировать стили под настройки пользователя.

:root {
  color-scheme: light dark;
}

@media (prefers-color-scheme: dark) {
  /* стили тёмного режима. */
}

Более того, добавление color-scheme приведёт к переключению предустановленной темы элементов управления со светлой на тёмную (поддерживается только в Safari).

▍ Логические свойства


При работе над многоязычными сайтами нам необходима поддержка как макетов LTR (слева направо), так и макетов RTL (справа налево).

Разберём следующий пример.



Здесь у нас компонент, который имеет:

  • отступы (слева и справа);
  • границу слева;
  • поля для иконки.

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

.card {
  padding-inline-start: 2.5rem;
  padding-inline-end: 1rem;
  border-inline-start: 6px solid blue;
}

.card__icon {
  margin-inline-end: 1rem;
}

Подробнее о логических свойствах я рассказывал в статье «Digging into CSS logical properties», а также в обширном руководстве по написанию CSS для сайтов RTL.

▍ Защитный CSS


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

Как раз этой теме посвящён мой проект Defensive CSS. Очень рекомендую с ним ознакомиться.

Заключение


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

Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх

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


  1. Spaceoddity
    00.00.0000 00:00
    +4

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

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

    И на этом все разговоры про отзывчивость заканчиваются. Потому что большинство дизайнеров, я так понял, считают что в мире существуют устройства просмотра с шириной только в диапазоне от 360 (кстати, частенько даже не 320!) до 1920px. И что вся использованная дизайнером растровая графика, каким-то волшебным образом, без потери качества, сама собой экстраполируется под девайсы 8К.

    И когда такой вопрос ставится перед дизайнером ребром - вопрос о необходимости "полного адаптива" как-то вдруг резко становится неактуальным... Ну иногда правда приходится напомнить старую кавказскую притчу о шитье семи шапок из одного куска шкуры.

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


    1. Zavodman
      00.00.0000 00:00
      +1

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


      1. vanxant
        00.00.0000 00:00

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


  1. johnfound
    00.00.0000 00:00
    +1

    веб-среда отзывчива по умолчанию, пока мы не начинаем применять к нашим макетам креативность

    Вот это надо высечь на камне!


  1. vanxant
    00.00.0000 00:00

    Если честно, статья очень слабая, автор практически не знает матчасть. container-fluid в бутстрапе с первой версии, только им мало кто пользуется. Почему? Все тупые? Нет, в европейских языках есть ограничение на длину строки, порядка 60-80 символов. Если строка длиннее, глаз срывается, и читать такую колбасу становится неудобно. При стандартном размере шрифта 16px (это рекомендация ВОЗ для слабовидящих) мы и получаем где-то 1100-1200 пикселей. Именно поэтому стандартный бутстраповский контейнер имеет полезную ширину 1140 пикселей.

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


    1. Singrana
      00.00.0000 00:00

      Вот тут, позвольте добавить один нюанс - так же стандартная ширина 1140 очень бедненько смотрится на мониторах с большим разрешением, и не "кадратным" соотношением сторон. Отдельная боль - широкоформатные мониторы с соотношением сторон 21:9 - и когда вот по середине такая жиденькая колонка, а вокруг одно пустое место


      1. RifleR
        00.00.0000 00:00
        +1

        А что, если у меня монитор 32", то текст в нем должен быть от края до края? Те самые 1000 пикселей(+/-) и есть комфортная длина строки для чтения. Пустое пространство, которое образуется вокруг, должно заполняться или другими колонками или, если сайт построен так, что там нет информации для других колонок, просто не нужно открывать браузер на весь экран.
        Ну уж точно не нужно растягивать текстовое содержимое на все 2000-3000 пикселей ширины монитора.


        1. Singrana
          00.00.0000 00:00

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


          1. vanxant
            00.00.0000 00:00

            ... и вот здесь таки должен пострадать хотя бы один дизайнер, а не освоивший 5% бутстрапа формошлёп. Когда у вас контент - карточки, товаров там или статей, ещё понятно что делать. А вот с текстом хороших решений в общем случае нет.


            1. slammed
              00.00.0000 00:00

              Мысль я вашу понимаю и в целом поддерживаю, но, как человек, прочувствовавший на собственной шкуре масштаб работы, которую нужно проделать, чтобы красиво презентовать руководству компании, например страницу каталога более-менее серьёзного е-коммерса с насыщенной параметрами товарной группой, не говоря уже о маркетплейсах типа условных Озонов/Амазонов, могу вам сказать, что текст — меньшая из проблем, учитывая то, что он достаточно «текучий». Я, естественно, про «резиновый от уха до уха» дизайн с перестройкой всего лейаута и его вложенных частей. Тут нужно быть верстаком, который занимается вёрсткой для души и во славу бога адаптивного дизайна, а не за ради себе поесть и жене сапоги купить.


              1. vanxant
                00.00.0000 00:00

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


                1. slammed
                  00.00.0000 00:00

                  Дык и на флексах-то можно ого-го. Вон, Penpot уже научили flex-wrap'у. В Фигму тоже, думаю, завезут. Вообще, конечно, плохо, что именно толковым дизайнерам, которые всей душой за облегчение жизни фронтам, нужно, как вы выразились «пострадать». На своём веку я разных лентяев на фронтенде повидал — обколются фреймворками до зависимостей, а потом им MUI не кастомайзи (и это под задачу-то), а то бедненькие боятся лишний оверрайд написать... А вот балбесов-дизайнеров, которые грязь в макетах развозят с неймингом типа "Frame 407", например, нужно кошмарить, да.


                  1. vanxant
                    00.00.0000 00:00

                    Не, на флексах как раз плохо получается, если мы говорим именно о десктопах против больших десктопов. Т.е. где надо не просто в кишку вытянуться, а именно разное число колонок с контентом. Вот гриды огонь, вообще думать не надо, если дизайнер нарисовал табличку под каждую ширину. Обозвал ячейки и просто перечислил где какая для каждого разрешения. Ну, людям, которые до сих пор на ХР, конечно печаль.


                    1. slammed
                      00.00.0000 00:00

                      Ну, если мыслить категориями «если дизайнер нарисовал табличку под каждую ширину», то мы вернёмся опять, к тому, что фронты будут синить на дизайнеров при разборе полётов. И тут победят софт-иху-мать-скиллы и прочие уровни интеграции с заказчиком. Как говорил мой коллега, бывший прапорщик: «Нужно, ***ть, сначала определиться с понятиями». Короче, это куча внутренних разборок. Я про то, что дизайнер хорошего уровня может ткнуть фронта носом в гайд, где написаны относительные величины для брейкпоинтов. Причём написаны с предельными значениями в абсолютных величинах для определённых «молекул». И будет прав. Но фронты чаще всего начинают качать права, мол, обижают программистов... За такое в нормальном рабочем коллективе, например, строителей — просто дадут **зды. И поделом, в принципе. Ну, как-то так.


                      1. vanxant
                        00.00.0000 00:00

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

                        Конечно, если проджект — про красивые отчёты и лизинг вышестоящего руководства, тогда ой.


                      1. slammed
                        00.00.0000 00:00

                        Целую ваши мысли :) Видимо я постоянно вписывался в какой-то колхоз, где материализовывалось именно последнее ваше предложение. Что, собственно, подтверждает расхожее утверждение «Быт определяет сознание».

                        P.S.: может с «постоянно» я переборщил, и, дабы не обидеть нормальных пацанов, скажу «в большинстве случаев».


          1. vtal007
            00.00.0000 00:00

            А какими другими? вот возьмите хабр, даже на фуллхд - пустые поля по бокам
            и так делают 99% сайтов
            ну и делать еще 1,2,3,4 сайдбара по разные разрешения, да как-то туда полезную инфу выводить - это надо придумывать. Ну и опять таки, получится, что люди с более узким окном браузера будут получать какую-то другую информацию


            1. Singrana
              00.00.0000 00:00

              Почему другую? Всю туже, только по другому расположенную. Возможно, не все сайты могут в "полный адаптив", но большинство сможет, там где есть что выводить


              1. vtal007
                00.00.0000 00:00

                в смысле по-другому? типа как в газетах - 10 узких колонок? вместо одной области под текст?
                Вот пример у Ведомостей
                https://www.evkova.org/evkovaupload/job/80366/4.jpg
                2 сайдбара, а центральная область поделена на 3 колонки
                То есть в случае хабра, левого сайдбара не будет, будет4 колонки под тектстовую область и имеющийся правый сайдбар
                Ближе наверно вот такая газета
                https://kartinkin.net/uploads/posts/2021-01/1611330095_51-p-fon-sovetskie-gazeti-58.jpg

                Знаю один сайт, который для главной раскидывал блоки в ширину
                https://old.elementy.ru/
                но это ток для главной, для контентных у них уже там все "хреновато"


    1. Spaceoddity
      00.00.0000 00:00

      При стандартном размере шрифта 16px (это рекомендация ВОЗ для слабовидящих) мы и получаем где-то 1100-1200 пикселей.

      А может быть ВОЗ, прежде чем давать рекомендации, перестанет набирать людей с улицы? 16px - это сколько попугаев? Вы же понимаете, что пиксель - это не физическая единица измерения? Что рассматривать её в отрыве от dpi нет смысла? И даже в связке с dpi - у нас всё ещё недостаточно вводных для выводов о "комфортном кегле". Нужно ещё расстояние просмотра, ну или хотя бы опосредованно, через физические размеры дисплея.


      1. vanxant
        00.00.0000 00:00

        Да, все кругом идиоты, вы один тут в белом плаще:)

        На самом деле там довольно длинные лонгриды от ВОЗ, W3C и их адаптация в наших посконных ГОСТах. И покрывают они целую кучу параметров, от размеров мониторов и расстояния от глаз по горизонтали и вертикали и до яркости и контрастности. Ну вот для типичной офисной техники с разрешением 72-96 dpi рекомендуется размер шрифта не менее 16px. И именно это требование веб-мастерам выполнить проще всего, потому как на расстояние до монитора они влиять не могут, а высококонтрастная цветовая схема как правило не дружит с брендбуком заказчика, да и здоровому человеку неудобна.

        Если вам действительно интересно, можете начать с ознакомления с документами WCAG 2.0 и ГОСТ Р 52.872−2012


  1. mobilkip
    00.00.0000 00:00
    +1

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


    1. noodles
      00.00.0000 00:00

      • На старте просишь дизайнера отрисовать только в двух разрешениях - 320px и 1920px.

      • Всё что между - делаешь на своё усмотрение (!). Без глобальных брейкпоинтов, а индивидуально по каждому компоненту.

      • Т.е. делаешь, например, хедер - смотришь в отрыве от всего остального контекста как он себя ведёт на разных разрешениях. Нужно чтоб не ломался и небыл кривым. Этого достаточно. Сам принимаешь решение когда именно спрятать\показать гамбургер меню и т.д.. Условно, ты должен мочь копипастнуть компонент в другой проект без проблем. И такой подход по каждому компоненту (Container Queries сейчас по идее очень облегчают жизнь в этом).

      • Таким образом ты секономишь кучу времени, энергии и нервов и у себя и у дизайнера.

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

      • И не программируешь в стилях! Никаких много этажных миксинов. Везде используй px (!) по максимуму, а еm - только в местах где явно понятно, что расстояние зависит от соседнего текста и это очевидно, и прописывается в одном блоке правил рядом (паддинги у кнопок, иконок, марджины между параграфами)

      • Инедайбог использовать rem - как говорят "лучшие практики" - чтобы удовлетворить юзеров, которые вдруг могут менять размер шрифта в браузере. Не знаю ни однорго, кто знает где эта настройка находится. Все используют или pinch-zoom, или ctrl+, или ctrl+mause-whell-up


      1. Spaceoddity
        00.00.0000 00:00

        сейчас вы просмотрели советы из разряда "как не надо делать"))

        самый простой способ "прокинуть" адаптив между 320 и 1920 - это как раз использовать rem и привязывать его к vw. грубо говоря:

        html{
          font-size: calc(100vw / 1920);
        }

        как видите, дефолтный размер шрифта в браузере элементарно меняется при помощи css))


  1. AlreadyDead
    00.00.0000 00:00

    всегда так верстаю отзывчивость
    всегда так верстаю отзывчивость


  1. hitriymuh
    00.00.0000 00:00

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