От автора: сегодня я более взволнован новой функциональностью CSS, чем когда либо за последние шесть лет, которые я провел в качестве фронтенд-разработчика. Прототип контейнерных запросов теперь доступен в Chrome Canary.
Я помню, как видел много шуток о поддержке контейнерных запросов CSS, но, наконец, они есть. В этой статье я расскажу, зачем нам нужны контейнерные запросы, как они упростят вашу жизнь, и, что наиболее важно, вы получите более мощные компоненты и макеты.
Веб-страница состоит из разных разделов и компонентов, и мы делаем их адаптивными с помощью медиа-запросов CSS. В этом нет ничего плохого, но есть ограничения. Например, мы можем использовать медиа-запрос, чтобы показать минимальную версию компонента на мобильном устройстве по сравнению с настольным компьютером.
Часто отзывчивый веб-дизайн не зависит от области просмотра или размера экрана. Дело в размере контейнера. Рассмотрим следующий пример:
У нас очень типичный макет. Имеется два варианта компонентов: вложенный и горизонтальный.
Есть несколько способов реализовать это в CSS, но наиболее распространенный из них следующий. Нам нужно создать базовый компонент, а затем сделать его вариации.
CSS .c-article { /* The default, stacked version */
}
.c-article > * + * { margin-top: 1rem;
}
/* The horizontal version */ @media (min-width: 46rem) { .c-article—horizontal { display: flex; flex-wrap: wrap;
}
.c-article > * + * { margin-top: 0;
}
.c-article__thumb { margin-right: 1rem; }
}
1234567891011121314151617181920212223 | .c-article { /* The default, stacked version */} .c-article > * + * { margin-top: 1rem;} /* The horizontal version */@media (min-width: 46rem) { .c-article—horizontal { display: flex; flex-wrap: wrap; } .c-article > * + * { margin-top: 0; } .c-article__thumb { margin-right: 1rem; }} |
Обратите внимание, что мы создали класс .c-article—horizontal для обработки горизонтальной версии компонента. Если ширина области просмотра больше 46 rem, компонент должен переключиться на горизонтальное отображение.
Это неплохо, но существуют некоторые ограниченным. Я хочу, чтобы компонент реагировал на ширину своего родителя, а не на размер окна просмотра или экрана браузера.
Учтите, что мы хотим использовать значение по умолчанию .c-card в основном разделе. Что случится? Что ж, компонент расширится до ширины свого родительского и, следовательно, будет слишком большой. см. следующий рисунок:
Это проблема, и мы можем решить ее с помощью контейнерных запросов CSS (наконец-то ура). Прежде чем углубиться в них, позвольте мне дать вам представление о желаемом результате.
Нам нужно сообщить компоненту, что если его прямая родительская ширина больше 400 пикселей, то ему необходимо переключиться на горизонтальный стиль. Вот как будет выглядеть CSS:
123456789101112 |
CSS .o-grid__item { contain: layout inline-size;
}
.c-article { /* The default style */
}
@container (min-width: 400px) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ }
}
1234567891011121314 | .o-grid__item { contain: layout inline-size;} .c-article { /* The default style */} @container (min-width: 400px) { .c-article { /* The styles that will make the article horizontal** ** instead of a card style.. */ }} |
Отечественные сайты чаще всего используют российские доменные зоны
Как нам помогут контейнерные запросы CSS?
Предупреждение: контейнерные запросы CSS пока поддерживаются только в Chrome Canary.
С помощью контейнерных запросов CSS мы можем решить указанную выше проблему и создать гибкий компонент. Это означает, что мы можем добавить компонент в узкий родительский элемент, и он превратится в составную версию, или в более широкий, и тогда он превратится в горизонтальную версию. Опять же, все они не зависят от ширины области просмотра.
Вот как я это себе представляю.
Пурпурный контур представляет ширину родительского элемента. Обратите внимание, как при увеличении его размера, компонент адаптируется к этому. Разве это не круто? В этом сила контейнерных запросов CSS.
Как работают контейнерные запросы
Теперь мы можем поэкспериментировать с контейнерными запросами Chrome Сanary. Чтобы включить возможность контейнерных запросов, введите в адресной строке chrome://flags и выполните поиск по запросу «container queries», а затем включите их.
Первый шаг – добавить свойство contain. Поскольку компонент будет адаптироваться в зависимости от его родительской ширины, нам нужно указать браузеру перерисовывать только затронутую область, а не всю страницу. С помощью свойства contain мы можем сообщить об этом браузеру заранее.
Значение inline-size означает реагирование только на изменение ширины родительского элемента. Я пробовал использовать block-size, но он все еще не работает. Пожалуйста, поправьте меня, если я ошибаюсь.
12345678910111213 |
CSS .o-grid__item { contain: layout inline-size;
}
123 | .o-grid__item { contain: layout inline-size;} |
Это первый шаг. Мы определили элемент .o-grid__item как родительский элемент для .c-article который внутри него. Следующим шагом является добавление стилей, которые мы хотим заставить работать с контейнерными запросами.
CSS .o-grid__item { contain: layout inline-size;
}
@container (min-width: 400px) { .c-article { display: flex; flex-wrap: wrap;
}
/* other CSS.. */
}
123456789101112 | .o-grid__item { contain: layout inline-size;} @container (min-width: 400px) { .c-article { display: flex; flex-wrap: wrap; } /* other CSS.. */} |
@container является элементом .o-grid__item, а его ширина min-width: 400px. Мы даже можем пойти дальше и добавить больше стилей. У нас есть следующие стили:
По умолчанию.
Горизонтальная карточка с маленькой миниатюрой.
Горизонтальная карточка с большой миниатюрой.
Если родительский объект слишком велик, стиль будет указать нам, что это избранная статья.
Давайте рассмотрим варианты использования контейнерных запросов CSS.
Примеры использования контейнерных запросов CSS
Контейнерные запросы и CSS-сетка auto-fit
В некоторых случаях использование auto-fit в сетке CSS может привести к неожиданным результатам. Например, компонент будет слишком широким, и его содержимое будет трудночитаемым. Вот наглядное пособие, показывающее разницу между auto-fill и CSS-сеткой auto-fit.
Обратите внимание, что при использовании auto-fit элементы расширяются, чтобы заполнить доступное пространство. Однако в случае auto-fill, элементы сетки не будут расти, и вместо этого у нас будет свободное место (пунктирный элемент справа).
Теперь вы можете спросить, какое отношение это имеет к контейнерным запросам CSS? Что ж, каждый элемент сетки — это контейнер, и когда он расширяется (мы также используем auto-fit), нам нужно, чтобы компонент изменялся в зависимости от контейнера.
Прикрепленные элементы сетки в CSS
1234567891011121314 |
CSS .o-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 1rem;
}
12345 | .o-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 1rem;} |
Когда у нас есть четыре элемента, результат должен выглядеть примерно так.
Ситуация изменится, когда количество статей станет меньше, вот что произойдет. Чем меньше у нас предметов, тем они станут шире. Причина в том, что используется auto-fit. Первый выглядит хорошо, но два последних (по 2 в ряду, по 1 в ряду) выглядят не очень хорошо, так как они слишком широкие.
Что, если каждый компонент статьи изменяется в зависимости от ширины его родительского элемента? Таким образом, мы можем извлечь выгоду от использования auto-fit. Вот что нам нужно сделать.
Если ширина элемента сетки больше 400 пикселей, статья должна переключиться на горизонтальный стиль. Вот как мы можем это сделать:
CSS .o-grid__item { contain: layout inline-size;
}
@container (min-width: 400px) { .c-article { display: flex; flex-wrap: wrap; }
}
12345678910 | .o-grid__item { contain: layout inline-size;} @container (min-width: 400px) { .c-article { display: flex; flex-wrap: wrap; }} |
Кроме того, если статья является единственным элементом в сетке:
CSS .o-grid__item { contain: layout inline-size;
}
@container (min-width: 700px) { .c-article { display: flex; justify-content: center; align-items: center; min-height: 350px;
}
.card__thumb { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }
}
123456789101112131415161718192021 | .o-grid__item { contain: layout inline-size;} @container (min-width: 700px) { .c-article { display: flex; justify-content: center; align-items: center; min-height: 350px; } .card__thumb { position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover; }} |
Вот и все. У нас есть компонент, который реагирует на ширину своего родителя, и он может работать в любом контексте. Разве это не круто? Посмотрите демонстрацию на CodePen.
Боковая и главная панели
Часто нам нужно настроить компонент, чтобы он работал в контейнерах небольшой ширины, таких как . Для этого идеально подходит вид информационных бюллетеней. Когда ширина мала, нам нужно, чтобы его элементы складывались в стопку, а когда достаточно места, нам нужно, чтобы они разложились по горизонтали.
Как вы видите на рисунке, у нас есть информационный бюллетень, который существует в двух разных контекстах: боковой раздел и основной раздел.
Без контейнерных запросов это невозможно, пока у нас не будет класса вариаций в CSS, например, .newsletter—stacked или что-то в этом роде.
Я знаю, что мы можем принудительно обернуть элементы, если во флексбоксе недостаточно места, но этого недостаточно. Мне нужно гораздо больше контроля, чтобы делать такие вещи, как:
Скрыть определенные элементы.
Сделать кнопку полной ширины.
CSS .newsletter-wrapper { contain: layout inline-size;
}
/* The default, stacked version */ .newsletter { /* CSS styles */
}
.newsletter__title { font-size: 1rem;
}
.newsletter__desc { display: none;
}
/* The horizontal version */ @container (min-width: 600px) { .newsletter { display: flex; justify-content: space-between; align-items: center;
}
.newsletter__title { font-size: 1.5rem;
}
.newsletter__desc { display: block; }
}
123456789101112131415161718192021222324252627282930313233 | .newsletter-wrapper { contain: layout inline-size;} /* The default, stacked version */.newsletter { /* CSS styles */} .newsletter__title { font-size: 1rem;} .newsletter__desc { display: none;} /* The horizontal version */@container (min-width: 600px) { .newsletter { display: flex; justify-content: space-between; align-items: center; } .newsletter__title { font-size: 1.5rem; } .newsletter__desc { display: block; }} |
Посмотрите демонстрацию на CodePen.
Пагинация
Я обнаружил, что разбиение на страницы хорошо подходит для использования контейнерных запросов. Изначально у нас могут быть кнопки «Назад» и «Далее», затем мы можем скрыть их и показать полную разбивку на страницы, если есть достаточно места. Рассмотрим следующий рисунок.
Чтобы обработать состояния, указанные выше, нам нужно сначала поработать со стилем по умолчанию (сложенные кнопки), а затем работать с двумя другими состояниями.
CSS .wrapper { contain: layout 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) { .pagination { justify-content: center;
}
.pagination__item:not(.btn) { display: block;
}
.pagination__item.btn { display: none; }
}
1234567891011121314151617181920212223242526272829 | .wrapper { contain: layout 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) { .pagination { justify-content: center; } .pagination__item:not(.btn) { display: block; } .pagination__item.btn { display: none; }} |
Посмотрите демонстрацию на CodePen.
Карточка профиля
Вот еще один вариант, который идеально подходит для использования в нескольких контекстах. Маленькое состояние работает для небольших размеров области просмотра и контекстов, таких как боковая панель. Большое состояние работает в гораздо более широком контексте, например, при размещении ее в сетке с двумя столбцами.
CSS .p-card-wrapper { contain: layout inline-size;
}
.p-card { /* Default styles */
}
@container (min-width: 450px) { .meta { display: flex; justify-content: center; gap: 2rem; border-top: 1px solid #e8e8e8; background-color: #f9f9f9; padding: 1.5rem 1rem; margin: 1rem -1rem -1rem;
}
/* and other styles */
}
123456789101112131415161718192021 | .p-card-wrapper { contain: layout inline-size;} .p-card { /* Default styles */} @container (min-width: 450px) { .meta { display: flex; justify-content: center; gap: 2rem; border-top: 1px solid #e8e8e8; background-color: #f9f9f9; padding: 1.5rem 1rem; margin: 1rem -1rem -1rem; } /* and other styles */} |
При этом мы можем увидеть компоненты в разных контекстах без использования единого медиа-запроса.
Посмотрите демонстрацию на CodePen.
Элементы формы
Я еще не углублялся в варианты использования форм, но задумал переключиться с горизонтальных меток на штабелированные.
CSS .form-item { contain: layout inline-size;
}
.input-group { @container (min-width: 350px) { display: flex; align-items: center;
gap: 1.5rem;
input { flex: 1; } }
}
123456789101112131415 | .form-item { contain: layout inline-size;} .input-group { @container (min-width: 350px) { display: flex; align-items: center; gap: 1.5rem; input { flex: 1; } }} |
Попробуйте сами в демонстрации ниже. Посмотрите демонстрацию на CodePen.
Компоненты тестирования
Теперь, когда мы изучили пару примеров использования, в которых контейнерные запросы CSS могут быть полезны, как мы можем протестировать компонент? К счастью, мы можем сделать это с помощью свойства CSS resize в родительском компоненте.
CSS .parent { contain: layout inline-size; resize: horizontal; overflow: auto;
}
12345 | .parent { contain: layout inline-size; resize: horizontal; overflow: auto;} |
Пока что вы не можете увидеть что-то вроде @container (min-width: value). Думаю, это вопрос времени, и это будет реализовано.
Заключение
Мне понравилось изучать контейнерные запросы CSS и экспериментировать с ними в браузере. Я знаю, что они еще официально не поддерживаются, но сейчас самое время начать работать с ними.
Часть нашей работы как фронт-энд разработчиков — тестировать и помогать людям, которые работают над реализацией таких функций. Чем больше мы тестируем, тем меньше проблем мы увидим, когда он будет поддерживаться во всех основных браузерах. Спасибо за чтение.
Автор: Ahmad Shadeed
Редакция: Команда webformyself.
Читайте нас в Telegram, VK, Яндекс.Дзен
Источник
Вам также может понравиться