Хабр, привет! Я стабильно пишу здесь о 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 💻
ссылка на оригинал статьи https://habr.com/ru/articles/859826/
Добавить комментарий