Написание CSS в 2023 году: Отличается ли он от того, что было раньше?

Источник: «Writing CSS In 2023: Is It Any Different Than A Few Years Ago?»
CSS развивается быстрее, чем когда-либо. С учётом всех новых возможностей, которые появились — и ещё появятся — после появления Flexbox и Grid несколько лет назад, меняется и способ написания CSS. В этой статье Geoff Graham рассказывает о том, какие функции оказали наибольшее влияние на его нынешние подходы к CSS, а также о тех, которые не оказали (по крайней мере, пока).

Есть ли в мире фронтенда что-то, развивающееся быстрее, чем CSS? После, казалось бы, долгого затишья после блокбастеров Flexbox и Grid, наблюдение за появлением новых возможностей CSS в течение последних нескольких лет больше напоминает игру в регби по телевизору. Темп захватывающий, но в то же время не подавляющий.

Но изменили ли все эти "колокольчики" и "свистки" на самом деле то, как вы пишете CSS? Новые возможности, безусловно, повлияли на то, как я пишу CSS сегодня, но, возможно, не так радикально, как я ожидал.

И хотя я не видел недостатка в записях в блогах с высокоуровневыми примерами и творческими экспериментами со всеми этими новомодными штуками, которые нам доступны, я ещё не видел практического применения в продакшене или повседневном использовании. Я помню, как Sass начал проникать в учебники по CSS, и часто использовался в качестве основного синтаксиса для примеров и фрагментов кода. Я не вижу такого же органичного внедрения, скажем, логических свойств, а ведь их полная поддержка браузером существует уже около двух лет.

Это не для того, чтобы поносить кого-то или что-то. Мне, например, до чёртиков нравится, как развивается CSS. Многие из новейших функций — это те, которых мы ждали много-много лет. И действительно, некоторые из них нашли своё место в моем CSS. Опять, некардинально, но достаточно, чтобы я получал от написания CSS больше удовольствия, чем когда-либо.

Позвольте мне посчитать, как это сделать.

Все больше и больше контейнерных запросов

Скажу сразу: Я никогда не любил писать медиа-запросы для отзывчивых макетов. Контент по-разному реагирует на ширину области просмотра в зависимости от компонента, в котором он находится. И балансировка контента в одном компоненте всегда была жонглированием с балансировкой контента в других компонентах, что приводило к путанице медиа-запросов в, казалось бы, произвольных точках разрыва. Вложение медиа-запросов в селектор с помощью Sass сделало это терпимым, но не до такой степени, чтобы я наслаждался написанием новых запросов и модификацией существующих каждый раз, когда мне передают новый дизайн с изменениями пользовательского интерфейса.

Использование контейнерных запросов — это правильный ответ для меня. Теперь я могу привязывать дочерние элементы к родительскому контейнеру и полагаться на размер контейнера для определения места смещения макета, не обращая внимания на другие окружающие компоненты.

Ещё одна вещь, которая мне нравится в контейнерных запросах, — это то, что они очень похожи на CSS. Определение контейнера непосредственно в селекторе соответствует естественному синтаксису "свойство-значение" и помогает мне избежать предварительных математических вычислений для определения точек разрыва.

.parent {
container-type: inline-size;
}

@container (min-width: 600px) {
.child {
align-self: center;
}
}

Я по-прежнему использую медиа-запросы для отзывчивых макетов, но стараюсь применять их для больших макетов, состоящих из собранных контейнеров. Точки останова более предсказуемы (и могут более точно нацеливаться на конкретные устройства), когда нет необходимости учитывать, что происходит внутри каждого отдельного контейнера.

Узнать больше о контейнерных запросах

Группировка стилей в слои

Мне нравится этот способ управления каскадом! Теперь, если у меня есть сброс или сторонний CSS из фреймворка или что-то ещё, я могу обернуть их в каскадный слой и бросить в самый низ файла, чтобы мои собственные стили были впереди и в центре.

Мне ещё не доводилось создавать что-либо с использованием каскадных слоёв, но теперь использую их практически в каждой демонстрации CodePen. Поддержка браузерами есть, так что это не проблема. Дело в том, что я все ещё полагаюсь на Sass в проектах ради некоторых возможностей, и поддержка стилей в парциальных файлах все ещё кажется приятной, по крайней мере, для такого рода работы.

Но в изолированной демонстрации, где все мои стили находятся в одном месте, как в CodePen? Да, все каскадные слои, пожалуйста! На самом деле мне нужен только один слой для базовых стилей, поскольку неслоистые стили имеют более высокую специфичность, чем слоистые. Таким образом, мои стили, специфичные для демо-версии, остаются чистыми, не загромождёнными и все ещё могут переопределять базовые стили, расположенные сверху, что делает доступ к ним гораздо более удобным.

body {
display: grid;
gap: 3rem;
place-items: center;
}

@layer base {
body {
font-size: 1.25rem;
line-height: 1.35;
padding: 3rem;
}
}

Узнать больше о каскадных слоях

:is() и :where()

Я, конечно, обращаюсь к этим новым реляционным псевдо-селекторам, но не совсем из-за преимуществ условного выбора элементов на основе связей.

Вместо этого я чаще всего использую их для управления спецификой. Но в отличие от каскадных слоёв, я действительно использую их в продакшене.

Почему? Потому что при использовании :is() специфичность определяется не главным селектором, а наиболее специфичным селектором в списке его аргументов.

/* Специфичность: 0 1 1 */
:is(ol, .list, ul) li {}

/* Специфичность: 0 0 2 */
ol li {}`

Селектор .list даёт первому набору правил более высокий балл специфичности, то есть он побеждает второй набор правил, даже если первый набор правил находится выше в каскаде.

С другой стороны, специфичность :where() равна нулю, поэтому она не увеличивает общую оценку селектора, в котором находится. Просто совершенно неважно, что находится в его списке аргументов. По той же причине, по которой я использую :is() для добавления специфичности, я использую :where() для её устранения. Мне нравится, когда специфичность в целом невелика, поскольку я все ещё хочу, чтобы каскад работал с минимальным трением, и :where() делает это возможным, особенно при определении глобальных стилей.

Отличным примером является обёртывание :not() внутри :where(), чтобы не допустить повышения специфичности :not():

/* Specificity: 0 0 0 */
:where(:not(.some-element)) {}

В совокупности :is() и :where() не только помогают управлять конкретикой, но и снимают некоторую когнитивную нагрузку с именования.

Я отношусь к тем людям, которые до сих пор любят синтаксис БЭМ. Но именование — одна из самых сложных вещей в нем. Я часто сталкиваюсь с тем, что у меня заканчиваются имена, которые помогают описать функцию элемента и его связь с окружающими элементами. Благодаря силе :is() и :where(), позволяющей устранять специфику, я могу меньше полагаться на сложные имена классов и больше на селекторы элементов.

Узнать больше о :is() и :where()

Новый синтаксис функции цвета

Обновлённый синтаксис таких цветовых функций, как rgb() и hsl() (а также развивающихся oklch() и oklab()), не является тем привлекающим внимание заголовком, который приводит к oo и оу, но с его помощью определять значения цветов стало гораздо удобнее.

Во-первых, мне никогда не приходится обращаться к rgba() или hsla(), когда мне нужно значение альфа. На самом деле, я всегда использовал их независимо от того, нужна ли мне альфа или нет, потому что не хотел мучиться с выбором версии.

color: hsl(50deg, 100%, 50%);

/* Тоже самое */
color: hsla(50deg, 100%, 50% / 1)

Да, написание лишних a, / и 1 стоило того, чтобы не думать о том, какую функцию использовать.

Но обновлённый цветовой синтаксис подобен медоеду: ему просто все равно. Его не волнует лишняя буква a в имени функции. Его даже не волнуют запятые.

color: hsl(50deg 100% 50% / .5);

Так что, да. Это определённо изменило мой подход к написанию цветов в CSS.

Мне не терпится начать использовать новые цветовые пространства oklch() и oklab(), которые теперь полностью поддерживаются браузерами!

Узнать больше о возможностях цвета в CSS 4

Выявление пользовательских предпочтений

Думаю, многие из нас были в восторге, когда появились медиа-запросы, учитывающие предпочтения пользователя при отображении информации на экране, в том числе предпочитаемую им цветовую схему для быстрого создания тёмных и светлых интерфейсов.

:root {
--bg-color: hsl(0deg 0% 100%);
--text-color: hsl(0deg 0% 0%);
}

@media (prefers-color-scheme: dark) {
:root {
--bg-color: hsl(0deg 0% 0%);
--text-color: hsl(0deg 0% 100%);
}
}

body {
background: var(--bg-color);
color: var(--text-color);
}

Но именно запрос prefers-reduced-motion изменил мой CSS больше всего. Это первое, о чем я думаю, когда проект включает в себя CSS-анимацию и переходы. Мне нравится идея, что предпочтение уменьшенного движения не означает отказ от всей анимации, поэтому я часто использую prefers-reduced-motion, чтобы замедлить все, когда это предпочтительно. Это означает, что у меня есть нечто подобное (обычно в каскадном слое для базовых стилей):

@layer base {
:root {
--anim-duration: 1s;
}

/* Уменьшение движения по умолчанию */
body {
animation-duration: --anim-duration;
transition: --anim-duration;
}

/* Опция повышения движения */
@media screen and (prefers-reduced-motion: no-preference) {
body {
--anim-duration: .25s;
}
}
}

Узнать больше о запросах пользовательских предпочтений

Определение цветовых палитр

Я использовал переменные для определения и назначения цветов с тех пор, как перешёл на Sass, и был в восторге от появления пользовательских свойств CSS. Я давал общие имена цветам в палитре, а затем передавал их в переменные с более функциональными именами.

/* Цветовая палитра */
--red: #ff0000;
/* и т.д. */

/* Фирменные цвета */
--color-primary: var(--red);
/* и т.д. */

Я и сейчас так делаю, но теперь ещё больше абстрагируюсь, используя функции цвета в проектах с большими палитрами:

:root {
/* Первичный цвет HSL */
--h: 21deg;
--s: 100%;
--l: 50%;

--color-primary: hsl(var(--h) var(--s) var(--l) / 1);
}

.bg-color {
background: var(--color-primary);
}

.bg-color--secondary {
--h: 56deg;
background: hsl(var(--h) var(--s) var(--l) / 1);
}

Слишком абстрактно? Возможно. Но для тех проектов, где у вас может быть десять различных сортов красного, оранжевого, жёлтого и т.д., приятно иметь такой уровень тонкого контроля для работы с ними. Возможно, в функции color-mix() есть и другие возможности, которые я ещё не исследовал.

Узнать больше об определении цветовой палитры

Что я не использую

Похоже, я пишу CSS немного иначе, чем раньше! Этого не чувствуется, но, возможно, это связано с тем, что есть много других новых возможностей, которые я сейчас не использую. Количество новых возможностей, используемых мной, намного, намного меньше, чем количество возможностей, которые ещё предстоит освоить, будь то из-за поддержки браузерами или потому, что я просто ещё не дошёл до этого.

CSS Nesting/CSS Вложения

Я с нетерпением жду этого события. Оно может стать переломным моментом, когда я полностью откажусь от Sass в пользу ванильного CSS. На момент написания этой статьи Firefox ожидает его поддержки, так что это может произойти уже совсем скоро.

Style Queries/Запросы стиля

Я не скрывал, что применение стилей к элементам на основе стилей других элементов — это то, что меня действительно интересует. Возможно, это скорее академический интерес, поскольку конкретные примеры использования стилевых запросов мне не известны. Возможно, ситуация изменится, когда они получат поддержку в браузерах и мы увидим в блогах больше записей, в которых умные люди экспериментируют с ними.

:has()

Я обязательно воспользуюсь этим, когда Firefox будет поддерживать его. До тех пор я просто пробовал его использовать и наслаждался тем, как другие экспериментируют с ним. Но без полной поддержки это не изменило мой подход к написанию CSS. Однако я ожидаю, что это произойдёт, ведь как может быть плохой возможность выбора родительского элемента на основе дочернего, который он содержит?

Динамические единицы области просмотра

Я начал добавлять их в свои стили с тех пор, как они получили широкую поддержку в конце 2022 года. Как и стилевые запросы, я вижу только ограниченное применение — прежде всего, при установке элементов на полную высоту на мобильном устройстве. Поэтому вместо height: 100vh я все чаще начинаю писать height: 100dvh. Думаю, это повлияло на то, как я пишу CSS, пусть и незаметно.

Синтаксис диапазона медиа-запросов

Честно говоря, я просто не задумывался, что существует более удобный способ написания отзывчивых медиа-запросов во вьюпорте. Я знаю об этом, но не сделал это частью своего повседневного CSS по другой причине, кроме как по незнанию.

Цветовые пространства oklch/oklab

oklch(), безусловно, станет моей основной функцией для работы с цветом. Она получила широкую поддержку в марте этого года, поэтому у меня было всего несколько месяцев и ни одного проекта для её использования. Но, учитывая время, я ожидаю, что она станет наиболее широко используемым способом определения цветов в моем CSS.

Единственная проблема, заключается в том, что oklch() несовместима с другой цветовой функцией, от которой я в восторге…

color()

Теперь он широко поддерживается, начиная с мая 2023 года! Это слишком новая информация, чтобы попасть в мой повседневный CSS, но можно не сомневаться, что она попадёт туда. Возможность использовать любое цветовое пространство — будь то sRGB, Display P3 или Rec2020 — гораздо приятнее, чем необходимость обращаться к конкретной функции цвета, по крайней мере, для цветов в цветовом пространстве с каналами RGB (именно поэтому color() несовместима с oklch() и другими цветовыми пространствами, отличными от RGB).

--primary-color: color(display-p3 1 0.4 0);

Я не люблю значения RGB, потому что их трудно понять, в отличии, скажем, от HSL. Уверен, что в большинстве случаев я буду использовать oklch() или hsl() именно по этой причине. Жаль, что мы не можем сделать что-то подобное:

/* 👎 */
--primary-color: color(oklch 70% 0.222 41.29);

Вместо этого мы должны сделать вот что:

/* 👍 */
--primary-color: oklch(70% 0.222 41.29);

Смущает то, что дисплей P3 не имеет собственной функции, как OKLCH:

/* 👎 */
--primary-color: display-p3(1 0.434 0.088);

Мы вынуждены использовать color() для доступа к Display P3. Это расходится с OKLCH/OKLAB, где мы вынуждены обращаться к этим специфическим функциям.

Может быть, когда-нибудь у нас появится глобальная функция color(), которая будет поддерживать их все! До тех пор в моем CSS будут использоваться и color(), и специфические функции, такие как oklch(), и я буду решать, что лучше для того, над чем я работаю.

Сюда можно отнести и color-mix(), так как он получил полную поддержку одновременно с color(). Пока это не то, что я пишу регулярно. Но я обязательно поиграю с ней, скорее всего, для создания цветовых палитр.

Почётные упоминания

Было бы довольно сложно прокомментировать все новые возможности CSS, появившиеся за последние пять с лишним лет. Если говорить о том, какие функции я не использую в своей повседневной работе, то основной темой является то, что они просто слишком новые или не поддерживаются браузерами. Это не означает, что я не буду их использовать (скорее всего, буду!). Но пока что я просто наблюдаю за ними со стороны или просто развлекаюсь.

К ним относятся:

А как насчёт вас? Вы, наверное, сейчас пишете CSS иначе, чем пять лет назад? По-другому ли вы обращаетесь с каскадом? Пишете ли вы больше ванильный CSS, чем обращаетесь к препроцессору? Как насчёт типографики, например, управления высотой и масштабом строк? Расскажите, а ещё лучше покажите, как вы используете CSS в наши дни.

Дополнительные материалы

Предыдущая Статья

firstOrCreate() vs createOrFirst()

Следующая Статья

JavaScript итераторы и генераторы: Полное руководство