[Перевод] Руководство по реализации отзывчивого дизайна в 2023 году

aqigzl7sg2fpfqea_-mzrifvr_4.png


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

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

Содержание


Введение


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

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

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

stdotg0cw-hl2i1mqnezllywd1s.png

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

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


evholuvpqadixholfe3aa1opk5a.png

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

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


vcuvb99zie0mrvu-ybitqvfyoyk.png

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

▍ С помощью современного 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 пространства.

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

cwnoei51ylammphbhen_omrqjvi.png

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

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

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


qbbmibgsqyizkmmh_gxq1h1xsze.png

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

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

ryuowgkwvljjauy9tr0qqfionj8.png

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

jtgjvqhceezdzcgp00r0aoudgyi.png

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

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

2vnmo3xapdefuo8pmmd27tblfw0.png

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

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

ijeo49ytyshddta0wfxijw8p77k.png

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

r5h9e4xoqgcmzvzyl9qhavwt3fk.png

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


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

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

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


a4lq0jmxkkj6i3o85lowncchvyw.png

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

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

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

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

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


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

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

1yavby2xflcvt2tgnhkqrdkj8wk.png

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

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

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


e3ckdmfk6-oahdh0tcd_gywxop4.png

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

-xwqeioc7dpxttwj4nxxrz5yjs4.png

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

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


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

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


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

qw8bbqavgzrmcbn6rd1gme1jp9c.png

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


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

lyr-dpjiuoq2wdh_wjlsyfai5ci.png

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

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


vcebxqg_tcqzgf9rsvrzwhk72vm.png

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


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

auwxzwled0sndmomqihnpe7giok.png

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


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

4oiwgfhprsx-s8iuvnksg6jem5q.png

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


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

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


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

ffgcdrzc1a1dgadl3n2jzoi-mry.png

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

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


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

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


shhjec4htuadhmkine8o0blbd2e.png

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

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


cfmnudskkirljmlousqqr-xny-o.png

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

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


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

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

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

▍ CSS Flexbox


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

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

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

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

txbazw5jzortpwrews5glt0wjvm.png

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

i-z-b9rkck6iywujsq5llv26bfq.png

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

.c-card {
  display: flex;
}


vafk_ryj8mx0qqdxgzajkxfxepk.png

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

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


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

i-z-b9rkck6iywujsq5llv26bfq.png

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

Далее с помощью свойства 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;
}


khr6ay03zklahdzj56nnkl9l6ws.png

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

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


xf16mv6vq4bhxqi99r-vzjhxcjq.png

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

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

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

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

v_comwf1k1sx2prvajog8-vkd-m.png

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

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

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

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


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

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

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


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

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

biarrttjxm5h1nmywvhtjgdc65q.png

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

da-yttqqaqvv5-bypzsx3zf0ugs.png

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

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


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

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


▍ CSS Grid Layout


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

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

kdjjhxsq24nkynotfeq0a3gadjq.png

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

.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;
}


inpfn0omvfzzadvwdhr57msaqkg.png

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

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


whsxpl_uprgmojr0i3kbvehzk7e.png

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

Ещё одной полезной возможностью 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);
}


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

mt9xl0ey79r6fiwxufykule-vk4.png

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

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

h2 {
  font-size: 1rem;
}

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

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


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

dorqijyi7yts8ni2bjekxmtanbq.png

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

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

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

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


kw5brqobj9_4tslc1_uxgrjok5k.png

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

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


l68by27igjzaxzfmnug1ijmt0cu.png

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


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

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

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

zimfetbxhhlfe40plqcym-cw8y8.png

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

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

▍ Компонент article

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

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


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

▍ Компонент pagination

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

el-ymumdf6ilyauesl4xpltykdi.png

.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);
}


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

upbwxvospx1ohyh15sjn1jtvfds.png

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

pouyz5vx6sdtvp3q_bhxdazpsje.png

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


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

kdv9bt2bsw97h-floksj9ud6rv4.png

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

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

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

k4roeggkmonx8wivtl2mvlnrcgk.png

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

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

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


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

▍ Компонент article

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

wi53_vlokldvapv54djv7lntl1i.png

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

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

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


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

iewec4bhg4qhe8ypgbbzw-obgtc.png

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


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

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

:root {
  color-scheme: light dark;
}

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


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

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


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

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

fjo1nzycs0helcppisou1034khu.png

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

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


Используя логические свойства, можно написать код 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 и постами о ретроиграх

sz7jpfj8i1pa6ocj-eia09dev4q.png

© Habrahabr.ru