
Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков 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-фишки вы используете, о которых другие могут не знать. Буду ждать их. Спасибо за чтение!
Другие статьи из серии можно найти по тегу «sm909_unknown_css».
P.S. Помогаю больше узнать про CSS в своём ТГ канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻

