Baseline: ноябрь 2025
- вторник, 2 декабря 2025 г. в 00:00:05
Обзор на браузерные API, которые стали Widely available в ноябре 2025. Раз в месяц я буду вам напоминать, что вы уже можете использовать в проде.
Каждый месяц выходят новые CSS-свойства, HTML-атрибуты, JavaScript-методы и WebAPI, но применять в проде мы их конечно же не будем. 2.5 года назад также каждый месяц выходили новые фичи в браузере, а вот их уже пора начинать применять.
У каждой компании, да что уж там компании, у каждой команды в компании своя методика принятия решения о внедрении той или иной фичи в проекте.
Общий же сценарий выглядит так:
- Посмотрели в пользовательские метрики. Поняли какими браузерами и их версиями в основном пользуются пользователи проекта;
- Заглянули в caniuse и поняли, какие фичи уже поддерживаются большинством браузеров;
- Приняли решение о внедрении той или иной фичи в проект.
Какие-то команды позволяют себе указывать правило "последние три версии браузеров". У других специфика проекта, что проект работает исключительно на iPad с Safari. Сами понимаете, все мы разные и требования разные, и у каждого свой подход.
Baseline - позволяет немного упростить процесс принятия решения о внедрении той или иной фичи в проект. Если фича Widely available значит фича уже как минимум есть во всех основных браузерах как минимум стабильно используются последние 2.5 года.
color() — функция для работы с цветовыми пространствами (Display P3, Rec.2020 и др.)
color-mix() — смешение цветов в заданном цветовом пространстве
Compression Streams API — нативное сжатие и распаковка данных (gzip, deflate)
:nth-child(… of <selector>) — улучшенный псевдокласс для точного выбора элементов
Oklab и OkLCh — восприятие-ориентированные цветовые пространства
WebRTC: RTCPeerConnection.sctp — доступ к информации о SCTP-транспорте
Lab и LCH — научно обоснованные цветовые пространства CIE
Функция color() позволяет задавать цвет непосредственно в нужном цветовом пространстве, а не только в привычном srgb. Это особенно важно для современных дисплеев с широким цветовым охватом (wide-gamut).
Стандартное srgb охватывает лишь часть цветов, которые способен воспринимать человек — и тем более, часть цветов, которые способны отобразить современные экраны. Цветовые пространства вроде Display P3, Rec.2020 или ProPhoto RGB позволяют отображать более насыщенные, яркие и точные оттенки.
💡 Пример: в
srgbневозможно отобразить «реальный» красный цвет, который виден на дисплее с Display P3. Сcolor(display-p3 1 0 0)— можно.
color(<colorspace> <coordinates>)Поддерживаемые цветовые пространства включают:
srgb — стандартное RGB (0–1 или 0%–100%)
srgb-linear — линейное RGB (для физически корректных вычислений)
display-p3 — широко используемое в Apple-устройствах и HDR-контенте
rec2020 — для Ultra HD и HDR видео
a98-rgb — Adobe RGB
prophoto-rgb — для профессиональной цветокоррекции
xyz / xyz-d50 / xyz-d65 — CIE XYZ, базовое цветовое пространство
/* Яркий красный в Display P3 */
.banner {
background-color: color(display-p3 1 0 0);
}
/* Мягкий серый в линейном sRGB */
.text {
color: color(srgb-linear 0.7 0.7 0.7);
}
/* Насыщенный зелёный в Rec.2020 */
.video-overlay {
border-color: color(rec2020 0 0.9 0);
}Координаты указываются в диапазоне 0–1 (или в процентах, где 1 = 100%). В отличие от rgb(), здесь нет альфа-канала — если нужна прозрачность, используйте color(display-p3 1 0 0 / 0.8) (поддержка / для альфы зависит от браузера, но в Baseline 2025 — уже есть).
Если ваш бренд использует насыщенные фирменные цвета (например, яркий синий или зелёный), они будут выглядеть тускло в sRGB, но восхитительно — на iPhone или MacBook с P3-дисплеем.
:root {
--brand-primary: color(display-p3 0 0.6 1); /* насыщенный синий */
}
.hero {
background-color: var(--brand-primary);
}Для приложений, работающих с фотографиями, видео или творческими инструментами, точная цветопередача критична. color() даёт доступ к тем оттенкам, которые раньше были «заперты» в браузере.
Смешение цветов в Display P3 через color-mix() или градиенты с color() даёт более естественные переходы без «серых провалов», характерных для sRGB.
Функция color-mix() позволяет плавно смешивать два цвета в заданном цветовом пространстве — прямо в CSS, без препроцессоров или JavaScript. Это особенно ценно, потому что результат смешения зависит от выбранного цветового пространства: в srgb переходы могут выглядеть неестественно, а в Oklab, LCH или Display P3 — гладко и предсказуемо.
Раньше, чтобы получить оттенок цвета, приходилось вручную подбирать HEX/RGB-значения или использовать Sass-функции вроде mix() или darken(). Теперь браузер сам корректно смешает цвета — с учётом того, как человек воспринимает яркость и насыщенность.
💡 Пример: если смешать синий и чёрный в
srgb, получится тусклый, «грязный» оттенок. Вoklab— насыщенный тёмно-синий, сохраняющий чистоту тона.
color-mix(in <colorspace>, <color> <percentage>, <color> <percentage>?)<colorspace> — цветовое пространство, в котором происходит смешение (srgb, oklab, lch, display-p3 и др.).
Первый <color> и <percentage> — первый цвет и его доля (например, 70%).
Второй цвет необязательно указывать с процентом: если не указан, берётся остаток до 100%.
Если процент не задан ни для одного цвета, по умолчанию используется 50/50.
/* Смешать красный и белый в srgb (получим розовый) */
.button {
background-color: color-mix(in srgb, red, white 30%);
}
/* Осветлить фирменный синий на 20% в Oklch — без потери насыщенности */
.card {
border-top-color: color-mix(in oklch, var(--brand-blue) 80%, white);
}
/* Создать тёмный вариант фона для темной темы */
:root[data-theme="dark"] {
--surface: color-mix(in oklab, var(--brand-gray) 90%, black);
}💡 Проценты можно указывать только для одного цвета — второй автоматически дополняет до 100%.
Например:
color-mix(in lch, blue 70%, white)= 70% синего + 30% белого.
С color-mix() вы можете генерировать всю палитру прямо в CSS:
светлые и тёмные варианты основных цветов,
hover-состояния кнопок,
градиенты с живыми переходами.
:root {
--primary: oklch(65% 0.25 260);
--primary-light: color-mix(in oklch, var(--primary), white 85%);
--primary-dark: color-mix(in oklch, var(--primary), black 90%);
}Смешивайте цвета в display-p3, чтобы сохранить насыщенность даже в производных оттенках:
.hero-gradient {
background-image: linear-gradient(
to right,
color-mix(in display-p3, var(--vibrant-red) 90%, white),
color-mix(in display-p3, var(--vibrant-blue) 90%, white)
);
}Поскольку color-mix() работает в восприятие-ориентированных цветовых пространствах (вроде Oklab или LCH), анимации и градиенты выглядят плавно, без «серых провалов» или неожиданного изменения яркости — чего нельзя добиться в rgb() или hsl().
Сравните:
/* В srgb — тусклый, «мыльный» розовый */
.pink-srgb { background-color: color-mix(in srgb, red, white 70%); }
/* В oklch — чистый, насыщенный пастельный розовый */
.pink-oklch { background-color: color-mix(in oklch, red, white 70%); }
Именно поэтому color-mix() особенно мощен в связке с современными цветовыми пространствами — oklab, lch, display-p3.
Compression Streams API — это встроенный в браузер инструмент для сжатия и распаковки данных с использованием алгоритмов gzip и deflate. Теперь вы можете обрабатывать объёмы данных в десятки и сотни мегабайт прямо в клиенте — без библиотек, без сервера и без блокировки основного потока.
Раньше для сжатия в браузере приходилось:
подключать тяжёлые библиотеки вроде pako или fflate,
жертвовать производительностью (всё происходило синхронно),
или отправлять данные на сервер только ради архивации.
Теперь всё это делается нативно, асинхронно и построчно, через потоки (Streams API).
💡 Пример: вы собираете логи работы приложения — 50 МБ текста. Вместо отправки «как есть», вы сжимаете их в gzip на лету и отправляете пакет в 5 МБ. Экономия трафика — 90%.
API работает через два класса:
CompressionStream(format) — сжимает данные
DecompressionStream(format) — распаковывает
Поддерживаемые форматы:
'gzip'
'deflate'
Оба класса принимают поток на вход и возвращают преобразованный поток на выход, что позволяет легко встраивать их в цепочки обработки.
async function compressText(text) {
const stream = new Blob([text]).stream();
const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
const compressedBlob = await new Response(compressedStream).blob();
return compressedBlob;
}
// Использование
const logData = 'User performed action X...'; // длинная строка
const compressed = await compressText(logData);
console.log('Original size:', logData.length);
console.log('Compressed size:', compressed.size);async function decompressBlob(blob) {
const stream = blob.stream();
const decompressedStream = stream.pipeThrough(new DecompressionStream('gzip'));
const text = await new Response(decompressedStream).text();
return text;
}💡 Всё работает через ReadableStream → TransformStream → ReadableStream, поэтому вы можете комбинировать сжатие с шифрованием, отправкой через
fetch, записью в IndexedDB и т.д.
Собираете события, ошибки, метрики производительности? Сжимайте их перед отправкой:
const events = JSON.stringify(collectedEvents);
const blob = await compressText(events);
await fetch('/api/logs', {
method: 'POST',
body: blob,
headers: { 'Content-Encoding': 'gzip' }
});Сервер (например, Nginx или Cloudflare) может автоматически распаковать такие данные, если указан правильный заголовок.
Пользователь загрузил CSV или JSON-файл размером 100 МБ? Вы можете:
сжать его перед сохранением в IndexedDB,
позволить экспортировать «архив» без участия сервера,
предварительно обработать перед отправкой на бэкенд.
В PWA или оффлайн-редакторе можно сжимать документы на лету и сохранять их компактно — особенно полезно на устройствах с ограниченным хранилищем.
Файлы, сжатые через CompressionStream('gzip'), — это стандартные .gz-файлы. Их можно открыть любым архиватором или сервером. То же самое с deflate.
Псевдокласс :nth-child() получил долгожданное улучшение: теперь он может считать только те элементы, которые соответствуют заданному селектору, а не всех подряд. Это решает одну из самых раздражающих проблем CSS — невозможность точно выбрать «n-й элемент нужного типа» среди всех соседей.
Раньше :nth-child(2) означало «второй дочерний элемент вообще», даже если он не был нужного тега или класса. Чтобы выбрать, например, вторую кнопку среди множества других элементов, приходилось использовать костыли: обёртки, кастомные классы или JavaScript.
Теперь синтаксис :nth-child(An+B of S) позволяет писать точные, читаемые и семантически верные селекторы — прямо в CSS.
💡 Пример: у вас список из 10 элементов: чередуются
<div>,<button>,<span>. Вы хотите выделить вторую кнопку. Раньше — почти невозможно без классов. Теперь —button:nth-child(2 of button).
:nth-child(An+B of <селектор>)
:nth-last-child(An+B of <селектор>)An+B — стандартная формула (например, 2, odd, 3n+1).
<селектор> — любой валидный CSS-селектор (класс, тег, атрибут и т.д.).
Отсчёт ведётся только среди элементов, соответствующих селектору, но позиция проверяется в контексте всех соседей.
Аналогично работает :nth-last-child(… of …) — отсчёт с конца.
/* Второй элемент с классом .item */
.item:nth-child(2 of .item) {
border-top: 2px solid red;
}
/* Каждая нечётная кнопка среди всех кнопок */
button:nth-child(odd of button) {
background-color: #f0f0f0;
}
/* Последний активный пункт меню */
.menu-item:nth-last-child(1 of .menu-item.active) {
margin-bottom: 0;
}
/* Третий <li> с атрибутом data-visible="true" */
li:nth-child(3 of [data-visible="true"]) {
font-weight: 700;
}💡 Важно: элемент должен одновременно соответствовать селектору и занимать n-ю позицию среди таких же. Но сама проверка позиции учитывает всех соседей — это соответствует логике DOM.
В React, Vue или другом фреймворке часто рендерятся списки с условной логикой:
<ul>
<li class="ad">Реклама</li>
<li class="post">Пост 1</li>
<li class="ad">Реклама</li>
<li class="post">Пост 2</li>
<li class="post">Пост 3</li>
</ul>Как выделить второй пост? Раньше — только через :nth-of-type() (не работает с классами) или кастомный data-index. Теперь:
.post:nth-child(2 of .post) {
border-left: 4px solid #007AFF;
}В адаптивных сетках или таблицах с условным отображением строк (например, «скрытые» или «заблокированные») можно точно стилизовать каждую n-ю видимую запись:
.row:nth-child(even of .row:not(.hidden)) {
background-color: #fafafa;
}Больше не нужно генерировать классы вроде .item--index-2 на стороне JS. Вся логика — в CSS, а структура остаётся семантичной.
Oklab и OkLCh — это современные цветовые пространства, созданные не для машин, а для человеческого восприятия. В отличие от привычных rgb() или hsl(), они обеспечивают равномерные градиенты, предсказуемую яркость и естественное смешение цветов — без «серых провалов», неожиданной потери насыщенности или странного поведения при анимациях.
Раньше, чтобы добиться плавного перехода от тёмно-синего к светло-голубому, приходилось подбирать промежуточные цвета вручную или использовать сложные алгоритмы в JavaScript. Теперь всё это работает нативно в CSS через функции oklab() и oklch().
💡 Пример: если вы создадите градиент
linear-gradient(hsl(240, 100%, 30%), hsl(240, 100%, 80%)), в середине он станет неожиданно серым. Сoklch(50% 0.3 240)→oklch(80% 0.3 240)— переход будет гладким и насыщенным от начала до конца.
Oklab — трёхмерное цветовое пространство с координатами:
L — светлота (0% = чёрный, 100% = белый),
a — от зелёного (–) к красному (+),
b — от синего (–) к жёлтому (+).
OkLCh — та же система, но в полярных координатах (как HSL, но умный):
L — светлота,
C — хрома (насыщенность),
h — оттенок (hue) в градусах (0–360°).
/* Oklab: L a b */
color: oklab(60% 0.1 -0.05);
/* OkLCh: L C h */
color: oklch(70% 0.25 260);L — от 0% до 100% (или 0–1).
a, b — обычно от –0.4 до +0.4 (но могут выходить за пределы).
C — хрома, чем выше, тем насыщеннее (максимум ≈ 0.4 для отображаемых цветов).
h — оттенок в градусах, как в hsl().
Поддерживается альфа-канал через /:
background-color: oklch(80% 0.15 120 / 0.9);/* Насыщенный фиолетовый */
.card-header {
background-color: oklch(65% 0.3 300);
}
/* Тёплый серый без цветового оттенка */
.text-muted {
color: oklab(50% 0 0);
}
/* Плавный акцентный цвет с контролируемой насыщенностью */
:root {
--accent: oklch(72% 0.22 45); /* золотисто-оранжевый */
}Поскольку L (светлота) отделена от C (насыщенности), вы можете:
менять яркость темы, не трогая оттенок,
генерировать оттенки с одинаковой насыщенностью,
легко строить контрастные пары по WCAG.
:root {
--primary-hue: 260;
--primary-500: oklch(68% 0.25 var(--primary-hue));
--primary-300: oklch(82% 0.25 var(--primary-hue)); /* светлее, но той же насыщенности */
--primary-700: oklch(50% 0.25 var(--primary-hue)); /* темнее */
}Градиенты в OkLCh не теряют насыщенность посередине:
.hero {
background-image: linear-gradient(
to right,
oklch(70% 0.25 220),
oklch(70% 0.25 300)
);
}Анимация яркости или насыщенности теперь выглядит физически корректно:
.button:hover {
color: oklch(60% 0.28 260);
transition: color 0.2s ease;
}Oklab и OkLCh — идеальные цветовые пространства для color-mix(), потому что смешение в них сохраняет воспринимаемую яркость:
/* Осветление без «побеления» */
.hover-bg {
background-color: color-mix(in oklch, var(--brand) 90%, white);
}Свойство sctp в интерфейсе RTCPeerConnection наконец стало Widely available — и с ним в веб приходит прямой доступ к информации о SCTP-транспорте, лежащем в основе RTCDataChannel. Это не просто «ещё одно свойство», а важный шаг к отладке, мониторингу и управлению P2P-соединениями на уровне, который раньше был доступен только в нативных приложениях.
До появления pc.sctp разработчики WebRTC-приложений были «слепы» к состоянию канала данных: они могли отправлять и получать сообщения, но не видели, насколько велик буфер, поддерживается ли соединение, каков максимальный размер сообщения и работает ли транспорт вообще.
💡 Пример: вы отправляете 10 МБ данных через
RTCDataChannel, но соединение нестабильно. Безsctpвы не узнаете, почему — то ли сеть, то ли превышен лимит. Сpc.sctp.maxMessageSizeиpc.sctp.state— получаете точные метрики.
SCTP (Stream Control Transmission Protocol) — это транспортный протокол, используемый WebRTC для надёжной или частично надёжной передачи данных через RTCDataChannel. Он обеспечивает:
упорядоченную или неупорядоченную доставку,
управление потоками,
контроль перегрузки,
безопасность (через DTLS).
Хотя вы работаете с RTCDataChannel, «под капотом» всё это управляется SCTP — и теперь вы можете заглянуть «под капот».
После установки соединения (обычно после negotiationneeded и iceconnectionstatechange) у RTCPeerConnection появляется объект sctp:
const pc = new RTCPeerConnection();
const channel = pc.createDataChannel('messaging');
// Позже, когда соединение установлено:
pc.addEventListener('connectionstatechange', () => {
if (pc.sctp) {
console.log('SCTP ready!');
console.log('Max message size:', pc.sctp.maxMessageSize);
console.log('State:', pc.sctp.state); // "connected", "closed" и т.д.
}
});maxMessageSize — максимальный размер одного сообщения в байтах (часто ~64 КБ, но зависит от реализации).
state — состояние транспорта: "new", "connecting", "connected", "closed".
transport — ссылка на underlying DTLS-транспорт (для продвинутой диагностики).
💡
maxMessageSizeособенно важен: если вы попытаетесь отправить сообщение больше этого значения — оно не будет доставлено, и ошибки в консоли может не быть!
Разбивайте большие сообщения на чанки, зная точный лимит:
function sendLargeMessage(channel, pc, data) {
const maxSize = pc.sctp?.maxMessageSize || 16384; // fallback
const chunks = splitIntoChunks(data, maxSize);
chunks.forEach(chunk => channel.send(chunk));
}Логируйте состояние соединения для анализа проблем:
if (pc.sctp) {
analytics.track('webrtc-sctp', {
state: pc.sctp.state,
maxMessageSize: pc.sctp.maxMessageSize,
browser: navigator.userAgent
});
}Если pc.sctp.state === 'closed', вы можете:
показать уведомление «Соединение с коллегой потеряно»,
предложить переподключиться,
автоматически переключиться на резервный канал.
Lab и LCH — это научно обоснованные цветовые пространства, разработанные ещё в 1976 году Комитетом по освещению (CIE), но до недавнего времени недоступные в CSS. Теперь, став Widely available в Baseline (ноябрь 2025), они открывают веб-разработчикам доступ к точной, восприятие-ориентированной модели цвета, где яркость, насыщенность и оттенок ведут себя предсказуемо — в отличие от rgb() или hsl().
Если rgb() описывает, как смешать свет, а hsl() — попытка упростить это для человека, то Lab/LCH описывают, как человек видит цвет. Это разница между «технической инструкцией» и «психофизиологией».
💡 Пример: в
hsl(0, 100%, 50%)(красный) иhsl(60, 100%, 50%)(жёлтый) значение «lightness» одинаково — 50%. Но на глаз жёлтый выглядит гораздо светлее. В LCH светлота (L) соответствует реальному восприятию: жёлтый будет иметьL ≈ 97%, а красный —L ≈ 54%.
/* Lab: L a b */
color: lab(54% 81 69); /* красный */
color: lab(97% -21 94); /* жёлтый */
/* LCH: L C H */
color: lch(54% 106 41); /* тот же красный */
color: lch(97% 96 100); /* тот же жёлтый */L — от 0% до 100%.
a, b — обычно от –100 до +100 (но могут быть и больше).
C — хрома, теоретически до ~130, но на практике ограничен отображаемыми цветами.
H — оттенок в градусах, как в hsl().
Поддержка прозрачности через /:
background-color: lch(70% 50 260 / 0.85);/* Точный нейтральный серый — без цветового оттенка */
.text {
color: lab(50% 0 0);
}
/* Яркий, но не «вырвиглазный» акцент */
.button {
background-color: lch(65% 60 320); /* насыщенный пурпурный */
}
/* Гармоничная палитра с фиксированной насыщенностью */
:root {
--hue: 180;
--primary-500: lch(70% 40 var(--hue));
--primary-300: lch(85% 40 var(--hue));
--primary-700: lch(50% 40 var(--hue));
}Меняйте только оттенок или только яркость, не затрагивая остальное:
/* Все кнопки — разные оттенки, но одинаковой насыщенности и яркости */
.button--alarm { background: lch(68% 45 30); }
.button--green { background: lch(68% 45 140); }
.button--blue { background: lch(68% 45 260); }LCH — отличное пространство для color-mix(), особенно если вы хотите сохранить воспринимаемую яркость:
.hover-bg {
background-color: color-mix(in lch, var(--brand) 90%, white);
}Lab/LCH и Oklab/Oklch похожи, но есть нюансы:
Lab/LCH — стандарт CIE, используется в полиграфии, фотографии, науке.
Oklab/Oklch — более новая модель, оптимизированная под отображаемые цвета и часто даёт чуть более насыщенные результаты в вебе.
Обе работают отлично, но LCH — «промышленный стандарт», а OkLCh — «оптимизирован для веба».
Выбор зависит от задачи. Для большинства UI — подойдёт Oklab/Oklch . Для точной цветопередачи (например, в дизайн-системах медицинских или печатных приложений) — предпочтителен LCH.
Следующий выпуск — в начале января 2026 года после праздников. До встречи!