Хабр, привет! Я стабильно пишу здесь о CSS. Мне радостно, что моя работа вам полезна. Только я немного заскучал. Мне хочется повеселиться и отвести душу. В общем, я придумал новый формат статей в виде квиза.
Сразу скажу, что вопросы в нём только отчасти имеют практический смысл. На практике навряд ли вы столкнётесь с ними. Но это так задумано! Я специально хочу сделать всё, чтобы вы не ответили на вопросы! Поэтому, пожалуйста, не воспринимайте квиз серьёзно. Просто весело проведём время.
Конечно, я оставлю своё решение для каждого вопроса. Будет классно, если вы тоже добавите своё в комментариях. Я тоже хочу учиться. Давайте вместе увидим, каким может быть CSS.
Так, вы готовы? Давайте посмотрим, что я вам подготовил.
▍ Задача №1
Есть абстрактный элемент с классом .awesome-block
. Я хотел установить ему фон с помощью background-color
. Но, находясь в приподнятом настроении, добавил значение 10px
.
:root {
--\*: 10px;
}
.awesome-block {
background-color: tomato;
background-color: var(--\*); /* какое здесь будет значение в devTools? */
}
Сработает ли этот код? Если да, какое значение вычислят браузеры для свойства background-color
?
Сначала разберёмся с пользовательским свойством --\*
. Может показаться, что оно написано с нарушением синтаксиса. Это не так.
Как говорится в спецификации, название пользовательского свойства — это специальный CSS-идентификатор <dashed-ident>
, начинающийся с --
. Одновременно к нему применимы правила синтаксиса CSS-идентификатора <ident>
, описанного в стандарте CSS Syntax Module Level 3. По правилам мы можем использовать символы [a-zA-Z0-9]
, дефис -
, подчёркивание _
и другие ASCII-символы, если их экранировать.
Получается, что ошибки нет. Для свойства background-color
браузеры передадут значение 10px
.
:root {
--\*: 10px;
}
.awesome-block {
background-color: tomato;
background-color: var(--\*); /* здесь значение 10px */
}
Мы получили ситуацию, когда после замены пользовательского свойства получается некорректное значение для свойства background-color
. Браузеры это понимают и не могут позволить такому произойти. Они некорректное значение заменят корректным.
Здесь стандартами описаны два сценария. Они зависят от типа свойства. Если оно не позволяет наследовать значение, используется начальное (initial
) значение. Если значение можно получить в результате алгоритма наследования, то так и произойдёт.
В нашем примере используется свойство background-color
. Оно не является наследуемым. По этой причине вместо некорректного значения 10px
подставляется начальное значение, т. е. transparent
.
:root {
--\*: 10px;
}
.awesome-block {
background-color: tomato;
background-color: var(--\*); /* в итоге здесь значение transparent */
}
Правильный ответ — Код корректный. Браузеры вычислят значение transparent
.
▍ Задача №2
Сидел я как-то вечером и думал: «Можно ли добавить обводку к родительскому элементу, когда пользователь сфокусировался на определённом дочернем элементе при помощи клавиатуры?». В итоге я придумал одно решение. Вам нужно его повторить.
В задаче мы будем использовать следующую разметку:
<body>
<div class="awesome-block">
<a href="#0" class="no-call-outline">ссылка не вызывает обводку</a>
<a href="#0" class="call-outline">ссылка вызывает обводку</a>
</div>
</body>
Вам нужно написать селектор, который сработает только на элементе с классом .call-outline
, когда пользователь сфокусировался при помощи клавиатуры, и добавить свойство outline
со значением 3px solid darkolivegreen
к элементу с классом .awesome-block
.
Если пользователь сфокусировался на элемент с классом .no-call-outline
, стили не должны быть добавлены. Также нужно учесть, что если по элементу кликнули мышкой, то стили не должны применяться.
Я оставляю изображения итогового результата. Думаю, так вам будет проще.
Сначала определимся, как можно отследить момент, когда пользователь сфокусировался на дочернем элементе. Поскольку стили должны добавлять к родительскому элементу, то тут напрашивается псевдо-класс :focus-within
.
Всё было бы хорошо, но нет. Данный код будет срабатывать на любом интерактивном элементе. А нам нужно только на том, у которого установлен класс .call-outline
.
Следующий способ отследить попадание фокуса на дочернем элементе основан на использовании псевдо-класса :has
. Мы буквально скажем, что если на элементе сфокусировались, то применяй стили.
.awesome-block:has(.call-outline:focus) {
outline: 3px solid darkolivegreen;
}
Это почти правильное решение. Поскольку в условии задачи сказано, что нужно отлавливать момент фокусировки только при помощи клавиатуры, то псевдо-класс :focus
не подходит. Тут нужен псевдо-класс :focus-visible
.
.awesome-block:has(.call-outline:focus-visible) {
outline: 3px solid darkolivegreen;
}
Так выглядит мой правильный вариант.
▍ Задача №3
Представим, что у нас есть элемент с классом .awesome-block
.
<body>
<div class="awesome-block"></div>
</body>
Он создаёт грид-контейнер с сеткой из шести колонок и четырёх строк.
.awesome-block {
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;
}
У него также есть дочерний псевдо-элемент ::before
. Он растягивается на всю ширину и высоту родительского элемента с помощью свойства inset
.
.awesome-block::before {
content: "";
background-color: darkolivegreen;
position: absolute;
inset: 0;
}
У меня к вам вопрос. Если для псевдо-элемента ::before
добавить свойства grid-column
и grid-row
, продолжит ли он растягиваться на всю ширину и высоту родительского элемента с классом .awesome-block
?
.awesome-block::before {
content: "";
background-color: darkolivegreen;
grid-column: 2 / span 2;
grid-row: 2 / span 2;
position: absolute;
inset: 0;
}
Для правильного ответа нужно вспомнить, какая задача у свойств grid-column
и grid-row
. Она заключается в указании номеров линий, по которым браузеры рассчитают положение дочерних элементов. Другими словами, этими свойствами мы очерчиваем контур, где будет расположен элемент.
В моём примере строка grid-column: 2 / span 2
приведёт к тому, что элемент будет отображаться, начиная от второй линии, отвечающей за колонки, и заканчиваться через две линии. Строка grid-row: 2 / span 2
делает то же самое, только по направлению строк.
После создания контура браузеры начинают использовать его для расчёта координат для свойств top
, right
, bottom
, right
и inset
, если у грид-контейнера установлено свойство position
со значением relative
.
В нашем примере это условие соблюдается, и по этой причине псевдо-элемент ::before
перестаёт растягиваться по размерам родительского элемента .awesome-block
и начинает это делать по созданному свойствами grid-column
и grid-row
контуру.
В итоге правильный ответ — псевдо-элемент ::before
перестаёт растягиваться по родительскому элементу с классом .awesome-block
.
▍ Задача №4
Напоследок я подготовил творческое задание. Мы будем рисовать на CSS флаг Индонезии. Я уже подготовил разметку:
<body>
<div class="indonesia">
<div class="indonesia__group"></div>
<div class="indonesia__group"></div>
</div>
</body>
У флага будет ширина шестьсот пикселей, а высота — четыреста пикселей. Цвета я уже тоже задал.
.indonesia {
width: 600px;
height: 400px;
border: 2px solid;
}
.indonesia__group:first-child {
background-color: #f70000;
}
.indonesia__group:nth-child(2) {
background-color: #fff;
}
Какие есть способы сделать равные две полоски, расположенные друг под другом? Важно учесть, что если высота элемента с классом .indonesia
поменяется, то высота полос динамически подстроится под новое значение.
Первый способ будет основан на использовании свойства flex-grow
.
.indonesia {
display: flex;
flex-direction: column;
}
.indonesia__group {
flex-grow: 1;
}
Главная задача свойства flex-grow
— это растянуть элемент по основной оси. Его значение указывает, в какой пропорции нужно распределять свободное пространство, по которому браузеры будут тянуть элемент.
Поскольку полосы во флаге Индонезии одинаковые, то и пространство должно равномерно растянуться. Это означает, что у элементов с классом .indonesia__group
должно быть одинаковое значение для свойства flex-grow
. Я выбрал 1
.
Второй способ ещё проще, чем первый. Мы просто объявим свойство display
со значением grid
для элемента с классом .indonesia
.
.indonesia {
display: grid; /* ещё можно inline-grid */
}
После применения значения grid
или inline-grid
к элементу, у его дочерних элементов появляется ряд свойств. Одно из них очень полезно для нашей задачи. После создания грид-контейнера, дочерние элементы начинают растягиваться на всю его ширину и высоту. И это свойство динамическое. Если мы изменим высоту грид-контейнера, то высота дочерних элементов тоже обновится.
В третьем способе мы будем работать со свойством inset
.
.indonesia {
position: relative;
}
.indonesia__group {
position: absolute;
inset-inline: 0;
}
.indonesia__group:first-child {
inset-block: 0 50%;
}
.indonesia__group:nth-child(2) {
inset-block: 50% 0;
}
Свойство inset
— это краткая альтернативна для свойств top
, right
, bottom
и left
. Если мы устанавливаем значение 0
, то получаем такое значение сразу со всех сторон. А это приводит к тому, что элемент со свойством position
и значением absolute
занимает всю ширину и высоту родительского элемента с нестатическим значением для свойства position
. Напомню, что это все значения, кроме static
.
Только нам нужно, чтобы первая полоса по высоте занимала пятьдесят процентов. Соответственно нам нужно сделать отступ снизу для первого элемента с классом .indonesia__group
, и сверху для второго элемента. Поэтому я использую значение 50%
.
▍ Заключение
В этой статье мы рассмотрели вопросы, касающиеся следующих тем:
- какие правила синтаксиса есть у пользовательских CSS свойств;
- что делают браузеры, если после замены пользовательского CSS свойства получилось некорректное значение;
- наши новые возможности отслеживать дочерние элементы при разных состояниях с помощью псевдо-класса
:has
; - ограничение псевдо-класса
:focus-within
по сравнению с псевдо-классом:has
; - разницу между псевдо-классами
:focus-visible
и:focus
; - что меняется при позиционировании элементов со свойством
position
и значениемabsolute
внутри грид-контейнера, если для него ещё добавили свойстваgrid-column
иgrid-row
; - свойство дочерних элементов равномерно растягиваться на всю ширину и высоту родительского элемента;
- свойство
inset
является краткой альтернативой для свойствtop
,right
,bottom
иleft
; - значение
0
, установленное для свойствtop
,right
,bottom
иleft
приводит к тому, что элемент cposition: absolute
начинает занимать всё пространство родительского элемента, если у него установлено любое значение для свойстваposition
, кромеstatic
.
Также, пожалуйста, напишите в комментариях, какие вопросы или задачки спросили бы вы. Буду ждать их. Спасибо за чтение!
P.S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.
© 2024 ООО «МТ ФИНАНС»
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻