За пределами медиазапросов CSS
![](/static/storage/172112538528781274771861518689399139206.png)
Медиазапросы существуют почти столько же, сколько и сам CSS, и в условиях отсутствия гибкости, сетки, отзывчивых блоков и математических функций медиазапросы были наиболее прагматичным выбором для создания в некоторой степени отзывчивых веб-сайтов.
В начале 2010-х годов, с распространением мобильных устройств и своевременной публикацией классической статьи Итана Маркотта «Отзывчивый веб-дизайн», медиа-запросы стали очень нужны для создания макетов, которые могли бы меняться на разных экранах и устройствах. Даже когда появились спецификации CSS Flexbox и Grid, медиазапросы для изменения размеров не исчезли.
Хотя данные о фактическом использовании медиазапросов неуловимы, тот факт, что со временем они обросли дополнительными функциями, выходящими далеко за пределы области просмотра и включающими такие вещи, как пользовательские предпочтения, по-прежнему делает их ключевым компонентом отзывчивого дизайна.
Сегодня в CSS существует больше возможностей и инструментов для создания макетов, которые позволяют элементам страницы адаптироваться к различным условиям, помимо размера области просмотра. Некоторые из них используются более широко — Flexbox и Grid, а также такие вещи, как отзывчивые единицы длины и, что особенно важно, запросы к контейнерам, к концепции которых мы вернемся чуть позже.
Но медиазапросы все еще часто являются инструментом де-факто, к которому обращаются разработчики. Может быть, это мышечная память, непоследовательная поддержка браузерами или то, что мы застряли в своих привычках, но внедрение современных подходов для отзывчивых интерфейсов, похоже, происходит медленно.
Чтобы было понятно, я полностью поддерживаю медиазапросы. Они играют важную роль в работе, которую мы выполняем помимо контроля размера области просмотра, чтобы сделать более качественный и доступный пользовательский интерфейс, основанный на предпочтениях ОС пользователя, типе устройства ввода, которое он использует, и многом другом.
Но должны ли медиазапросы оставаться золотым стандартом для отзывчивых макетов? Как всегда, все зависит от ситуации, но некоторые утверждают: «Бесспорно, медиа-запросы эволюционировали в сторону решения проблемы доступности, освободив место для других функций CSS, которые должны взять на себя ответственность за отзывчивость».
Проблема с медиа-запросами
Медиазапросы казались отличным решением для большинства проблем, связанных с отзывчивостью, но по мере того, как Интернет развивается в сторону больших и более сложных макетов, ограничения медиазапросов становятся всё более распространенными, чем когда-либо.
Проблема №1: Они ориентированы на область просмотра
При написании точек разрыва медиазапроса, в которых мы хотим адаптировать макет, у нас есть доступ только к свойствам области просмотра, таким как ширина или ориентация. Иногда нам нужно лишь изменить размер шрифта, и область просмотра — наш лучший помощник в этом деле, но чаще всего важен контекст.
Компоненты на странице делят пространство с другими и располагаются относительно друг друга в соответствии с нормальным движением документа. Если все, что нам доступно, — это ширина области просмотра, то точное определение точки разрыва становится задачей компромиссов, когда одни компоненты будут хорошо реагировать на адаптированный макет, а другие будут нуждаться в дополнительной настройке в этой конкретной точке разрыва.
Итак, мы изменяем размеры браузера и ищем правильную точку разрыва, где наш контент становится слишком сжатым.
Следующий пример, вероятно, содержит худший CSS, который вы увидите за последнее время, но он помогает понять одну из проблем с медиа-запросами.
Этот элемент занимает половину экрана, но выглядит тесновато, если страница меньше 600px, поэтому мы изменим его размер, чтобы он занимал 100%. | Этот элемент занимает половину экрана, но выглядит тесновато, если страница меньше 600px, поэтому мы изменим его размер, чтобы он занимал 100%. |
Этот элемент занимает половину экрана, но выглядит тесновато, если страница меньше 600px, поэтому мы изменим его размер, чтобы он занимал 100%. | Этот элемент занимает половину экрана, но выглядит тесновато, если страница меньше 600px, поэтому мы изменим его размер, чтобы он занимал 100%. |
<ul>
<li>This item is half of the screen, but looks to cramped when the page is smaller than 600px, so we resize it to cover 100%</li> <li>This item is half of the screen, but looks to cramped when the page is smaller than 600px, so we resize it to cover 100%</li>
<li>This item is half of the screen, but looks to cramped when the page is smaller than 600px, so we resize it to cover 100%</li>
<li>This item is half of the screen, but looks to cramped when the page is smaller than 600px, so we resize it to cover 100%</li>
</ul>
*{
margin: 0px;
padding: 0px;
box-sizing: border-box;
}
ul{
display: flex;
flex-flow: row wrap;
min-height: 100vh;
background-color: #8C52FF;
}
li{
width: 50vw;
border: 1px solid #000;
list-style:none;
background-color: #fff;
}
@media(width < 600px){
li{
width: 100%;
}
}
Это довольно неудобный пример, но почему именно он плох?
Если попытаться преобразовать CSS дословно, то получится следующее: «Когда ширина страницы меньше 600px
, эти элементы будут увеличиваться и уменьшаться. Это утверждение совершенно не зависит от содержимого элемента или его братьев и сестер. А что произойдет, если братьев и сестер будет больше или только один? Или что произойдет, если элемент находится внутри меньшего контейнера? В медиазапросе просто нет информации, необходимой для учета этих моментов, что приводит нас ко второй проблеме медиазапросов.
Проблема № 2: Ими трудно управлять
В наше время все является компонентами. Подобно кочевникам, компоненты кочуют с места на место, деля пространство с другими компонентами и принося с собой их постоянно меняющийся контент. Опять же, медиазапросы совершенно не знают контекста, окружающего компонент, и на разработчика ложится бремя поиска той самой точки разрыва для каждого конкретного случая.
Это трудоемкая работа, потому что идеальная точка разрыва в медиазапросе - это своего рода закодированное магическое число, которое мы находим, изменяя размер страницы, а также потому, что она будет отличаться в зависимости от контекста, окружающего каждый компонент, что потребует множества медиазапросов. Если мы хотим изменить содержимое элемента или его контейнер, то медиазапрос тоже должен измениться.
И где же в таблице стилей вы управляете медиазапросами? Некоторые разработчики вставляют их в конец, другие — в частичные файлы и полагаются на препроцессор для их компиляции.
Недавняя функция CSS Nesting делает все более чистым, поскольку мы можем прикрепить медиазапрос к компоненту в одном стилевом правиле, но теперь мы должны делать это для каждого компонента в системе, что приводит к увеличению количества экземпляров, которые необходимо учитывать при редактировании стилей. Это приводит к следующей проблеме, связанной с медиазапросами как серебряной пулей отзывчивости.
Проблема №3: Они не очень отзывчивы
В большинстве случаев мы хотим, чтобы элемент плавно изменял свои размеры в зависимости от экрана, но писать новый медиазапрос каждый раз, когда компонент «ломается» в определенной точке разрыва, — это слишком сложно. Если компонент становится слишком высоким на узком экране, когда область просмотра находится в диапазоне от 960px
до 970px
, действительно ли нам нужно писать новый набор стилей, за которыми теперь придется следить?
Я бы назвал это не отзывчивым дизайном, а скорее некой формой адаптивного дизайна, основанного на жестких цифрах для суперспецифической ситуации. В этом нет никакой плавности.
Вам не нужны медиазапросы (для изменения размера)
К счастью, мы уже не живем в 2012 году, и есть гораздо лучшие варианты, чем медиа-запросы, большинство из которых уже приняты и широко поддерживаются, во главе с Flexbox и Grid, отзывчивыми блоками и математическими функциями, в то время как другие, такие как контейнерные запросы, находятся на пороге, но все еще в относительно ранней стадии.
Я не собираюсь вести себя так, как будто я открыл горячую воду, указывая на то, что эти современные функции CSS существуют и являются обычными инструментами, используемыми почти каждым разработчиком CSS. Однако медиазапросы по-прежнему используются в CSS для изменения размеров, особенно в ситуациях, когда функция clamp()
или немного креативного мышления с отзывчивыми блоками не только справятся с задачей, но и сделают это лучше, чем медиазапросы, потому что они предназначены именно для этого.
Поэтому вместо того, чтобы пытаться научить вас всем этим не совсем новым возможностям CSS (вы потрясающие, и я уверен, что вы уже знаете о них), я сосредоточусь на замене существующих медиа-запросов на современные отзывчивые техники.
Флексбокс
Флексбокс и медиазапросы часто используются в паре, так что флексбокс устанавливает макет в определенном направлении, а медиазапросы используются для изменения направления при определенной ширине области просмотра.
<main>
<article>
<h1>Hey!</h1>
<p>This item is half of the screen, but looks to cramped when the page is smaller than 700px, so we resize it to cover 100vw</p>
</article>
<article>
<h1>Hey!</h1>
<p>This item is half of the screen, but looks to cramped when the page is smaller than 700px, so we resize it to cover 100vw</p>
</article>
<article>
<h1>Hey!</h1>
<p>This item is half of the screen, but looks to cramped when the page is smaller than 700px, so we resize it to cover 100vw</p>
</article>
</main>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
main {
display: flex;
flex-flow: row wrap;
}
main article {
width: 50vw;
padding: 10px;
border: 1px solid #000;
}
@media (width < 700px) {
main article {
width: 100vw;
}
}
Эта очень простая, но распространенная схема наталкивается на каждую из трех проблем, о которых мы говорили ранее:
1. Он ориентирован на область просмотра.
Мы учитываем только ширину области просмотра при выборе места, где контейнер меняет направление. После тестирования я обнаружил, что магическая точка разрыва равна 700px
, так что именно в этом месте нам нужно будет создать новый медиазапрос.
2. Его сложно использовать повторно и управлять им.
Поскольку мы учитываем только ширину области просмотра, элемент не может быть использован внутри меньшего контейнера или может выглядеть неловко, если элемент имеет различное содержимое внутри или вокруг него.
3. Он не настолько отзывчив.
У нас есть точка отрыва на 700px
, поэтому устройства с узкими экранами, выходящими за пределы порога, могут слишком сильно сжимать контент, в то время как другие получают оптимальные впечатления.
Если мы попытаемся исправить ситуацию, добавив больше медиазапросов, мы вернемся к проблеме №2.
Лучшее решение для этого случая - вообще отказаться от медиазапросов. Я бы заменил их свойством flex
, которое позволяет элементам <article>
увеличиваться и уменьшаться в зависимости от доступного пространства до определенной точки, установленной на 400px
.
main {
display: flex;
flex-flow: row wrap;
}
main article {
flex: 1 1 400px;
}
Если мы переведем CSS, то в первом примере с медиа-запросом будет сказано: «Когда область просмотра меньше 700px
, я сделаю элементы оберткой». Почему? На самом деле я не знаю. Опять же, запрос не зависит от контекста статьи. Если мы переведем обновленный пример, то он будет звучать примерно так: «Независимо от того, где находится элемент, я постараюсь сделать так, чтобы он занимал 400px
, но буду корректировать его, если доступное пространство изменится».
Измените размер следующей демонстрации. Видите, насколько приятнее стали отображаться статьи при изменении размера экрана?
<main>
<article>
<h1>Hey!</h1>
<p>This item will try its best to be 400px, but will grow and shrink for any space left </p>
</article>
<article>
<h1>Hey!</h1>
<p>This item will try its best to be 400px, but will grow and shrink for any space left </p>
</article>
<article>
<h1>Hey!</h1>
<p>This item will try its best to be 400px, but will grow and shrink for any space left </p>
</article>
</main>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
main {
display: flex;
flex-flow: row wrap;
}
main article {
flex: 1 1 400px;
padding: 10px;
border: 1px solid #000;
}
И мы справились с этой задачей, используя меньше кода и ноль магических чисел.
Grid
Последний пример хорош, но вы можете заметить, что последний гибкий элемент занимает всё свободное место в последнем ряду, а не остается того же размера, что и его братья и сестры. Если вы хотите, чтобы все гибкие элементы были одинакового размера, вам придется изменить их ширину и, возможно, снова использовать медиазапросы. В большинстве случаев, когда вы сами устанавливаете ширину (width
) для гибкого элемента, это признак того, что вам лучше перейти на Grid, так как мы можем установить определенные границы для столбцов и строк.
К счастью, это можно сделать с помощью всего двух строк CSS:
main {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
}
Вкратце о том, что произошло:
auto-fit
вписывает столько колонок, сколько может, а также расширяет их, если остается свободное место.
minmax
задает минимальную ширину колонок, в данном случае500px
.
<main>
<article>
<h1>Lorem!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu enim eu lacus elementum tincidunt. In hac habitasse platea dictumst. Nunc eget rutrum enim.</p>
</article>
<article>
<h1>Lorem!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu enim eu lacus elementum tincidunt. In hac habitasse platea dictumst. Nunc eget rutrum enim.</p>
</article>
<article>
<h1>Lorem!</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus eu enim eu lacus elementum tincidunt. In hac habitasse platea dictumst. Nunc eget rutrum enim.</p>
</article>
</main>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
main {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
}
main article {
padding: 10px;
border: 1px solid #000;
}
Примечание: Сара Суэйдан (Sara Soueidan) дала, возможно, лучшее объяснение этого подхода, и его определенно стоит прочитать. Возможно, это станет вашим новым любимым фрагментом CSS.
Математические функции и единицы реагирования
Математические функции и отзывчивые единицы решают большинство проблем, связанных с изменением размеров элементов. Они задают границы отзывчивости без необходимости кропотливо определять конкретные точки разрыва. Они полностью поддерживаются в современных браузерах и уже широко используются, поэтому мы просто подытожим, что и почему доступно.
Используя функцию min()
, мы можем заставить элементы изменять размер в зависимости от единицы реагирования, такой как ширина области просмотра (vw
), или относительной единицы, такой как процент, чтобы установить верхний предел, чтобы они не увеличивались слишком сильно. Этот элемент будет пытаться охватить своего родителя по всей ширине, но не будет расти больше 300px
:
.min {
height: 400px;
width: min(100%, 300px);
}
Мы можем изменить размер высоты по ширине с помощью свойства aspect-ratio
:
.min-and-aspect-ratio {
aspect-ratio: 1/1; /* or 1 */
width: min(100%, 300px);
}
Используя функцию max()
, мы можем установить нижний предел. Следующий CSS позволяет элементу увеличивать свой размер до половины родительского элемента, но при этом он никогда не будет уменьшаться ниже 300px
:
.max {
height: 400px;
width: max(50%, 300px);
}
Это немного запутанно, верно? Мы используем min()
, чтобы установить максимальную ширину, и max()
, чтобы установить минимальную.
Затем есть очень популярная функция clamp()
, которая устанавливает и максимальный, и минимальный пределы - но с дополнительным бонусом в виде установки «идеального» размера в качестве среднего аргумента. Мы «зажимаем» значения с диапазоном, которого они придерживаются, пытаясь попасть в идеальную цель.
Элемент в следующей демонстрации пытается занять всю доступную ширину своего родительского элемента, но не будет выходить за пределы 300px
или опускаться ниже 200px
.
<div class="min">
min()
</div>
<div class="min-and-aspect-ratio">
min() + aspect-ratio
</div>
<div class="max">
max()
</div>
<div class="clamp">
clamp()
</div>
div{
display: flex;
align-items:center;
justify-content:center;
margin: 10px 0px;
font-size: 1.5rem;
font-family: arial;
font-weight: 600;
text-align: center;
}
.min {
height: 400px;
width: min(100%, 300px);
background-color: #fb5607;
}
.min-and-aspect-ratio {
aspect-ratio: 1/1;
width: min(100%, 300px);
background-color: #ff006e;
}
.max {
height: 400px;
width: max(50%, 300px);
background-color: #8338ec;
}
.clamp{
height: 300px;
width: clamp(200px, 100%, 300px);
background-color: #3a86ff;
}
Это комбинация
Чтобы сайт выглядел великолепно вне зависимости от устройства, необходимы не только отзывчивые блоки или математические функции. Нам нужно сочетание всех техник, чтобы создать бесшовный отзывчивый интерфейс. Это можно сравнить с Performance API в JavaScript — группой стандартов, которые работают вместе для обеспечения производительности.
То, что мы имеем, — это группа спецификаций CSS, построенных вокруг отзывчивого дизайна. Они не обязательно заменяют медиазапросы CSS, но являются дополнительными и предназначены для совместной работы для наилучшего покрытия.
Например, мы можем захотеть, чтобы значение размера шрифта (font-size
) увеличивалось или уменьшалось в зависимости от ширины области просмотра. С медиа-запросами все достаточно просто, но теперь у нас есть дополнительные способы решения этой задачи, которые могут быть более эффективными или удобными в зависимости от вашего проекта.
Мы, конечно, можем использовать медиазапросы для обновления значения font-size
при определенной ширине браузера. Скорее всего, нам придется написать несколько запросов, чтобы получить нужный размер в каждой точке разрыва, но это возможно и допустимо.
<section>
<h1>All this text<h1>
<h2>Will grow and shrink using media queries</h2>
<p>Using the <code>@media</code> at-rule, when the screen get small enough the text will decrease in size. This is far from a perfect solution</p>
</section>
h1 {
font-size: 48px;
}
h2 {
font-size: 28px;
}
p {
font-size: 18px;
}
@media (width < 600px) {
h1 {
font-size: 36px;
}
h2 {
font-size: 22px;
}
p {
font-size: 16px;
}
}
Вместо того чтобы обновлять размер шрифта, font-size
, с помощью фиксированных пикселей в нескольких точках разрыва, мы можем использовать единицы длины отзывчивости. Например, единица vw
относится к ширине области просмотра, где каждая единица составляет 1 % от текущей ширины браузера.
Но мы можем пойти дальше, потому что одни только единицы области просмотра не спасут нас от слишком маленьких и больших размеров шрифтов для их контекста. Давайте объединим их с функцией clamp()
, чтобы установить минимальные и максимальные границы, определив идеальный размер.
<section>
<h1>All this text<h1>
<h2>Will grow and shrink</h2>
<p>Using <code>clamp()</code> and responsive units, we can make our font size seamlessly resize. Go ahead an resize it ;)</p>
</section>
h1{
font-size: clamp(36px, 6vw, 48px);
}
h2{
font-size: clamp(22px, 4vw, 28px);
}
p{
font-size: clamp(16px, 3vw, 18px);
}
Но подождите! Мы можем улучшить ситуацию еще больше, объявив этот параметр непосредственно в font-size
элемента <html>
, чтобы все остальные шрифты изменяли размер на тот же коэффициент. Затем, используя единицу rem
, мы можем написать, насколько большим или маленьким должен быть font-size
каждого элемента, или отказаться от этого и использовать clamp()
, или даже фиксированные единицы пикселей в определенных элементах.
Стоит отметить, что разница между единицами rem
и em
заключается в том, что первые относятся к «корневому» элементу, то есть <html>
, а вторые - к родительскому элементу селектора.
<section>
<h1>All this text<h1>
<h2>Will grow and shrink by the same factor</h2>
<p>Using <code>clamp()</code> and responsive units, we can make our font size seamlessly resize by the same factor. Go ahead an resize it ;)</p>
<p class="samesize">This text will remain the same <code>font-size</code> always<p>
</section>
html{
font-family: arial;
font-size: clamp(12px, 2vw, 18px);
}
h1{
font-size: 3rem
}
h2{
font-size: 1.8rem;
}
p{
font-size: 1.2rem;
}
.samesize{
font-size: 18px;
}
Так что, да, все это не должно использоваться изолированно или как замена медиа-запросов. Для создания отзывчивых интерфейсов нужна целая деревня, так сказать, а у нас есть набор молотков, гаечных ключей, гвоздей и шурупов, которые мы можем использовать, чтобы собрать все воедино.
Привет, контейнерные запросы!
Медиазапросы умеют изменять макеты в масштабах всей страницы. Возьмем, к примеру, корзину покупок. Если ширина области просмотра достаточно велика, мы можем отобразить все товары в широкой таблице, <table>
, где им будет комфортно дышать.
![](/static/storage/126767186589942894076393583991910265979.png)
Такой же макет в мобильных устройствах просто не работает. Таблицы и так имеют свой собственный набор проблем с отзывчивостью, и хотя недостатка в решениях нет, мы можем рассмотреть другой макет, используя современные методы, которые гораздо менее инженерны.
![](/static/storage/190700718551305045620042268848835533007.png)
Мы делаем гораздо больше, чем просто меняем ширину или высоту элементов! Необходимо изменить цвета границ, видимость элементов и направления сгибания, а это можно сделать только с помощью медиазапроса, верно? Что ж, даже в тех случаях, когда нам нужно полностью изменить макет в зависимости от размера области просмотра, мы можем лучше сделать это с помощью запросов контейнера.
Опять же, проблема №1 медиазапросов заключается в том, что при принятии решений они учитывают только размер области просмотра и совершенно не учитывают окружающий элемент контекст.
Это не так уж важно, если речь идет о ряде элементов, которым разрешено занимать всю ширину страницы, поскольку полная ширина страницы очень сильно связана с размером области просмотра, что делает медиазапросы вполне подходящим вариантом для внесения корректировок.
<ul class="cards">
<li>
<img src="https://picsum.photos/id/29/367/267" alt="mountain landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/28/367/267" alt="forest landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/25/367/267" alt="tree branches" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
</ul>
* {
margin: 0;
padding: 0;
}
html {
font-family: Sans-Serif;
}
body {
background-color: #8c52ff;
}
.cards {
display: flex;
flex-flow: row wrap;
gap: 20px;
padding: 20px;
}
.cards li {
display: flex;
gap: 20px;
padding: 20px;
border-radius: 10px;
list-style: none;
background-color: #fff;
}
.cards img {
max-height: 200px;
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 10px;
}
.details {
display: flex;
flex-flow: column;
gap: 10px;
}
.details h2 {
font-size: 30px;
}
@media (width < 700px) {
.cards li {
flex-flow: column;
}
}
Но, допустим, мы хотим отобразить эти же элементы в многоколоночном макете, где они включены в узкую колонку в качестве <aside>
рядом с более крупной колонкой, содержащей элемент <main>
. Теперь у нас проблемы.
Более традиционное решение - написать серию медиазапросов в зависимости от того, где используется элемент и где его содержимое разрывается. Но медиазапросы полностью упускают взаимосвязь между элементами <main>
и <aside>
, а это очень важно, поскольку размер одного из них влияет на размер другого в соответствии с нормальным движением документа.
<div class="wrapper">
<main class="main-content">
<h1>Main Content</h1>
</main>
<aside>
<ul class="cards">
<li>
<img src="https://picsum.photos/id/29/367/267" alt="mountain landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/28/367/267" alt="forest landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/25/367/267" alt="tree branches" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
</ul>
</aside>
</div>
* {
margin: 0;
padding: 0;
}
html {
font-family: Sans-Serif;
}
body {
background-color: #8c52ff;
}
.wrapper{
display: flex;
flex-flow: row wrap;
gap: 20px;
padding: 20px;
}
.main-content{
flex: 1 0 50%;
border-radius: 10px;
padding: 20px;
height: 600px;
background-color: #AD84FD;
}
aside{
flex: 1 1 40%;
}
.cards {
display: flex;
flex-flow: row wrap;
gap: 20px;
}
.cards li {
display: flex;
gap: 20px;
padding: 20px;
border-radius: 10px;
list-style: none;
background-color: #fff;
}
.cards img {
max-height: 200px;
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 10px;
}
.details {
display: flex;
flex-flow: column;
gap: 10px;
}
.details h2 {
font-size: 30px;
}
@media (width < 700px) {
.cards li {
flex-flow: column;
}
}
Элемент .cards
находится в контексте элемента <aside>
и сжимается из-за того, что находится в узкой колонке. Было бы здорово менять расположение каждого компонента .card
, когда содержащий их элемент .cards
достигает определенного размера, а не когда область просмотра имеет определенный размер.
Именно здесь в игру вступают контейнерные запросы, позволяющие нам условно применять стили на основе размера элемента. Мы регистрируем элемент в качестве «контейнера», которым в нашем примере является неупорядоченный список, содержащий серию компонентов .card
. По сути, мы предоставляем родительскому селектору широкие возможности для влияния на текущий макет.
.cards {
container-name: cards;
}
Запросы контейнера контролируют элемент по его размеру, и нам нужно указать браузеру, как именно интерпретировать этот размер, задав .cards
тип контейнера (container-type
), который может быть размером (size
) контейнера (т. е. в блочном и инлайн-направлении) или его инлайн-размером (inline-size
) (т. е. общей длиной в инлайн-направлении). Существует обычное значение (normal
), которое не учитывает размер, но позволяет опрашивать элемент по его стилям.
.cards {
container-name: cards;
container-type: inline-size;
}
Мы можем немного упростить ситуацию, используя сокращенное свойство container
.
.cards {
container: cards / inline-size;
}
Теперь мы можем изменять расположение компонентов .card
, когда контейнер .cards
имеет определенный размер в строке. Запросы контейнеров используют тот же синтаксис, что и медиа-запросы, но вместо @media
используется at-правило @container
.
.cards {
container: cards / inline-size;
}
@container cards (width < 700px) {
.cards li {
flex-flow: column;
}
}
Теперь каждая .card
- это гибкий контейнер, который выстраивается в направлении колонки, если ширина контейнера .cards
меньше 700px
. Если ширина больше, мы можем расположить их в направлении строки.
<div class="wrapper">
<main class="main-content">
<h1>Main Content</h1>
</main>
<aside>
<ul class="cards">
<li>
<img src="https://picsum.photos/id/29/367/267" alt="mountain landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/28/367/267" alt="forest landscape" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
<li>
<img src="https://picsum.photos/id/25/367/267" alt="tree branches" />
<div class="details">
<h2>Lorem Impusm</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at dapibus nibh. Ut nisl est, fringilla eget ante vitae, sagittis congue nibh. In lacus odio, lacinia id aliquet vel, porta ut quam. Etiam maximus orci eget mi tempor volutpat. Etiam mattis turpis et interdum finibus. </p>
</div>
</li>
</ul>
</aside>
</div>
* {
margin: 0;
padding: 0;
}
html {
font-family: Sans-Serif;
}
body {
background-color: #8c52ff;
}
.wrapper{
display: flex;
flex-flow: row wrap;
gap: 20px;
padding: 20px;
}
.main-content{
flex: 2 0 50%;
border-radius: 10px;
padding: 20px;
height: 600px;
background-color: #AD84FD;
}
aside{
flex: 1 1 40%;
}
.cards {
container-type: inline-size;
display: flex;
flex-flow: row wrap;
gap: 20px;
}
.cards li {
display: flex;
gap: 20px;
padding: 20px;
border-radius: 10px;
list-style: none;
background-color: #fff;
}
.cards img {
max-height: 200px;
width: 100%;
aspect-ratio: 16/9;
object-fit: cover;
border-radius: 10px;
}
.details {
display: flex;
flex-flow: column;
gap: 10px;
}
.details h2 {
font-size: 30px;
}
@container (width < 700px) {
.cards li {
flex-flow: column;
}
}
Запросы стиля — это родственник запросов контейнера в том смысле, что мы можем запрашивать стили контейнера и условно применять изменения стиля к его дочерним элементам, например, менять цвет (color
) дочернего элемента на белый, если цвет фона контейнера (background-color
) установлен на темный цвет. Мы все еще находимся в самом начале пути, и поддержка запросов стилей и поддержка браузеров все ещё развиваются.
Надеюсь, это даст вам представление о том, насколько удивительно, что у нас есть такой контекстно-зависимый способ создания отзывчивых макетов. Контейнеры — это совершенно новая идея в CSS (хотя мы уже давно используем этот термин как синоним термина «родительский элемент»), которая отличается новизной и элегантностью.
Так что, медиазапросы бесполезны?
НЕТ! Хотя медиазапросы были основным решением для создания отзывчивого дизайна, их ограничения стали очевидны сейчас, когда у нас есть более надежные инструменты в CSS, предназначенные для решения этих проблем.
Это не делает медиа-запросы устаревшими — это просто другой инструмент, который является частью более широкого набора инструментов для создания отзывчивых интерфейсов. Кроме того, медиазапросы по-прежнему решают важные проблемы доступности благодаря своей способности распознавать визуальные и двигательные предпочтения пользователя — среди прочих параметров — на уровне операционной системы.
Так что да, продолжайте использовать медиазапросы! Но, возможно, используйте их реже, поскольку CSS может предложить нам гораздо больше.