Использование CSS :has() в реальных условиях
:has()
, используемых в реальных клиентских проектах.Сейчас много шума вокруг нового CSS псевдокласса :has()
. Это то, о чем мы мечтали годами: возможность выбирать родительские элементы!
Полезная ментальная модель для :has()
заключается в том, что вы запрашиваете состояние и/или наличие детей родителя, а не выбираете родителя из самих детей. Мне это нравится. В этом кроется огромный смысл.
Я не на 100% уверен, что :has()
— это та серебряная пуля, о которой говорят другие. Я всё ещё регулярно использую исключения CUBE, но я также нахожусь в привилегированном положении, когда проекты, над которыми я работаю в студии, не ограничивают доступ к разметке. Я считаю :has()
более удобным для небольших доработок, но если у вас нет доступа к разметке, то это действительно серебряная пуля.
Учитывая всё это, я решил привести несколько примеров использования :has()
в реальных проектах клиентов, чтобы вы могли взглянуть на них в реальном мире. Давайте начнём.
Корректировка макета баннера
В дизайн системе, над которой мы работаем для клиента, есть довольно простой баннер. Недавно его обновили, и он стал непривлекательным, поэтому пришлось создать новый вариант шаблона.
Единственным отличием от шаблона по умолчанию стало наличие элемента <button>
, так что быстрый запрос :has()
позволит нам в мгновение ока применить гибкий макет.
.banner {
background: var(--color-primary);
color: var(--color-light);
font-weight: var(--font-bold);
text-align: center;
}
.banner:has(button) {
display: flex;
justify-content: space-between;
gap: var(--space-s);
text-align: revert;
}
Flex метки с дочерними элементами input
Это связано с тем, что мне нравится следующий шаблон для меток. Потому что я люблю, чтобы они были inline
элементами, но поля формы, которые следуют за ними, должны переноситься на новую строку.
label::after {
content: "\A";
white-space: pre;
}
Однако для меток, содержащих поля ввода, такие как чекбоксы и радио, желательно отображать их в виде макетов flexbox. Исторически это требовало добавления класса (или нескольких в кодовых базах ASS), но я обновил глобальные стили, чтобы использовать их вместо этого.
label:has(input) {
display: flex;
align-items: flex-start;
gap: var(--space-s);
}
Примечание. Причина, по которой я ищу только input
, заключается в том, что я никогда не помещаю текстовые поля внутри меток. Если вы делаете так, то вам следует обновить селектор: label:has(:is(input[type="checkbox"], input[type="radio"]))
.
Выделение родительских элементов, когда целью являются их потомки
Это очень быстро и просто. Если у вас есть элемент с id
, вы можете вызвать его :target
состояние, добавив его id к URL с #
, например, так: https://example.com/#my-element
.
Исторически сложилось так, что вы не могли применять стили к родительскому элементу элемента, когда он является целевым, но теперь вы можете это сделать с помощью :has()
.
section:has(:target) {
background: var(--color-light-shade);
border: 2px solid var(--color-primary);
}
Затемнение соседних элементов при наведении курсора на элемент
Это паттерн, существует в Интернете уже целую вечность. Идея заключается в том, что при наведении курсора на элемент все его родственные элементы затемняются, так что визуальный фокус пользователя становится более целенаправленным.
Мы уже давно научились делать это с помощью CSS, но для достижения эффекта использовались довольно сложные селекторы. Многие подходы приводили к мерцанию или затемнению всех элементов, если курсор случайно оказывался между элементами.
Но с :has()
это больше не так!
.tiles:has(:hover) .tile:not(:hover) {
opacity: 70%;
}
Прелесть этого селектора в том, что он чётко показывает, что происходит.
Подведение итогов
Да, в этой статье нет ничего сложного или навороченного, но я хотел показать несколько удобных реальных способов использования :has()
. Если вы действительно хотите разобраться в :has()
, настоятельно рекомендую ознакомиться с интерактивным руководством Ахмада. Это просто фантастика!
P.S. И последний маленький трюк. На сайте Piccalilli абзацы в блоке .post
ограничены max-width: 60ch;
. Однако это не идеально для примеров, так что...
.post p:has(code-pen) {
max-width: unset;
}