
Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков CSS-фишки. Я отбираю их так, чтобы они были полезны в разного рода проектах. Неважно, верстаете ли вы сайт для малого бизнеса или создаёте супермодное React-приложение. Они поддерживаются большинством браузеров. Отдельно отмечу, что я не считаю IE11 современным браузером. По этой причине я не учитывал его.
Сегодня мы рассмотрим:
- возможность задать несколько фонов с помощью свойства
background; - свойство
display, которое позволяет сделать так, что свойства элемента будут влиять через потомка; - как заставить псевдо-элемент
nth-childвыбрать элементы без привязки к позиции; - где будет находиться элемент с
position: absolute, если для него заданы свойстваgrid-columnиgrid-row.
Больше не буду затягивать. Давайте посмотрим, что я вам подготовил.
▍ Множество фонов для свойства background
Вёрстка текста поверх изображения — обычная задача верстальщика. В ней встречается маска. По задумке дизайнеров, она мешает тексту слиться с фоном. Я создавал её с использованием псевдо-элемента.
<body>
<div class="hero">
<span class="hero__msg">Hey! I'm Stas Melnikov</span>
</div>
</body>
.hero {
/* здесь стили для позиционирования текста */
min-height: 100dvh;
position: relative;
isolation: isolate;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
background-image: url("hero.webp");
}
.hero::before {
content: "";
inset: 0;
position: absolute;
background-color: rgba(15, 63, 122, 0.5);
z-index: -1;
}
Так я делал достаточно долго. Пока однажды коллега не сказал мне, что псевдо-элемент лишний. Я сначала подумал, что он ошибается, но на всякий случай спросил: «Почему?». Он сказал, что достаточно использовать слои для свойства background. И тут у меня перед глазами всплыл момент из 2013 года.
На обеде у меня была привычка читать про технологии. Как раз был перерыв на обед. Сижу и читаю новость, что теперь можно задавать несколько фоновых изображений. Свойство background стало поддерживать слои.
Очухавшись, я попросил Влада показать его версию кода. Открыв редактор, он удалил псевдо-элемент ::before и добавил функцию linear-gradient() для свойства background-image.
.hero {
/* здесь стили для позиционирования текста */
min-height: 100dvh;
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: cover;
background-image: linear-gradient(to top, rgba(15, 63, 122, 0.5), rgba(15, 63, 122, 0.5)), url("hero.webp");
}

Слева — вариант свёрстанный первым способом, а справа — вторым.
Что такое слои для свойства background? Очень давно свойство задавало фоновое изображение или цвет элемента. С появлением слоёв стало можно задавать множество фонов, которые располагаются друг на друге. Последний указанный фон является самым последним, а первый — самый верхний.
В моём примере url("hero.webp") — самый нижний слой, на который накладывается фон с linear-gradient(). В этом решении также интересно, почему используется градиент. Дело в том, что для свойства background-image мы не можем задать однородный цвет. По этой причине Влад использует хак с градиентом, который «изменяется» от одного цвета к такому же цвету.
▍ display: contents «позволяет» пропустить элемент
При решении задач, связанных с позиционированием или размерами, мне всегда хотелось иметь возможность задать свойства потомку, пропустив ближайшего. Как в недавней моей задаче, где ссылка в блоке должна была тянуться на всю ширину родительского элемента.
Для начала создам разметку.
<body>
<div class="intro">
<div class="intro__container">
<a href="cv.pdf" class="intro__download">download</a>
</div>
</div>
</body>
Поскольку по умолчанию у элемента <a> установлено display: inline, свойство width рассчитывается в зависимости от контента. Мне же требуется сделать так, чтобы оно было эквивалентно свойству width родительского элемента. Первым решением будет использовать display: block для элемента .intro__download.
.intro {
display: grid;
}
.intro__download {
/* здесь декоративные стили */
display: block;
}
Этот код решает мою задачу, но, к сожалению, в своей задаче я не мог его использовать. У меня в проекте множество компонентов. Получилось так, что на ссылке уже было установлено свойство display со значением inline-grid. Изменять его было нельзя. Можно было добавить ещё один класс к элементам .intro__download и в нём установить display: grid.
Но меня смущало, что пришлось бы переопределять значение. Хотелось использовать возможности уже установленного display: grid. Я стал думать дальше, и мне пришло другое решение.
Поскольку у элемента с классом .intro используется display: grid, то свойство width прямого потомка, т. е. элемента с классом .intro__container, будет соответствовать свойству width родительского элемента. А это то, что я хотел сделать для ссылки. Осталось как-то сделать её прямым потомком. Тут я вспомнил про значение contents.
Оно позволяет скрыть элемент так, что свойства родителя будут влиять на его дочерние элементы. То, что нужно в моей задаче.
Осталось добавить его к элементу с классом .intro__container.
.intro {
display: grid;
}
.intro__container {
display: contents;
}
.intro__container {
/* здесь декоративные стили */
display: inline-grid;
}

Всё! Работает как надо. А главное, не надо добавлять новые классы. Люблю я лаконичность.
Кстати, если поведение элемента с display: grid вызвало у вас вопросы, то у меня есть кое-что. Я рассказал про такие нюансы в отдельной статье. Дальше, я думаю, вы разберётесь.
▍ Можно выбрать конкретные элементы без учёта его позиции
У меня к вам вопрос. Как браузеры обработают следующий код:
:nth-child(2 of .highlight) {
color: red;
}
Удивил?! Да, я старался. Это новое расширение of S для псевдо-класса :nth-child. Оно позволяет выбрать определённые элементы из списка селекторов без привязки к номеру в DOM. Сейчас покажу, что я имею в виду.
Для нашего примера с классом .highlight я создам следующую разметку:
<body>
<div class="container">
<span>1</span>
<span class="highlight">2</span>
<span>3</span>
<span>4</span>
<span class="highlight">5</span>
<span class="highlight">6</span>
</div>
</body>

Браузеры применят стили ко второму элементу с классом .highlight, который является пятым элементом по порядку! Другими словами, при поиске элемента не важен номер его позиции в DOM. Это главное отличие от привычного синтаксиса псевдо-класса :nth-child.
Вместо S мы можем использовать любой селектор. Например, так можно отобрать первые три элемента <span> с классом important.
.container :nth-child(-n+3 of span.important) {
color: red;
}
Опять же, браузеры найдут нужные элементы, даже если они не будут первыми тремя элементами в разметке.
<body>
<div class="container">
<span class="important">1</span>
<span>2</span>
<span>3</span>
<span class="important">4</span>
<span>5</span>
<span class="important">6</span>
</div>
</body>

Мне лично всегда не хватало такой возможности. Вспоминаю типичную сетку из карточек товаров. И где-то в середине надо было вставить блок с рекламой, который сбивал порядок элементов. Вот и приходилось плясать с бубнами. Так что синтаксис of — весьма полезная штука.
▍ Элемент с position: absolute не всегда располагается относительно границ родительского элемента с position: relative
Я для тренировки люблю проходить собеседования — иногда это даже полезно. Встречаются интересные вопросы, которые помогают узнать что-то новое. Как раз недавно так и произошло.
Сначала мне показали фрагмент кода, где был абсолютно спозиционированный элемент внутри грид-контейнера:
.container {
box-sizing: border-box;
min-height: 100dvh;
border: 1px solid currentColor;
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(4, 1fr);
position: relative;
}
.container::before {
content: "";
width: 5rem;
height: 5rem;
background-color: darkolivegreen;
position: absolute;
top: 0;
left: 0;
}
Меня спросили: «Где будет располагаться псевдо-элемент ::before?». Я с уверенностью ответил, что в левом верхнем углу. Это был правильный ответ.

А дальше интервьюер добавил для псевдо-элемента ::before свойства grid-column и grid-row.
.container {
box-sizing: border-box;
min-height: 100dvh;
border: 1px solid currentColor;
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(4, 1fr);
position: relative;
}
.container::before {
content: "";
width: 5rem;
height: 5rem;
background-color: darkolivegreen;
grid-column: 2 / span 2;
grid-row: 2 / span 2;
position: absolute;
top: 0;
left: 0;
}
Дальше он спросил: «Изменится ли позиция псевдо-элемента ::before?». Этот вопрос меня сбил с толку. Я думал: «Так, position: absolute выдёргивает элемент из контекста, поэтому свойства grid-column и grid-row не сработают». В итоге я ответил, что изменений не будет. Это был неправильный ответ. Оказывается, что позиция псевдо-элемента ::before изменится .
Свойства grid-column и grid-row определяют позицию дочернего элемента. А как это происходит? На деле мы задаём координаты прямоугольника, в который помещается элемент. Он же используется для отсчёта позиции элементов с position: absolute. По этой причине псевдо-элемент ::before отобразился в левом верхнем углу прямоугольника, созданного строками grid-column: 2 / span 2 и grid-row: 2 / span 2, а не родительского элемента.

▍ Заключение
Давайте подведём итог. Сегодня мы можем с помощью CSS:
- создавать маски для изображений без дополнительных элементов;
- пропустить ближайшего потомка с помощью
display: contents; - с помощью синтаксиса
ofприменять стили к группе элементов без учёта их позиции в DOM; - расположить элемент с
position: absoluteотносительно определённой области родительского элемента.
Оставляю ссылки на все выпуски:
Также, пожалуйста, напишите в комментариях, какие CSS-фишки вы используете, о которых другие могут не знать. Буду ждать их. Спасибо за чтение!
P.S. Помогаю больше узнать про CSS в своём ТГ канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Комментарии (11)

danyatanov
14.05.2024 14:31Использовать backround для фонового изображение через css - это моветон. У нас в компании вообще это под запретом. Только тэг picture и лейзи-лоадинг, к сожалению. Да, можно использовать image-set, но функционал у него не такой богатый, да и поддержка браузеров 84 процента может не всех устроить.

Spaceoddity
14.05.2024 14:31+4Мой вам совет - бегите из этой компании))
Это же надо такую дичь придумать - для бэкграунда элемента использовать дополнительную сущность в html-разметке... Я уж молчу про семантику, но как вы это юзаете? Каждому элементу с бэкграундом - position: relative, а затем позиционированный picture?
Моветон... У меня просто нет слов))

Ione1991
14.05.2024 14:31Видимо изображения должны редактироваться из админки, если это не простенький статичный сайт-визитка.

Beholder
14.05.2024 14:31Как ещё может быть полезен
display: contents: если вам неохота или сложно генерировать уникальные идентификаторы для пар элементов<label for="login">Login</label><input id="login" .../>, то можно же вкладывать поле внутрь метки:<label>Login <input .../></label>, но так поломается разметка сеткой, тогда вот дляlabelможно и указать данное свойство.
melnik909 Автор
14.05.2024 14:31Тогда ваш label потеряется для скринридера, а поле будет без имени

Beholder
14.05.2024 14:31Ну есть что-то такое, но это не по спецификации, а баги каких-либо браузеров, когда-нибудь исправят. Можно попробовать добавить span и прописать aria- атрибуты.
Spaceoddity
Ну, честно говоря, так себе...
Несколько изображений для бэкграунда - уже довольно старая фича. Думается мне, что с ней сталкивался любой, кто плотно работал с градиентами.
display: contents, я лично уже не меньше года использую - сильно помогает привести в соответствие мобильную и десктопную вёрстку (скрывая ненужные обертки).
Давайте тогда и pointer-events в очередной раз вспомним ;)