Привет, Хабр!

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

Этот год не стал исключением. Я составил список понравившихся новинок в CSS, которые появились к началу 2026 года. Сразу скажу, что это совсем новые фишки. Их браузерная поддержка ограничена в основном только браузером Google Chrome.

Давайте посмотрим, что я вам подготовил.

Правило @function

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

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

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

В нём я объявлю функцию --awesome-template, которая будет принимать один параметр --count-columns. С помощью него мы будем определять нужное количество колонок в сетке. Также они будут одинаковой ширины.

<body>
  <div class="awesome-container">
    <span class="awesome-box">1</span>
    <span class="awesome-box">2</span>
    <span class="awesome-box">3</span>
    <span class="awesome-box">4</span>
    <span class="awesome-box">5</span>
  </div>
</body>
@function --awesome-template(--count-columns) {
  result: repeat(var(--count-columns), 1fr);
}

.awesome-container {
  display: grid;
  grid-template-columns: --awesome-template(3); /* хочу чтобы была сетка из 3 колонок */
}

Мне кажется, что так не очень понятно, в чём же прелесть правила @function. Давайте добавим ещё медиа-запросы.

@function --awesome-template(--count-columns) {
  result: repeat(var(--count-columns), 1fr);
}

.awesome-container {
  display: grid;
}

@media (width > 640px) {
	
  .awesome-container {
    grid-template-columns: --awesome-template(2);
  }
}

@media (width > 1200px) {
	
  .awesome-container {
    grid-template-columns: --awesome-template(3);
  }
}

Этот код является прямым аналогом подхода с обычным объявлением свойства grid-template-columns в медиа-запросах.

.awesome-container {
  display: grid;
}

@media (width > 640px) {
	
  .awesome-container {
    grid-template-columns: repeat(2, 1fr);
	}
}

@media (width > 1200px) {
	
  .awesome-container {
    grid-template-columns: repeat(3, 1fr);
  }
}

Лично мне кажется, что первый вариант с правилом @function выглядит более читаемым. Конечно, это субъективная оценка. Но кто-то согласится со мной. Так что у нас появилась новая возможность упростить читаемость кода.

Браузерная поддержка: Google Chrome, Edge, Opera и Android Google Chrome.

Функция if()

Теперь в CSS есть условия и функция if(). Для доказательства я объявлю свойство background, которое будет менять своё значение в зависимости от ширины вьюпорта. И да, это всё будет работать без классической конструкции медиа-запроса.

body {
  background-color: if(
    media(width < 600px): green; else: yellow
  );
}

Чтобы увидеть, как меняется фон, используйте браузер Google Chrome. В нём сделайте ширину вьюпорта меньше 600 пикселей, а потом обратно больше 600.

Сама по себе функция if() бесполезна без функций media()style() и supports(). С помощью них разработчик расскажет браузеру о типе условия. Тогда они уже смогут с ними работать.

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

body {
  background-color: if(
    media((width < 700px) or (width > 1000px)): green; else: yellow
  );
}

С помощью функции style() мы можем объявить условия, основываясь на значении другого свойства. Например, я напишу условие, которое будет реагировать на изменение значения свойства color.

body {
  background-color: if(
    style(--color: white): green; else: yellow;
  );
  --color: black;
}

@media (width < 1000px) {
  body {
    --color: white;
  }
}

И последний вид условий — это проверка поддержки определённой возможности в браузере. Для примера я напишу условие, которое будет реагировать на поддержку в браузере свойства margin-inline.

Если свойство поддерживается, то будет использовано значение green. Если нет, то yellow.

body {
  background-color: if(
    supports(margin-inline: 1rem): green; else: yellow;
  );
}

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

Браузерная поддержка: Google Chrome, Edge, Opera и Android Google Chrome.

Функция sibling-index()

Иногда нам требуется написать стили на основе позиции элемента в группе. Например, при создании анимации, чтобы элементы следовали друг за другом. Скорее всего, вы решали такую задачу с помощью JavaScript.

В прошлом году в CSS появилась замечательная функция sibling-index(). Она возвращает номер элемента в группе. В рамках статьи я не буду создавать анимацию с помощью неё. Мы просто увеличим размер текста!

У каждого элемента будем увеличивать значение свойства font-size на свой номер в группе элементов. Второй увеличим на двойку, третий на тройку и т. д.

<body>
  <span class="awesome-block">1</span>
  <span class="awesome-block">2</span>
  <span class="awesome-block">3</span>
  <span class="awesome-block">4</span>
</body>
.awesome-block:nth-child(2) {
  font-size: calc(sibling-index() * 1rem);
}

.awesome-block:nth-child(3) {
  font-size: calc(sibling-index() * 1rem);
}

.awesome-block:nth-child(4) {
  font-size: calc(sibling-index() * 1rem);
}

Лично мне всегда не хватало такого элемента при создании анимации. Проще стало с пользовательскими CSS-свойствами. С помощью них можно было передать значение. Но некоторым разработчикам такой подход не нравился.

Хорошо, что скоро и эти костыли уйдут в прошлое. Ура!

Браузерная поддержка: Google Chrome, Edge, Opera, Safari, Android Google Chrome и iOS Safari.

Свойство interest-delay

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

Это огромная проблема, если реализовывать выпадающие элементы. Меню, селекты и т. д. Она заключается в том, что пользователь не может мгновенно перевести курсор на выпадающий список. В итоге он играл в игру «Попади на список, пока он не исчез».

Все эти года такая проблема решается с помощью JavaScript. Задаётся задержка, чтобы человек смог комфортно навести на элемент списка.

Я понимаю, что без примера сложно понять, о чём я говорю. Извиняюсь за это. Всё выше сказанное было к тому, что в CSS появился способ задать такую задержку. Это свойство interest-delay.

Вот теперь переходите на интерактивную демонстрацию с сайта MDN. В ней есть чекбокс, который позволит заметить разницу. Сначала посмотрите, как появляется элемент «Hover tooltip«» без установленного чекбокса, а потом с ним. Главное делайте это в браузере Google Chrome.

Для тех кто, не захочет переходить, расскажу, как объявить свойство. Всё также, как у свойств, у которых устанавливаем время.

.delay {
  interest-delay: 1s 2s;
}

В демонстрации этот класс добавляется к элементу button. После чего начинается вся магия. Всё же посмотрите!

Браузерная поддержка: Google Chrome, Edge и Android Google Chrome.

Свойство field-sizing

При вёрстке форм бывает, что требуется сделать размеры элементов адаптирующимися под введённый контент. Такую штуку всегда делали с помощью JavaScript. Но два года назад появилось свойство field-sizing, которое позволяет сделать тоже самое только с помощью CSS.

Нам достаточно объявить значение content. Для демонстрации я сделаю это для элемента input. И у него по умолчанию будет введён уже текст «Стас».

<body>
  <form>
    <label for="name">Имя</label>
    <input id="name" type="text" value="Стас">
  </form>
</body>
input {
  display: block;
  field-sizing: content;
}

А теперь добавим мою фамилию «Мельников».

Как я уже сказал, свойство field-sizing существует уже несколько лет. Но в конце 2025 года оно стало поддерживаться в браузере Safari. Это уже позволяет использовать его в гораздо большем количестве проектов. Также надеюсь, что в этом году подтянется браузер Firefox.

Браузерная поддержка: Google Chrome, Edge, Opera, Safari, Android Google Chrome и iOS Safari.

Заключение

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

  • адаптировать элемент формы под введённый контент с помощью свойстваfield-sizing;

  • создавать свои нестандартные функции с помощью правила @function;

  • объявлять значения в зависимости от условия, используя функцию if();

  • установить задержку свойством interest-delay перед появлением и исчезновением элемента при использовании псевдо-класса :hover;

  • определять номер элемента в группе и использовать его для стилизации благодаря функции sibling-index().

Это мой личный топ. Конечно, новинок больше. Есть и обновлённая функция attr(), новые свойства для анимации (view-timelineanimation-timeline и view-timeline) и многое другое.

В общем я вам дал свою рекомендацию. Теперь буду ждать вашу. Напишите, пожалуйста, в комментариях, какие появившиеся новинки в CSS к январю 2026 года вам понравились. Буду ждать их.

На этом всё. Спасибо за чтение!

P. S. Помогаю больше узнать про CSS в своём ТГ-канале CSS isn't magic. Присоединяйтесь. Ссылка в профиле.

© 2026 ООО «МТ ФИНАНС»

Комментарии (36)


  1. monochromer
    20.01.2026 10:40

    Мне кажется, что так не очень понятно, в чём же прелесть правила @function. Давайте добавим ещё медиа-запросы.

    Не очень удачный пример. Без функции даже проще:

    .awesome-container {
      --columns: 1;
      display: grid;
      grid-template-columns: repeat(var(--columns), 1fr);
    
      @media (width > 640px) {
        --columns: 2;
      }
    
      @media (width > 1200px) {
        --columns: 3;
      }
    }



    1. zelenin
      20.01.2026 10:40

      пример отвечает на вопрос как, а не зачем.


    1. melnik909 Автор
      20.01.2026 10:40

      Спасибо за комментарий. Скажите, пожалуйста, какой вы бы подобрали пример?


      1. monochromer
        20.01.2026 10:40

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

        @property --_vw {
          syntax: "<length>";
          initial-value: 0px;
          inherits: false;
        }
        
        @function --viewport-scale(--min <number>: 300, --max <number>: 1200) returns <number>  {
          --_vw: 1vw;
          --_unitless-viewport: calc(tan(atan2(var(--_vw) * 100, 1px)));
          --_clamped-viewport: clamp(var(--min), var(--_unitless-viewport), var(--max));  
          --_viewport-scale:
            calc(
              (var(--_clamped-viewport) - var(--min)) /
              (var(--max) - var(--min))
            );
          result: calc(var(--_viewport-scale));
        }
        
        body {
          margin: 0;
          min-height: 100dvh;
          background-color: rgb(255 100 50 / --viewport-scale());
          font-size: calc(--viewport-scale(100, 800) * (72px - 20px) + 20px);
        }



        1. melnik909 Автор
          20.01.2026 10:40

          Спасибо.

          Отдельно оставлю для читателей мой рассказ, почему единицы вьюпорта для размера шрифта — это плохая идея


          1. monochromer
            20.01.2026 10:40

            Да, была неплохая статья с объяснением причин и инструментом для подбора значений, которые не ломали бы доступность.


          1. monochromer
            20.01.2026 10:40

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


        1. dom1n1k
          20.01.2026 10:40

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


          1. monochromer
            20.01.2026 10:40

            Разве это можно реализовать на препроцессорах?


            1. dom1n1k
              20.01.2026 10:40

              В точности как тут - нельзя. Но можно по-другому с сохранением конечного смысла.

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

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


          1. AlexPershin
            20.01.2026 10:40

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


            1. dom1n1k
              20.01.2026 10:40

              Препроцессорное решение не работает в рантайме

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


              1. AlexPershin
                20.01.2026 10:40

                Тут всё зависит от задачи автора решения. Возможно, ему всё это было нужно. Для каких-то ваших задач что-то будет лишним.

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


                1. dom1n1k
                  20.01.2026 10:40

                  Даже говорить в общем, я не согласен с логикой "не работают в рантайме -> проигрывают вчистую".
                  Кто ж спорит, что нативные переменные - это крутая фича. Но там где это реально нужно. А где не нужно, лучше статические вычисления наоборот спрятать из рантайма, оставив там только необходимый минимум логики. Тем более костыли вроде tan(atan())

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

                  Поэтому сорян, забывать препроцессоры - так себе идея.


                  1. AlexPershin
                    20.01.2026 10:40

                    Так а толку от препроцессоров, если единственное, что они делают — это генерят кучу статичного цсс?

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

                    Ну и появляются задачи, которые с препроцессором эффективно не решить. Тот же пример с прогрессбаром: https://habr.com/ru/articles/983810/ Берём условную цсс-логику, переменные, и всё это не просто перекрашивается, так оно ещё и нормально анимируется. Как вы такого же добьётесь с помощью препроцессоров?


                    1. dom1n1k
                      20.01.2026 10:40

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

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

                      Что касается диаграм по ссылке - для каких-то случаев, почему бы нет. Хотя лично мне кажется, что в реальном проде все равно придется брать условный d3, потому что захотят по секторам кликать или что-то показывать по ховеру.


                      1. AlexPershin
                        20.01.2026 10:40

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

                        А лучше использовать для этого старый CSS + JS-библиотеку? Ведь это точно будет лучше по производительности?

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


                      1. dom1n1k
                        20.01.2026 10:40

                        Какое больше css-кода, какая тонна js-кода, что за фантазии?

                        Если вернуться к исходному примеру, то в моей трактовке на фронт попадет ровно 1 строка кода (цифры пишу от фонаря просто для примера):

                        font-size: clamp(20px, 10px + 1.5vw, 50px);

                        Без реалтайм-переменных, без кучи нечитаемых вложенных скобок, без тангенса арктангенса, и уж конечно без js.


                      1. AlexPershin
                        20.01.2026 10:40

                        Вы же первый начали фантазировать? Я показал пример с простым прогрессбаром, который отлично делается на современном цсс. Вы приплели какой-то сложный интерактивный компонент-диаграмму, который надо делать на d3.js

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


    1. Artem_Omny
      20.01.2026 10:40

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


  1. vanxant
    20.01.2026 10:40

    Печально, что огнелис как-то совсем отстаёт.

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


    1. ganzmavag
      20.01.2026 10:40

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

      Вот да. Причём при вёрстке существование Сафари злит, а в целом существование популярного браузера, который не Хром - скорее радует.


  1. Metotron0
    20.01.2026 10:40

    .awesome-block:nth-child(2) {
      font-size: calc(sibling-index() * 1rem);
    }
    
    .awesome-block:nth-child(3) {
      font-size: calc(sibling-index() * 1rem);
    }
    
    .awesome-block:nth-child(4) {
      font-size: calc(sibling-index() * 1rem);
    }

    А зачем так-то? Если вы заранее значете позицию, зачем тогда вызов функции? Тут всё удобство было бы как раз в том, чтобы написать один раз без всяких nth-child.


  1. Psychosynthesis
    20.01.2026 10:40

    @function это уже откровенная шизофрения. На кой хрен это в CSS? Все остальные костыли стандарта уже поправили?

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

    Да делали б уже его Тьюринг-полным, добавили бы БД, нейросети на CSS, чё мелочиться.


    1. cssfish
      20.01.2026 10:40

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


      1. IKStantin
        20.01.2026 10:40

        Как раз это упрощение. Можно от костылей на js плавно отказываться и делать всё больше на чистом CSS.


    1. jorgen
      20.01.2026 10:40

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


    1. Ents
      20.01.2026 10:40

      тсс, не подсказывайте

      Хотя я вот думаю, что в итоге вставят выполнение js кода прямо в css и дело с концом (вроде --eval(...)). Потом подъедет node_modules и можно будет нейронки делать на CSS


      1. andreymal
        20.01.2026 10:40

        Уже было в Internet Explorer 5.5


  1. AlexPershin
    20.01.2026 10:40

    Кстати, с if вообще история интересная. Его в 25 году успели зарелизить сначала в кастрированном виде, как аналог стилевых контейнерных запросов. А потом успели проапргрейдить, добавив range syntax for style queries. И вот это была бомба. Но примеров использования пока мало


  1. IKStantin
    20.01.2026 10:40

    Все больше радуют новые возможности.

    Жду, когда можно будет динамически передавать значения из одного селектора в другой типа:

    .selector2 { height: .selector1.height; }

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


    1. bbc_69
      20.01.2026 10:40

      Прикольно. И декларативно даже.


    1. monochromer
      20.01.2026 10:40

      А есть proposal?


      1. IKStantin
        20.01.2026 10:40

        У меня нет. Я просто ждун-мечтатель.


        1. monochromer
          20.01.2026 10:40

          Можно не ждать и принести свое предложение в https://github.com/w3c/csswg-drafts/issues


  1. IkotikI
    20.01.2026 10:40

    Классный обзор! Много увидел полезных фичей.

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

    А вот насколько это реально возродит кодинг на чистом CSS - это вопрос другой. Как будто топорно выглядят в сравнении с тем же SASS. Скорее цель уменьшить взаимодействие с JS в типовых задачах.