
Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков CSS-фишки. Я отбираю их так, чтобы они были полезны в разного рода проектах. Неважно, верстаете ли вы сайт для малого бизнеса или создаёте супермодное React-приложение. Они поддерживаются большинством браузеров. Отдельно отмечу, что я не считаю IE11 современным браузером. По этой причине я не учитывал его.
Сегодня мы рассмотрим:
- загрузку фоновых изображений для экранов с повышенной плотностью пикселя с помощью функции
image-set(); - как с помощью неё же ускорить загрузку страницы;
- можно ли использовать нестандартный шрифт без его загрузки;
- чем полезен псевдокласс
:focus-withinпри вёрстке кастомных чекбоксов; - мой любимый лайфхак на основе пользовательских CSS-свойств.
Больше не буду затягивать. Давайте посмотрим, что я вам подготовил.
▍ Функция image-set() и новые оптимизации загрузки фоновых изображений
За 13 лет разработки атрибут srcset и элемент <picture> были одними из самых долгожданных для меня фишек. Я помешан на оптимизации. А тогда уже нужно было использовать изображения в разных условиях. Мобильные устройства, экраны с повышенной плотностью пикселя или как способ ускорения загрузки страницы. Во всех этих случаях я применял новые возможности. Так что они очень нравились мне.
Только так нельзя было делать с фоновыми изображениями, вставленными через свойство background-image. Например, нельзя было использовать дескриптеры, как у атрибута srcset. В итоге по-прежнему приходилось загружать одно тяжёлое по весу изображение для всех устройств. Хорошо, что теперь везде работает функция image-set().
Я не помню, когда она появилась. Точно знаю, что с префиксом она работала в Google Chrome в 2012 году. А с 2021 года поддержка стала позволять использовать её в продакшене. С помощью функции мы можем подсказать браузерам, какое изображение им загружать в зависимости от условий. В целом её работа напоминает работу атрибута srcset.
Например, я скажу браузерам загружать разные изображения для экранов с обычной плотностью пикселя и повышенной.
.container { background-image: image-set( url("eiffel-tower-1x.jpg") type("image/jpg") 1x, url("eiffel-tower-2x.jpg") type("image/jpg") 2x ); }
В этом примере браузеры, используя дескриптер 2х, загрузят изображение eiffel-tower-2x.jpg для экранов с двойной и более плотностью пикселя. А для обычных будет загружено изображение eiffel-tower-1x.jpg. Здорово же? А это ещё не всё! Есть другой пример, где функция image-set() будет полезной.
Скорее всего, вы слышали о формате изображений WEBP и AVIF. Если нет, то обязательно прочитайте. Сейчас я ограничусь тем, что их используют для ускорения загрузки страницы, потому что изображения этих форматов в большинстве случаев весят меньше, чем JPG и PNG.
Конечно, есть нюанс с разной поддержкой браузерами. Эту проблему решает элемент <picture>. По этой причине в интернете можно встретить примерно следующий фрагмент кода:
<body> <picture> <source srcset="eiffel-tower.avif" type="image/avif"> <source srcset="eiffel-tower.webp" type="image/webp"> <img src="eiffel-tower.jpg" alt="Эйфелева башня. Вид с реки Сены"> </picture> </body>
Если браузер поддерживает AVIF-формат, то загрузится первое изображение. Если нет, то браузер проверит, можно ли погрузить WEBP-версию. Если можно, то она будет загружена. Если нет, то уже JPG-версия.
Самый класс в том, что мы можем сделать то же самое для фоновых изображений с помощью функции image-set().
.container { background-image: image-set( url("eiffel-tower.avif") type("image/avif"), url("eiffel-tower.webp") type("image/webp"), url("eiffel-tower.jpg") type("image/jpeg")); }
Алгоритм работы точно такой же, как в случае с элементом <picture>. Надеюсь, этот подход станет постоянным для вас. Только представьте, насколько быстрее станет ваше приложение. Оно будет как Флэш!
▍ Функция local() экономит трафик пользователя
При оптимизации скорости загрузки страницы, кроме изображений, ещё значительным пунктом являются шрифты. Поскольку они весят много. И каково было моё удивление, когда я узнал о существовании функции local().
В чём суть. С помощью функции мы можем проверить, установлен ли нестандартный шрифт на устройстве пользователя. Если установлен, то не загружать его по сети. Для этого её нужно использовать при объявлении правила @font-face. Например, я подключаю шрифт Cherry Bomb One.
@font-face { font-family: 'Cherry Bomb One'; font-style: normal; font-weight: 400; font-display: swap; src: local("CherryBombOne Regular"), local("CherryBombOne-Regular"), url("CherryBombOne-Regular.woff2") format('woff2'); }
В этом случае браузеры загрузят его только когда шрифт CherryBombOne Regular не установлен на устройстве пользователя. Очень простой трюк, но суперполезный.
▍ Более надёжные кастомные чекбоксы с псевдоклассом focus-within
Каждый верстальщик делал или сделает кастомный чекбокс. Как у любого элемента управления, нам нужно стилизовать состояние focus. Большинство примеров строятся на использовании соседнего родственного комбинатора +.
<body> <div class="custom-checkbox"> <input id="cb" type="checkbox" class="custom-checkbox__input"> <label class="custom-checkbox" for="cb">Запомнить данные</label> </div> </body>
.custom-checkbox__input:focus + .custom-checkbox__label::before { outline: 3px solid #222; }
Есть специалисты, которые нашли недостаток этого способа. По их мнению, часто происходит так, что люди случайно вставляют элемент <input> в другое место. По этой причине селектор перестаёт работать.
Думая над решением этой проблемы, ко мне пришла мысль: «А почему бы не заменить соседнего родственного комбинатора + на псевдокласс focus-within?». Ведь данный псевдокласс срабатывает, когда сам элемент или кто-то из его дочерних элементов находится в фокусе. И в случае использования его в нашей задаче, получится так, что позиция элемента <input> неважна.
Заменим фрагмент кода с комбинатором + на псевдоэлемент :focus-within.
.custom-checkbox:focus-within .custom-checkbox__label::before { outline: 3px solid #222; }
Мне кажется, даже код выглядит «красивее». А вы как думаете? Напишите в комментариях.
▍ Пользовательские CSS-свойства спасают от «причуд» дизайнеров
Я думаю, каждый верстальщик хоть раз мысленно «бесился» от макета дизайнера. Порой я хватался за голову от их творческих идей. Благо из этого опыта я вынес очень полезный лайфхак.
Представим абстрактные разделы на странице. Например, «О нас», «Наши проекты» и «Контакты».
<body> <section class="section" aria-labelledby="about-us-heading"> <h2 id="about-us-heading">О нас</h2> <!-- здесь контент раздела --> </section> <section class="section" aria-labelledby="projects-heading"> <h2 id="projects-heading">Наши проекты</h2> <!-- здесь контент раздела --> </section> <section class="section section_size-l" aria-labelledby="contacts-heading"> <h2 id="contacts-heading">Контакты</h2> <!-- здесь контент раздела --> </section> </body>
Дизайнер захотел так, чтобы у первых двух разделов отступ от верхней границы раздела и заголовка был 2.5rem, а у последнего — 5rem. Я создам класс .section, в котором определю наиболее часто встречающееся значение 2.5rem. А для значения 5rem буду использовать класс section_l.
.section { padding-top: 2.5rem; } .section_size-l { padding-top: 5rem; }

Всё работает, как задумывалось. Только в коде есть проблема. Если случайно правила поменяются местами, то у нас будет ошибка. Значение 2.5rem переопределит значение 5rem. В итоге у раздела «Контакты» будет неправильный отступ.
.section_size-l { padding-top: 5rem; } .section { padding-top: 2.5rem; }

В такие моменты к нам на выручку приходят пользовательские CSS-свойства. При правильном их использовании мы можем перестать зависеть от порядка правил. Всё сводится к нескольким деталям.
Во-первых, нам нужно определить их для основного элемента.
.section { padding-top: var(--section-padding-gap); }
Второй шаг заключается в том, что нужно определить значение по умолчанию. Я предлагаю использовать наиболее часто встречающиеся. В моём примере это 2.5rem.
.section { padding-top: var(--section-padding-gap, 2.5rem); }
Последний шаг — определить значение для класса .section_l не через свойство padding, а через пользовательское свойство --section-padding-gap.
.section { padding-top: var(--section-padding-gap, 2.5rem); } .section_size-l { --section-padding-gap: 5rem; }
Осталось проверить, будет ли всё работать, если поменять местами правила. Слушайте, а давайте добавим ещё один размер отступа. Устроим полную проверку.
Создам класс .section_size-xl и добавлю его для раздела «Наши проекты».
<body> <section class="section" aria-labelledby="about-us-heading"> <h2 id="about-us-heading">О нас</h2> <!-- здесь контент раздела --> </section> <section class="section section_size-xl" aria-labelledby="projects-heading"> <h2 id="projects-heading">Наши проекты</h2> <!-- здесь контент раздела --> </section> <section class="section section_size-l" aria-labelledby="contacts-heading"> <h2 id="contacts-heading">Контакты</h2> <!-- здесь контент раздела --> </section> </body>
.section_size-l { --section-padding-gap: 5rem; } .section { padding-top: var(--section-padding-gap, 2.5rem); } .section_size-xl { --section-padding-gap: 7.5rem; }

Отлично. Работает. Данный лайфхак полезен при вёрстке новых проектов или при рефакторинге. Им можно задавать отступы, размеры текста, цвет, да вообще всё. Я использовал его для лендингов, дизайн-систем и проектов с сотней компонент. Он выручал меня везде.
▍ Заключение
Давайте подведём итог. Сегодня мы можем с помощью CSS:
- использовать дескрипторы плотности пикселя экрана для отображения специальных фоновых изображений в свойстве
background-image; - загружать более лёгкие форматы изображения при оптимизации скорости загрузки страницы;
- подсказать браузерам, что нужно проверить, установлен ли нестандартный шрифт на пользовательском устройстве, чтобы избежать его загрузки;
- сделать более надёжными кастомные чекбоксы;
- использовать пользовательские CSS-свойства для упрощения поддержки кода.
Также, пожалуйста, напишите в комментариях, какие CSS-фишки вы используете, о которых другие могут не знать. Буду ждать их. Спасибо за чтение!
Другие статьи из серии можно найти по тегу «sm909_unknown_css».
P.S. Присоединяйтесь к моему ТГ каналу CSS isn't magic. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT 💻

