Состояние CSS 2022

Современные и будущие возможности веб-стилизации, представленные на Google IO 2022, а также некоторые дополнительные функции.

2022 год обещает стать одним из лучших в истории CSS как с точки зрения новых функций, так и с точки зрения совместных релизов новых функций для разных браузеров, с общей целью внедрения 14 новых функций!

Обзор

Этот пост представляет собой статью, основанную на докладе, прочитанном на конференции Google IO 2022. Он не претендует на подробное описание каждой функции, а скорее является введением и кратким обзором, призванным пробудить ваш интерес, предоставляя скорее общую информацию, чем углубляясь в детали. Если вас это заинтересовало, в конце раздела вы найдете ссылки на дополнительные ресурсы.

Оглавление

Воспользуйтесь списком ниже, чтобы перейти к интересующим вас темам:

Совместимость с браузерами

Основная причина, по которой так много функций CSS планируется выпускать совместно, заключается в усилиях Interop 2022. Прежде чем изучать усилия Interop, важно ознакомиться с усилиями Compat 2021 .

Совместимость 2021

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

  1. sticky позиционирование
  2. размер aspect-ratio
  3. flex компоновка
  4. grid разметка
  5. transform позиционирования и анимации

Результаты тестирования улучшились по всем показателям, что свидетельствует о повышении стабильности и надежности. Поздравляем команды, принявшие участие в этом проекте!

Взаимодействие 2022

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

  1. @layer
  2. Цветовые пространства и функции
  3. Сдерживание
  4. <dialog>
  5. Совместимость форм
  6. Прокрутка
  7. Подсеть
  8. Типография
  9. Блоки Viewport
  10. Веб-совместимость

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

Новинка 2022 года

Неудивительно, что на состояние CSS 2022 существенно повлияла работа над Interop 2022.

Каскадные слои

Browser Support

  • Chrome: 99.
  • Край: 99.
  • Firefox: 97.
  • Сафари: 15.4.

Source

До появления @layer порядок загрузки таблиц стилей имел очень важное значение, поскольку стили, загруженные последними, могли перезаписывать ранее загруженные стили. Это приводило к необходимости тщательного управления входными таблицами стилей, когда разработчикам приходилось загружать менее важные стили первыми, а более важные — позже. Существуют целые методологии, помогающие разработчикам управлять этим приоритетом, например, ITCSS .

С помощью @layer в файле конфигурации можно заранее определить слои и их порядок. Затем, по мере загрузки, загрузки или определения стилей, их можно размещать внутри слоя, что позволяет сохранить важность переопределений стилей, но без тщательно управляемой организации загрузки.

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

Инструменты разработчика Chrome полезны для визуализации того, какие стили поступают из каких слоев:

Скриншот боковой панели «Стили» в инструментах разработчика Chrome, демонстрирующий, как стили отображаются в новых группах слоев.

Ресурсы

Подсеть

Browser Support

  • Chrome: 117.
  • Край: 117.
  • Firefox: 71.
  • Сафари: 16.

Source

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

После subgrid дочерний элемент сетки может перенимать столбцы или строки родительского элемента и выравнивать себя или свои дочерние элементы по ним!

В приведенном ниже примере элемент body создает классическую сетку из трех столбцов: средний столбец называется main , а левый и правый столбцы fullbleed . Затем каждый элемент в body, <nav> и <main> , заимствует именованные строки из body, устанавливая grid-template-columns: subgrid .

​​body {
  display: grid;
  grid-template-columns:
    [fullbleed-start]
    auto [main-start] min(90%, 60ch) [main-end] auto
    [fullbleed-end]
  ;
}

body > * {
  display: grid;
  grid-template-columns: subgrid;
}

Наконец, дочерние элементы <nav> или <main> могут выравниваться и изменять свой размер, используя столбцы и строки, занимающие всю fullbleed , а также main столбцы.

.main-content {
  grid-column: main;
}

.fullbleed {
  grid-column: fullbleed;
}

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

Скриншот демонстрации подсетки с использованием инструментов наложения сетки в Chrome для отображения линий, заданных CSS.

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

Скриншот панели «Элементы» в инструментах разработчика Chrome, на котором указано, какие элементы имеют сеточную или подсеточную компоновку.
Скриншот из инструментов разработчика Firefox

Ресурсы

Запросы контейнеров

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Сафари: 16.

Source

До появления @container элементы веб-страницы могли реагировать только на размер всей области просмотра. Это отлично подходит для макромакетов, но для микромакетов, где внешний контейнер не занимает всю область просмотра, подстройка макета становится невозможной.

После @container элементы могут реагировать на размер или стиль родительского контейнера! Единственное условие — контейнеры должны объявить себя возможными целями запросов, что является небольшим требованием для получения значительной выгоды.

/* establish a container */
.day {
  container-type: inline-size;
  container-name: calendar-day;
}

Именно эти стили позволяют запрашивать данные из столбцов «Пн», «Вт», «Ср», «Чт» и «Пт» в следующем видео с помощью элементов событий.

Демонстрация от Уны Кравец

Вот CSS-код для запроса размера контейнера calendar-day и последующей настройки макета и размеров шрифта:

@container calendar-day (max-width: 200px) {
  .date {
    display: block;
  }

  .date-num {
    font-size: 2.5rem;
    display: block;
  }
}

Вот ещё один пример: один из элементов книги подстраивается под пространство, доступное в столбце, в который его перетаскивают:

Демонстрация от Макса Бёка

Уна права, оценивая ситуацию как новую адаптивность . При использовании @container приходится принимать множество интересных и важных дизайнерских решений.

Ресурсы

accent-color

Browser Support

  • Chrome: 93.
  • Край: 93.
  • Firefox: 92.
  • Сафари: 15.4.

Source

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

После добавления accent-color одна строка CSS задает фирменный цвет для встроенных компонентов. Помимо оттенка, браузер интеллектуально выбирает подходящие контрастные цвета для вспомогательных частей компонента и адаптируется к цветовой схеме системы (светлая или темная).

/* tint everything */
:root {
  accent-color: hotpink;
}

/* tint one element */
progress {
  accent-color: indigo;
}

Для сравнения рядом расположены HTML-элементы со светлой и темной акцентной отделкой.

Чтобы узнать больше об accent-color , ознакомьтесь с моей статьей на web.dev, где я рассматриваю множество других аспектов этого полезного свойства CSS.

Ресурсы

Цветовые уровни 4 и 5

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

Однако не в 2022 году — CSS предлагает ряд новых цветовых функций и пространств: - Цвета, достигающие возможностей цветопередачи HD-дисплеев. - Цветовые пространства, соответствующие заданным параметрам, например, воспринимаемой однородности. - Цветовые пространства для градиентов, которые кардинально меняют результаты интерполяции. - Цветовые функции, помогающие смешивать и контрастировать цвета, а также выбирать, в каком цветовом пространстве выполнять работу.

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

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

@media (dynamic-range: high) {
  .neon-pink {
    --neon-glow: color(display-p3 1 0 1);
  }
}

@supports (color: lab(0% 0 0)) {
  .neon-pink {
    --neon-glow: lab(150% 160 0);
  }
}

hwb()

Browser Support

  • Chrome: 101.
  • Edge: 101.
  • Firefox: 96.
  • Сафари: 15.

Source

Аббревиатура HWB расшифровывается как оттенок, белизна и чернота. Она представляет собой удобный для человека способ обозначения цвета, поскольку это всего лишь оттенок и количество белого или черного для осветления или затемнения. Художники, смешивающие цвета с белым или черным, могут оценить это дополнение к цветовой синтаксической системе.

Использование этой цветовой функции позволяет получить цвета из цветового пространства sRGB, аналогично HSL и RGB. Что касается новинок 2022 года, то это не добавляет новые цвета, но может упростить некоторые задачи для любителей синтаксиса и ментальной модели.

Ресурсы

Цветовые пространства

Цвета представляются в цветовом пространстве. Каждое цветовое пространство предлагает различные особенности и компромиссы при работе с цветом. В некоторых пространствах все яркие цвета располагаются вместе; в других они выстраиваются в первую очередь в зависимости от их светлости.

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

color-mix()

Browser Support

  • Chrome: 111.
  • Край: 111.
  • Firefox: 113.
  • Сафари: 16.2.

Source

До появления color-mix() разработчикам и дизайнерам требовались препроцессоры, такие как Sass, для смешивания цветов до того, как их увидит браузер. Большинство функций смешивания цветов также не предоставляли возможности указать, в каком цветовом пространстве следует выполнять смешивание, что иногда приводило к запутанным результатам.

После color-mix() разработчики и дизайнеры могут смешивать цвета в браузере вместе со всеми остальными стилями, без запуска процессов сборки или включения JavaScript. Кроме того, они могут указать, в каком цветовом пространстве следует смешивать цвета, или использовать цветовое пространство смешивания по умолчанию — LCH.

Часто в качестве основы используется фирменный цвет, на основе которого создаются варианты, например, более светлые или темные цвета для стилей при наведении курсора. Вот как это выглядит с помощью color-mix() :

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(var(--brand) 25%, black);
  --lighter: color-mix(var(--brand) 25%, white);
}

А если вы хотите смешать эти цвета в другом цветовом пространстве, например, sRGB, измените его:

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(in srgb, var(--brand) 25%, black);
  --lighter: color-mix(in srgb, var(--brand) 25%, white);
}

Ниже представлен пример изменения темы оформления с помощью color-mix() . Попробуйте изменить фирменный цвет и посмотрите, как обновится тема:

Наслаждайтесь смешиванием цветов в различных цветовых пространствах в ваших таблицах стилей в 2022 году!

Ресурсы

color-contrast()

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

Скриншот трех цветовых палитр Material Design, демонстрирующий 14 цветов и соответствующие им белые или черные контрастные цвета для текста.
Пример цветовых палитр Material Design 2014 года.

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

Вот скриншот демонстрации набора цветов HWB , где цвета текста автоматически выбираются браузером на основе образца цвета:

Скриншот демонстрации HWB, где каждая палитра имеет различное сочетание светлого и темного текста, определяемое браузером.
Попробуйте демоверсию

В основе синтаксиса лежит следующий код: в функцию передается цвет «серый», а браузер определяет, какой цвет — черный или белый — имеет наибольший контраст:

color: color-contrast(gray);

Функцию также можно настроить, задав список цветов, из которого будет выбран наиболее контрастный цвет:

color: color-contrast(gray vs indigo, rebeccapurple, hotpink);

Наконец, если предпочтительнее не выбирать цвет с самым высоким контрастом из списка, можно указать целевой коэффициент контрастности, и будет выбран первый цвет, который его превысит:

color: color-contrast(
  var(--bg-blue-1)
  vs
  var(--text-lightest), var(--text-light), var(--text-subdued)
  to AA /* 4.5 could also be passed */
);

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

Ресурсы

Относительный цветовой синтаксис

Browser Support

  • Chrome: 111.
  • Край: 111.
  • Firefox: 113.
  • Сафари: 15.

Source

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

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

В приведенном ниже примере синтаксиса указан базовый шестнадцатеричный код, и относительно него создаются два новых цвета. Первый цвет с --absolute-change создает новый цвет в LCH из базового цвета, а затем заменяет яркость базового цвета на 75% , сохраняя насыщенность ( c ) и оттенок ( h ). Второй цвет --relative-change создает новый цвет в LCH из базового цвета, но на этот раз уменьшает насыщенность ( c ) на 20%.

.relative-color-syntax {
  --color: #0af;
  --absolute-change: lch(from var(--color) 75% c h);
  --relative-change: lch(from var(--color) l calc(c-20%) h);
}

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

В приведенной ниже демонстрации я использовал синтаксис относительного цвета для создания более светлых и более темных вариантов базового цвета, а также функцию color-contrast() для обеспечения надлежащего контраста меток:

Скриншот с тремя столбцами, каждый столбец либо темнее, либо светлее центрального.
Попробуйте демоверсию

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

:root {
  --_color-base: #339af0;

  --color-0:  lch(from var(--_color-base) 98% 10 h);
  --color-1:  lch(from var(--_color-base) 93% 20 h);
  --color-2:  lch(from var(--_color-base) 85% 40 h);
  --color-3:  lch(from var(--_color-base) 75% 46 h);
  --color-4:  lch(from var(--_color-base) 66% 51 h);
  --color-5:  lch(from var(--_color-base) 61% 52 h);
  --color-6:  lch(from var(--_color-base) 55% 57 h);
  --color-7:  lch(from var(--_color-base) 49% 58 h);
  --color-8:  lch(from var(--_color-base) 43% 55 h);
  --color-9:  lch(from var(--_color-base) 39% 52 h);
  --color-10: lch(from var(--_color-base) 32% 48 h);
  --color-11: lch(from var(--_color-base) 25% 45 h);
  --color-12: lch(from var(--_color-base) 17% 40 h);
  --color-13: lch(from var(--_color-base) 10% 30 h);
  --color-14: lch(from var(--_color-base) 5% 20 h);
  --color-15: lch(from var(--_color-base) 1% 5 h);
}
Скриншот 15 цветовых палитр, все сгенерированы динамически с помощью CSS.
Попробуйте демоверсию

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

Ресурсы

Градиентные цветовые пространства

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

Четыре градиента в сетке, все от голубого до темно-розового. LCH и LAB имеют более равномерную насыщенность, тогда как sRGB немного теряет насыщенность в середине.

После указания цветовых пространств с градиентом, браузеру сообщается, какое цветовое пространство использовать для интерполяции цвета. Это дает разработчикам и дизайнерам возможность выбирать предпочтительный градиент. Цветовое пространство по умолчанию также меняется с sRGB на LCH.

Добавление синтаксиса выполняется после направления градиента, использует новый in и является необязательным:

background-image: linear-gradient(
  to right in hsl,
  black, white
);

background-image: linear-gradient(
  to right in lch,
  black, white
);

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

Показаны 11 цветовых пространств, сравнивающих черный и белый цвета.

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

Показаны 11 цветовых пространств, сравнивающих синий и черный цвета.

Для более подробного изучения, примеров и комментариев ознакомьтесь с этой веткой в ​​Твиттере .

Ресурсы

inert

Browser Support

  • Chrome: 102.
  • Край: 102.
  • Firefox: 112.
  • Сафари: 15.5.

Source

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

После inert отпадает необходимость в перехвате, поскольку можно заморозить или защитить целые разделы страницы или приложения. Пока эти части документа находятся в инертном состоянии, клики и попытки смены фокуса просто недоступны. Можно также рассматривать это как защиту, а не как ловушку, где inert не направлена ​​на то, чтобы заставить вас оставаться в одном месте, а скорее на то, чтобы сделать другие места недоступными.

Хорошим примером этого является функция alert() в JavaScript:

Веб-сайт отображается как интерактивный, затем вызывается функция alert(), и страница перестает быть активной.

Обратите внимание на видео выше: страница была доступна с помощью мыши и клавиатуры до вызова функции alert() . После появления всплывающего окна alert() остальная часть страницы «замораживалась», или становилась inert . Фокус пользователя перемещается внутрь окна alert() и ему некуда больше деваться. Как только пользователь взаимодействует с функцией alert() и завершает её выполнение, страница снова становится интерактивной. inert позволяет разработчикам с легкостью добиться такого же эффекта управляемого фокуса.

Вот небольшой пример кода, демонстрирующий принцип работы:

<body>
  <div class="modal">
    <h2>Modal Title</h2>
    <p>...<p>
    <button>Save</button>
    <button>Discard</button>
  </div>
  <main inert>
    <!-- cannot be keyboard focused or clicked -->
  </main>
</body>

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

Ресурсы

Шрифты COLRv1

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

После шрифтов COLRv1 в интернете появились шрифты с меньшим размером, масштабируемые, изменяемые по положению, с градиентами и режимами смешивания, которые принимают параметры для настройки шрифта в зависимости от конкретного случая или для соответствия бренду.

Сравнительная визуализация и гистограмма, показывающие, что шрифты COLRv1 более четкие и мельче.
Изображение взято с сайта https://developer.chrome.com/blog/colrv1-fonts/

Вот пример из статьи в блоге разработчиков Chrome об эмодзи. Возможно, вы заметили, что при увеличении размера шрифта эмодзи он теряет четкость. Это изображение, а не векторная графика. Часто в приложениях, где используется эмодзи, его заменяют на более качественный вариант. С шрифтами COLRv1 эмодзи получаются векторными и красивыми:

Иконочные шрифты могли бы творить чудеса с этим форматом, предлагая настраиваемые двухцветные палитры и многое другое. Загрузка шрифта COLRv1 ничем не отличается от загрузки любого другого файла шрифта:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

Настройка шрифта COLRv1 осуществляется с помощью @font-palette-values , специального правила CSS `@`, позволяющего группировать и именовать набор параметров настройки в единый пакет для последующего использования. Обратите внимание, что пользовательское имя указывается так же, как и пользовательское свойство, начиная с -- :

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

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

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

.spicy {
  font-family: "Bungee Spice";
  font-palette: --colorized;
}
Скриншот шрифта Bungee Spice со словом DUNE.
Шрифт Bungee Spice показан с пользовательскими цветами, источник: https://developer.chrome.com/blog/colrv1-fonts/

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

Ресурсы

Блоки Viewport

Графическое изображение, показывающее, как экран устройства, окно браузера и iframe имеют разные области просмотра.

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

На мобильных устройствах при загрузке страницы отображается строка состояния с URL-адресом, которая занимает часть пространства области просмотра. Через несколько секунд и после некоторого взаимодействия строка состояния может сдвинуться, чтобы освободить большее пространство для просмотра для пользователя. Но когда эта строка сдвигается, высота области просмотра изменяется, и любые блоки vh будут смещаться и изменять размер в соответствии с изменением целевого размера. В последующие годы блоку vh потребовалось специально определить, какой из двух размеров области просмотра он будет использовать, поскольку это вызывало неприятные визуальные проблемы на мобильных устройствах. Было решено, что vh всегда будет представлять собой наибольшую область просмотра.

.original-viewport-units {
  height: 100vh;
  width: 100vw;
  --size: 100vmin;
  --size: 100vmax;
}

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

Графическое изображение с тремя телефонами для иллюстрации DVH, LVH и SVH. На примере телефона DVH показаны две вертикальные линии: одна между нижней частью строки поиска и нижней частью области просмотра, а другая — от области над строкой поиска (под строкой состояния системы) до нижней части области просмотра; это показывает, что DVH может иметь любую из этих двух длин. LVH показан посередине с одной линией между нижней частью строки состояния устройства и кнопкой в ​​области просмотра телефона. Последний пример — это устройство SVH, на котором показана линия от нижней части строки поиска браузера до нижней части области просмотра.

Вот полный список всех новых вариантов блоков видового окна, доступных в новых модификациях видового окна:

единицы измерения высоты в окне просмотра
​​.new-height-viewport-units {
  height: 100vh;
  height: 100dvh;
  height: 100svh;
  height: 100lvh;
  block-size: 100vb;
  block-size: 100dvb;
  block-size: 100svb;
  block-size: 100lvb;
}
Ширина области просмотра (в единицах)
.new-width-viewport-units {
  width: 100vw;
  width: 100dvw;
  width: 100svw;
  width: 100lvw;
  inline-size: 100vi;
  inline-size: 100dvi;
  inline-size: 100svi;
  inline-size: 100lvi;
}
Самые маленькие боковые блоки иллюминатора
.new-min-viewport-units {
  --size: 100vmin;
  --size: 100dvmin;
  --size: 100svmin;
  --size: 100lvmin;
}
Самые большие боковые окна со стороны иллюминатора.
.new-max-viewport-units {
  --size: 100vmax;
  --size: 100dvmax;
  --size: 100svmax;
  --size: 100lvmax;
}

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

Ресурсы

:has()

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 121.
  • Сафари: 15.4.

Source

До появления :has() ` объект селектора всегда находился в конце. Например, объектом этого селектора является элемент списка: ul > li . Псевдоселекторы могут изменять селектор, но не меняют его объект: ul > li:hover или ul > li:not(.selected) .

После вызова :has() элемент, находящийся выше в дереве элементов, может оставаться тем же элементом, предоставляя запрос о дочерних элементах: ul:has(> li) . Легко понять, почему :has() получил распространенное название «селектор родителя», поскольку в этом случае элементом селектора становится родительский элемент.

Вот простой пример синтаксиса, где класс .parent остается основным, но выбирается только в том случае, если дочерний элемент имеет класс .child :

.parent:has(.child) {...}

Вот пример, где элемент <section> является субъектом, но селектор срабатывает только в том случае, если у одного из дочерних элементов есть свойство :focus-visible :

section:has(*:focus-visible) {...}

Селектор :has() становится невероятно полезным инструментом, когда появляются более практичные сценарии его использования. Например, в настоящее время невозможно выделить теги ` <a> , когда они оборачивают изображения, что затрудняет обучение тега `<a>` изменению стилей в этом случае. Однако с помощью :has() это возможно:

a:has(> img) {...}

Все это примеры, где :has() лишь выглядит как селектор родительского элемента. Рассмотрим случай использования изображений внутри элементов <figure> и корректировки стилей изображений, если у фигуры есть ` <figcaption> `. В следующем примере выбираются фигуры с `figcaption`, а затем изображения в этом контексте. :has() используется и не меняет тему, поскольку мы нацелены на изображения, а не на фигуры:

figure:has(figcaption) img {...}

Возможности комбинаций кажутся бесконечными. Сочетайте :has() с запросами количества и настраивайте CSS-сетки в зависимости от числа дочерних элементов. Сочетайте :has() с интерактивными псевдоклассами состояний и создавайте приложения, которые реагируют на происходящее новыми, креативными способами.

Проверить наличие поддержки очень просто с помощью @supports и функции ` selector() , которая проверяет, понимает ли браузер синтаксис перед его использованием:

@supports (selector(:has(works))) {
  /* safe to use :has() */
}

Ресурсы

2022 год и далее

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

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

Свободно типизированные пользовательские свойства

Browser Support

  • Chrome: 85.
  • Край: 85.
  • Firefox: 128.
  • Сафари: 16.4.

Source

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

Рассмотрим сценарий, в котором box-shadow использует пользовательские свойства для своих значений:

box-shadow: var(--x) var(--y) var(--blur) var(--spread) var(--color);

Всё это работает хорошо, пока какое-либо из свойств не будет изменено на значение, которое CSS не принимает, например, --x: red . Вся тень перестанет отображаться, если какая-либо из вложенных переменных отсутствует или имеет недопустимый тип значения.

Вот тут-то и вступает в дело @property : --x может стать типизированным пользовательским свойством, перестав быть гибким и негибким, а безопасным с определенными ограничениями:

@property --x {
  syntax: '<length>';
  initial-value: 0px;
  inherits: false;
}

Теперь, когда box-shadow использует var(--x) , а затем --x: red , значение red будет проигнорировано, поскольку оно не равно <length> . Это означает, что тень продолжает работать, даже если одному из её пользовательских свойств было присвоено недопустимое значение. Вместо сбоя она возвращается к своему initial-value 0px .

Анимация

Помимо типобезопасности, это также открывает множество возможностей для анимации. Гибкость синтаксиса CSS делает анимацию некоторых элементов невозможной, например, градиентов. Здесь помогает @property , поскольку типизированное свойство CSS может сообщить браузеру о намерениях разработчика внутри чрезмерно сложной интерполяции. По сути, это ограничивает возможности настолько, что браузер может анимировать те аспекты стиля, которые раньше были недоступны.

Рассмотрим этот демонстрационный пример, где радиальный градиент используется для создания части наложения, формируя эффект фокусировки в виде точечного источника света. JavaScript устанавливает координаты мыши по осям x и y при нажатии клавиши Alt/Opt, а затем изменяет значение параметра Focus-Size на меньшее, например, 25%, создавая круг фокусировки в точке, указанной курсором мыши:

Попробуйте демоверсию
.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );
}

Однако градиенты нельзя анимировать. Они слишком гибкие и сложные, чтобы браузер мог «просто определить», как именно вы хотите, чтобы они анимировались. С помощью @property же можно типизировать и анимировать отдельное свойство, и браузер легко поймет намерение пользователя.

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

@property --focal-size {
  syntax: '<length-percentage>';
  initial-value: 100%;
  inherits: false;
}

.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );

  transition: --focal-size .3s ease;
}
Попробуйте демоверсию

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

@property может сделать гораздо больше, но эти небольшие шаги могут принести значительные результаты.

Ресурсы

Была задана min-width или max-width

Перед использованием диапазонов медиазапросов, медиазапрос CSS использует min-width и max-width для определения условий превышения или недостаточности. Он может выглядеть так:

@media (min-width: 320px) {
  
}

После указания диапазонов медиазапроса тот же медиазапрос может выглядеть следующим образом:

@media (width >= 320px) {
  
}

Медиазапрос CSS, использующий как min-width так и max-width может выглядеть следующим образом:

@media (min-width: 320px) and (max-width: 1280px) {
  
}

После указания диапазонов медиазапроса тот же медиазапрос может выглядеть следующим образом:

@media (320px <= width <= 1280px) {
  
}

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

Ресурсы

Нет переменных запроса медиафайлов

До появления @custom-media медиа-запросы приходилось повторять снова и снова, или же полагаться на препроцессоры для генерации правильного результата на основе статических переменных во время сборки.

После появления @custom-media , CSS позволяет создавать псевдонимы для медиа-запросов и ссылаться на них, подобно пользовательским свойствам.

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

@custom-media --OSdark  (prefers-color-scheme: dark);
@custom-media --OSlight (prefers-color-scheme: light);

@custom-media --pointer (hover) and (pointer: coarse);
@custom-media --mouse   (hover) and (pointer: fine);

@custom-media --xxs-and-above (width >= 240px);
@custom-media --xxs-and-below (width <= 240px);

Теперь, когда они определены, я могу использовать один из них следующим образом:

@media (--OSdark) {
  :root {
    
  }
}

Полный список используемых мной пользовательских медиа-запросов вы найдете в моей библиотеке пользовательских свойств CSS OpenProps .

Ресурсы

Вложенные селекторы — это так здорово!

До появления @nest в таблицах стилей было много повторений. Это становилось особенно неудобно, когда селекторы были длинными, и каждый из них был нацелен на небольшие различия. Удобство вложенности — одна из самых распространенных причин использования препроцессора.

После @nest повторение исчезнет. Практически все возможности вложенности, поддерживаемые препроцессорами, будут доступны непосредственно в CSS.

article {
  color: darkgray;
}

article > a {
  color: var(--link-color);
}

/* with @nest becomes */

article {
  color: darkgray;

  & > a {
    color: var(--link-color);
  }
}

What's most important about nesting to me, besides not repeating article in the nested selector, is the styling context remains within one style block. Instead of bouncing from one selector, and its styles, to another selector with styles (example 1), the reader can remain within the context of an article and see the article owns links inside of it. The relationship and style intent are bundled together, so article gets to appear to own its own styles.

The ownership could also be thought of as centralization. Instead of looking around a stylesheet for relevant styles, they can all be found nested together within a context. This works with parent to child relationships, but also with child to parent relationships.

Consider a component child that wants to adjust itself when in a different parent context, as opposed to the parent owning the style and changing a child:

/* parent owns this, adjusting children */
section:focus-within > article {
  border: 1px solid hotpink;
}

/* with @nest becomes */

/* article owns this, adjusting itself when inside a section:focus-within */
article {
  @nest section:focus-within > & {
     border: 1px solid hotpink;
  }
}

@nest helps overall with healthier style organization, centralization, and ownership. Components can group and own their own styles, instead of having them spread amongst other style blocks. It may seem small in these examples, but it can have very large impacts, for both convenience and legibility.

Ресурсы

Scoping styles is really hard

Browser Support

  • Chrome: 118.
  • Edge: 118.
  • Firefox: 146.
  • Safari: 17.4.

Source

Before @scope , many strategies existed because styles in CSS cascade, inherit, and are globally scoped by default. These features of CSS are very convenient for many things, but for complex sites and applications, with potentially many different styles of components, the global space and nature of the cascade can make styles feel like they're leaking.

After @scope , not only can styles be scoped to only within a context, like a class, they can also articulate where the styles end and do not continue to cascade or inherit.

In the following example, BEM naming convention scoping can be reversed into the actual intent. The BEM selector is attempting to scope the color of a header element to a .card container with naming conventions. This requires that the header has this classname on it, completing the goal. With @scope , no naming conventions are required in order to complete the same goal without marking up the header element:

.card__header {
  color: var(--text);
}

/* with @scope becomes */

@scope (.card) {
  header {
    color: var(--text);
  }
}

Here's another example, less component-specific and more about the global scope nature of CSS. Dark and light themes have to coexist inside a stylesheet, where order matters in determining a winning style. Usually this means dark theme styles come after the light theme; this establishes light as the default and dark as the optional style. Avoid the ordering and scope battling with @scope :

​​@scope (.light-theme) {
  a { color: purple; }
}

@scope (.dark-theme) {
  a { color: plum; }
}

To complete the story here, @scope also allows the establishing of where the style scope ends. This can't be done with any naming convention or preprocessor; it's special and only something CSS built-in to the browser can do. In the following example, img and .content styles are exclusively applied when a child of a .media-block is a sibling or parent of .content :

@scope (.media-block) to (.content) {
  img {
    border-radius: 50%;
  }

  .content {
    padding: 1em;
  }
}

Ресурсы

No CSS way for a masonry layout

Before CSS masonry with grid, JavaScript was the best way to achieve a masonry layout, as any of the CSS methods with columns or flexbox would inaccurately represent the content order.

After CSS masonry with grid, no JavaScript libraries will be required and the content order will be correct.

Screenshot of the masonry layout which shows numbers traveling along the top, then going down.
Image and demo from Smashing Magazine
https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/

The preceding demo is achieved with the following CSS:

.container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: masonry;
}

It's comforting to know that this is on the radar as a missing layout strategy, plus you can try it today in Firefox .

Ресурсы

CSS can't help users reduce data

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Source

Before the prefers-reduced-data media query, JavaScript and a server could change their behavior based on a user's operating system or browser "data saver" option, but CSS could not.

After the prefers-reduced-data media query, CSS can join the user experience enhancement and play its part in saving data.

@media (prefers-reduced-data: reduce) {
  picture, video {
    display: none;
  }
}

The preceding CSS is used in this media scroll component and the savings can be huge. Depending on how large the visiting viewport is, the more savings to be had on page load. Saving continues as users interact with the media scrollers. The images all have loading="lazy" attributes on them and that, combined with CSS hiding the element entirely, means a network request for the image is never made.

Screenshot of a TV show carousel interface with many thumbnails and titles shown.

For my testing, on a medium sized viewport, 40 requests and 700kb of resources were initially loaded. As a user scrolls the media selection, more requests and resources are loaded. With CSS and the reduced data media query, 10 requests and 172kb of resources are loaded. That's half a megabyte of savings and the user hasn't even scrolled any of the media, at which point there are no additional requests made.

Screenshot of a TV show carousel interface with no thumbnails and many titles shown.

There are more advantages to this reduced data experience than just data savings. More titles can be seen and there's no distracting cover images to steal attention. Many users browse in a data saver mode because they pay per megabyte of data—it's really nice to see CSS able to help out here.

Ресурсы

Scroll snap features are too limited

Before these scroll snap proposals, writing your own JavaScript to manage a carousel, slider, or gallery could quickly get complex, with all the observers and state management. Also, if not careful, the natural scrolling speeds could get normalized by script, making user interaction feel a bit unnatural and potentially clunky.

Новые API

snapChanging()

As soon as the browser has released a snap child, this event fires. This allows UI to reflect the lack of a snap child and the indeterminate snap state of the scroller, as it's now being used and will land somewhere new.

document.querySelector('.snap-carousel').addEventListener('snapchanging', event => {
  console.log('Snap is changing', event.snappedTargetsList);
});
snapChanged()

As soon as the browser has snapped to a new child and the scroller is rested, this event fires. This lets any UI that depends on the snapped child to update and reflect the connection.

document.querySelector('.snap-carousel').addEventListener('snapchanged', event => {
  console.log('Snap changed', event.snappedTargetsList);
});
scroll-start

Scrolling doesn't always begin at the start. Consider swipeable components where swiping left or right triggers different events, or a search bar that on page load is initially hidden until you scroll to the top. This CSS property lets developers specify that a scroller should begin at a specific point.

:root { --nav-height: 100px }

.snap-scroll-y {
  scroll-start-y: var(--nav-height);
}
:snap-target

This CSS selector will match elements in a scroll snap container that are currently snapped by the browser.

.card {
  --shadow-distance: 5px;
  box-shadow: 0 var(--shadow-distance) 5px hsl(0 0% 0% / 25%);
  transition: box-shadow 350ms ease;
}

.card:snapped {
  --shadow-distance: 30px;
}

After these scroll snap proposals, making a slider, carousel, or gallery is much easier as the browser now offers conveniences for the task, eliminating observers and scroll orchestration code in favor of using built-in APIs.

It's still very early days for these CSS and JS features, but be on the lookout for polyfills that can help adoption, and testing, of them soon.

Ресурсы

Cycling between known states

Before toggle() , only states built into the browser already could be leveraged for styling and interaction. The checkbox input, for example, has :checked , an internally managed browser state for the input that CSS is able to use for changing the element visually.

After toggle() , custom states can be created on any element for CSS to change and use for styling. It allows groups, cycling, directed toggling, and more.

In the following example, the same effect of a list item strikethrough on complete is achieved but without any checkbox elements:

<ul class='ingredients'>
   <li>1 banana
   <li>1 cup blueberries
  ...
</ul>

And the relevant CSS toggle() styles:

li {
  toggle-root: check self;
}

li:toggle(check) {
  text-decoration: line-through;
}

If you're familiar with state machines, you may notice how much crossover there is with toggle() . This feature will let developers build more of their state into CSS, hopefully resulting in clearer and more semantic ways of orchestrating interaction and state.

Ресурсы

Customizing select elements

Before <selectmenu> , CSS didn't have the ability to customize <option> elements with rich HTML or change much about the display of a list of options. This led developers to load external libraries that recreated much of the functionality of a <select> , which ended up being a lot of work.

After <selectmenu> , developers can provide rich HTML for options elements and style them as much as they need, while still meeting accessibility requirements and providing semantic HTML.

In the following example, taken from the <selectmenu> explainer page , a new select menu is created with some basic options:

<selectmenu>
  <option>Option 1</option>
  <option>Option 2</option>
  <option>Option 3</option>
</selectmenu>

CSS can target and style the element's parts:

.my-select-menu::part(button) {
  color: white;
  background-color: red;
  padding: 5px;
  border-radius: 5px;
}

.my-select-menu::part(listbox) {
  padding: 10px;
  margin-top: 5px;
  border: 1px solid red;
  border-radius: 5px;
}

A select looking menu with red accent colors.

You can try the <selectmenu> element on Chromium in Canary with the web experiments flag enabled. Watch out in 2023 and beyond for customizable select menu elements.

Ресурсы

Anchoring an element to another

Before anchor() , position absolute and relative were position strategies provided for developers to have child elements move around within a parent element.

After anchor() , developers can position elements to other elements, regardless of them being a child or not. It also allows developers to specify which edge to position against, and other niceties for creating position relationships between elements.

The explainer has a few great examples and code samples provided, if you're interested in learning more.

Ресурсы