Привет, Хабр. Я продолжаю рассказывать про неизвестные широкому кругу разработчиков 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-фишки вы используете, о которых другие могут не знать. Буду ждать их. Спасибо за чтение!
P.S. Присоединяйтесь к моему ТГ каналу CSS isn't magic. Ссылка в профиле.
Telegram-канал со скидками, розыгрышами призов и новостями IT ?
Комментарии (7)
ikratkiy
09.04.2024 15:12+1А зачем в примере с уникальным значением отступа для последнего элемента вообще дополнительный класс? Есть же отличный селектор
:last-child.
И не нужно плодить никаких сущностей.melnik909 Автор
09.04.2024 15:12Он не последний. Отступ может быть у любого элемента. Если нравится через псевдо-класс, нет проблем. Суть таже.
vanxant
... не работает по поддерживаемому формату файла. По крайней мере, в тех браузерах, где это реально нужно (например, старые айфоны). Браузер увидит avif, честно его скачает, после чего скажет "фигня какая-то" и картинку не нарисует. Проверено.
Как лечить: смотрим заголовок Accept в начале сессии и вешаем на body классы типа supports-avif и supports-webp. Дальше в CSS указываем
.supports-webp .my-elem { background-image: url(...webp) }
Шрифты local сломаны с момента рождения (лет 15 назад). У юзера под заказанным именем может стоять что угодно. В частности, я напарывался а) на шрифты без кириллических символов и б) на шрифты-тёзки, тех же Open Sans-ов до гугла уже было штук 5 разных.
goose228
Вы абсолютно правы, удивился что src: local кто-то достал из чулана в 2к24
melnik909 Автор
Я может задам глупый вопрос. Как получается, что у пользователей стоит то, что не ожидалось?
vanxant
софт, который ставит в систему свои шрифты. Если он не русифицирован, то кириллицы там скорее всего не будет, даже если она есть в полном оригинальном шрифте. И я уже не говорю про версии (да, шрифты тоже часто обновляют)
дизайнеры и прочие близкие по духу товарищи, которые тянут всё, что плохо лежит, потому что ну надо там быстро что-то поправить, а в брендбуке заказчика прописана какая-то чуть ли не индивидуальной разработки дичь
melnik909 Автор
Спасибо