Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков 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 💻