Остановите войну в Украине!

CSS-нестинг — боль­ше, чем са­хар

Редактура Вадим Макеев

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

Не знаю, совпало или одно потянуло другое, но Бен Фрейн вскоре написал статью «CSS Nesting — the last piece of the puzzle», где, в частности, сослался на твит Адама. Кстати, Бен не последний человек в мире Sass и даже написал книгу «Sass and Compass for Designers» в 2013. Вероятно его авторитет и воодушевление по поводу черновика спецификации сделали своё дело и будущее модуля CSS Nesting заиграло новыми красками.

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

Отдельно стоит выделить цитату из обзора статьи Бена Фрейна на канале Defront:

Недавно Адам Аргайл представил сообществу черновик спецификации, над которым он работает вместе с Табом Аткинсом. В этой спецификации описывается синтаксис вложенности, который похож на аналогичный синтаксис из Sass и Less. Основное отличие — нужно использовать @nest при размещения вкладываемого селектора в качестве потомка…

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

Не такая уж новая спека Скопировать ссылку

Во-первых, какого-то явного «представления» спецификации от Адама не было. Он просто твитнул ссылку на спеку, в которой указан автором. Его имя попало в спеку всего полгода назад, он себя же и добавил. Саму спеку, или как принято называть «модуль», очень долго вынашивал Таб Аткинс и достаточно долгое время это были, как любит говорить Вадим Макеев, «заметки на салфетке». Таб — гениальный инженер, автор большого количества CSS-спецификаций, с фантазией и идеями у него полный порядок. У него даже есть свой отдельный репозиторий с потенциальными спецификациями, которые уже переписаны с «салфеток» во что-то более конкретное. Но нужно понимать, что в такой непростой теме как CSS, от идеи до реализации в браузерах могут пройти годы, если не десятилетия. И Таб делился своими идеями то тут, то там — в том числе, в своих докладах и статьях о возможном будущем CSS.

Так вот, первый черновик CSS Nesting был подготовлен Табом ещё в октябре 2013 года. А в мае 2018 Джонатан Нил начал масштабную дискуссию по этому предложению, версии черновика от 2015 года. По сути это и было первым представлением начальной версии модуля. Если у вас есть вопросы почему всё выглядит так, как выглядит, то вам обязательно стоит прочитать эту дискуссию (я там оказывается даже вставил свои пять копеек, о чём уже и забыл). Там, пожалуй, есть все типовые набросы и ответы к ним, технические нюансы, риски и прочее. После этого, лишь в июле 2018, спека перекочевала в официальные черновики CSSWG.

Не такой уж знакомый синтаксис Скопировать ссылку

Во-вторых, предложенный синтаксис всё-таки отличается от того что есть в Sass и Less. Ключевым является то, что подстановка вышестоящих селекторов вместо & (новый потенциальный житель в синтаксисе CSS) происходит не на уровне конкатенации строк, как в препроцессорах, а на уровне селекторов, точнее, на уровне CSS-токенов. Это принципиально и, пожалуй, вызывает наибольшие нападки на предложенный в модуле синтаксис. Например, следующий код:

.foo {
    &-bar {
        /* … */
    }
}

Будет воспринят препроцессорами и они произведут CSS:

.foo {
    /* … */
}

.foo-bar {
    /* … */
}

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

.foo {
    /* … */
}

-bar.foo {
    /* … */
}

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

В своём докладе «All about the CSS syntax» летом 2018, я рассказывал про механику CSS-синтаксиса и там есть секция про CSS Nesting на 30 слайдов (слайды 83-114). Там я рассматривал новый синтаксис с точки зрения работы CSS-парсера, который работает согласно правилам CSS Syntax Module Level 3, и немного сравнивал с препроцессорами.

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

Всё это было почти 3 года назад. Поэтому утверждения «Адам Аргайл представил сообществу черновик спецификации» и «синтаксис вложенности, который похож на аналогичный синтаксис из Sass и Less» кажутся некорректными. Последнее некорректно без уточнения, что механика другая, ведь вся схожесть ограничивается использованием & для подстановки вышестоящих селекторов.

Можно и без @nest Скопировать ссылку

В-третьих, использование директивы @nest не обязательно, если & идёт первым у всех селекторов в списке селекторов. Да, можно использовать директиву всегда, по какой-либо причине, это не возбраняется, но это не всегда необходимо. Использование же директивы обусловлено тем, что если у первого селектора списка в начале идёт не &, то CSS-парсер может свалиться в так называемый unbound lookahead. Это значит, что разбирая блок, парсер не может быстро принять решение: перед ним вложенный блок или же это поломанная декларация property: value, которую он должен проигнорировать. И это ломает предсказуемость и производительность. Это если коротко. Подробнее я разбирал в докладе на примерах, и об этом есть несколько объяснений в дискуссии к CSS Nesting. А для второго и далее селекторов в списке ограничение сохраняется для консистентности правила и устранения WTF при изменении порядка селекторов или удалении первого селектора, когда всё может внезапно сломаться.

В целом, наличие директивы @nest не ключевое отличие, а скорее небольшой нюанс. Субъективно, ситуации когда & идёт не в начале, а значит может потребоваться @nest, случаются куда реже. Поэтому ключевое отличие всё же, что вложенность в целом работает иначе — другие правила.

Также из Defront:

На данный момент черновик ещё не стабилизировался, и синтаксис описания вложенности скорее всего поменяется…

Лично я сомневаюсь, что синтаксис как-то поменяется.

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

Во-вторых, за прошедшие почти три года в спеке произошло не так много изменений, но изменения значимые:

  • Разобраны многие моменты со специфичностью. Это сложная тема, и тут на руку сыграли недавние нововведения в CSS селекторах :is() и :where().
  • Добавились вложенные @media (видимо, с подачи Адама) — как по мне, это хорошее дополнение.
  • Появился запрет на декларации после вложенных блоков. На самом деле, это изменение Таб внёс совсем недавно, и про это были те самые мои пять копеек. Я не думаю, что мой комментарий как-то на это повлиял, но приятно осознавать, что мои умозаключения были в правильном ключе.
  • Добавились расширения в интерфейсы CSSOM.

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

Defront молодец Скопировать ссылку

Ещё небольшая, но важная ремарка. Не стоит воспринимать разбор тезисов из обзора Defront к статье Бена Фрейна как нападку на автора канала. Я бы сделал комментарий к обзору в самом канале, но, к сожалению, комментарии там не включены (о, это отдельное «шаманство» включить комментарии в Telegram канале, поверьте). Как бы то ни было, Defront отличный канал, где публикуется много ссылок на очень занятные статьи с сопутствующим обзором. Отдельно хочу выразить респект и уважуху автору канала Александру Мышову, который сделал работу над каналом своей основной деятельностью. То есть он живёт на доход от канала и Патреона. Невероятная самоотдача своему делу, в какой-то степени авантюризм, но это очень круто! Поэтому категорически рекомендую канал.

Что касается темы CSS Nesting и CSS в целом, то это непростая история и в ней делают неверные выводы даже те, кто очень тесно с этим связан. Я сам не уверен, что не ошибся в своих тезисах (поправьте меня, если так). Поэтому особого криминала в неточностях обзора не вижу. Если не погружаться в глубину вопроса (а вопрос очень обширный, как можно заметить по тексту выше), то вполне понятно почему так может происходить и можно сделать скидку. У меня получилось всё это разложить лишь потому, что я давно копаю CSS со стороны спек, слежу и в какой-то степени участвую в происходящем. Но это нетипичный фронтенд. Надеюсь мой разбор поможет другим разобраться в вопросе и лучше понять положение вещей.

Но! Это только первая часть истории, есть ещё кое-что…

Сахар и препроцессоры Скопировать ссылку

Если отойти от обсуждения синтаксиса CSS Nesting и остальных вопросов, рассмотренных выше, то остаются неразобранными тезисы «это всего лишь синтаксический сахар» и «из CSS пытаются сделать препроцессор». Давайте разберёмся, что не так с этими тезисами.

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

Однако если продолжить углубляться, и внимательнее почитать комментарии и высказывания Таба Аткинса, то всплывает интересная деталь. Например, он между делом пишет о расширении CSSOM. Эти изменения уже получили отражение в черновике CSS Nesting. В частности, вводится новый интерфейс CSSNestingRule, наследуемый от CSSRule, а также расширяется интерфейс CSSStyleRule. Это означает, что браузер должен отражать CSS с использованием вложенности на объектную модель так как задал «автор». Иными словами, вложенность не разворачивается в линейную структуру (как в примерах), а сохраняется. Конечно, браузер может проводить линеаризацию в своих недрах, как, например, браузеры индексируют селекторы для более быстрого матчинга. Но это находится за рамками спецификации и зависит от имплементации.

Таким образом, новый синтаксис не совсем сахар и браузер не превращается в препроцессор, потому что новый синтаксис превращается в иное представление в CSSOM. Но зачем это нужно?

Начну с менее очевидного — комбинаторный взрыв и потенциал для оптимизаций. Рассмотрим такой пример:

ul, ol {
    & ol, & ul {
        & ol, & ul {
            /* ... */
        }
    }
}

Здесь указано три блока и шесть сложных селекторов. Если развернуть эту конструкцию в линейную форму, то получится 2 + 4 + 8 = 14 сложных селекторов:

ul,
ol,
ul ul,
ul ol,
ol ul,
ol ol,
ul ul ul,
ul ul ol,
ul ol ul {
    /* … */
}

Если использовать :is(), то можно остаться в рамках шести сложных селекторов:

ul,
ol,
:is(ol, ul) ul,
:is(ol, ul) ol,
:is(:is(ol, ul) ul, :is(ol, ul) ol) ul,
:is(:is(ol, ul) ul, :is(ol, ul) ol) ol {
    /* … */
}

Можно попробовать оптимизировать в три:

:is(ul, ol),
:is(ul, ol) :is(ul, ol),
:is(ul, ol) :is(ul, ol) :is(ul, ol) {
    /* … */
}

От того, что селекторов стало меньше, работы у браузера не уменьшилось. Последний вариант не менее сложный, чем первый (из 14 селекторов). Конечно, плюс в том, что браузер может это представлять внутри себя более оптимально, чем вариант из 14 селекторов. Но чтобы прийти к оптимальному варианту, браузеру нужно делать какие-то оптимизации на этапе анализа CSS по группировке селекторов. Учитывая, что в современных сайтах и приложениях содержатся тысячи правил (селекторов), то их группировка и оптимизация выглядит очень затратной, если вообще возможной, и браузеры могут эту оптимизацию не производить (как, например, не весь JavaScript оптимизируется на этапе разбора). Поэтому, стили организованные с использованием вложенности, имеют больший потенциал для группировки селекторов и внутренних оптимизаций.

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

Ещё неочевидный момент, возможность подмешать стилевой блок (в том числе, с вложенностью) в другой блок. Техническая возможность динамически менять таблицы стилей и отдельные правила существует уже лет 20. Даже Internet Explorer 6 это мог (хотя у него было нестандартное API для этого, то есть не по спеке, потому что она появилась позже). Другой вопрос, что это почти никак не используется в веб-разработке, да и мало кто об этом знает. Вроде как некоторые CSS-in-JS решения используют, но это неточно.

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

Инлайновые селекторы Скопировать ссылку

Ну и ключевое, что вносит CSS Nesting — это возможность стилизации состояний элемента, его псевдоэлементов и дочерних элементов, с использованием атрибута style. Да, содержимое style — такой же блок стилей, но без селектора (если точнее, то CSSStyleDeclaration). На данный момент, этот атрибут позволяет задать стили только для самого элемента. Если необходимо задать стили для состояний наведения :hover или фокуса :focus, то для этого нужно описывать стили отдельно. Либо, что тоже не редкость, прибегают к решениям на JavaScript, чтобы менять стили состояний по событиям. Однако JavaScript пока ещё не может стилизовать псевдоэлементы, поэтому ::before и ::after обычно превращаются в дополнительные вложенные элементы. А для других псевдоэлементов по-прежнему нужны стили сбоку.

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

Теперь же, с CSS Nesting станет возможным следующее:

<button style="
    color: black;
    &::before { content: url(icon.png) }
    &:hover { color: gray }
    &:focus { color: blue }
    @media (max-width: 400px) {
        & { width: 100% }
    }
">
        Click me!
</button>

Оставим на потом обсуждение, насколько это хорошо или плохо. Пока речь про возможности. И выглядит так, что с CSS Nesting атрибут style станет наравне с остальными способами задания стилей. Конечно, этим закрываются не все возможности CSS и часть остаётся за бортом, например, директивы @font-face, @keyframes, @supports и подобные.

Уже сейчас CSS Nesting должен заиграть для вас новыми красками. Но и это ещё не финал 😉

Возможны миксины Скопировать ссылку

Сразу предупрежу, что в этой секции будут мои домыслы. Однако они могут быть недалеки от правды, если сложить А + Б. Где «А» это CSS Nesting, а «Б» это давняя мечта многих — примеси или миксины.

Начну с предыстории. Летом 2015 уже знакомый нам Таб Аткинс формализовал первый черновик спецификации CSS @apply Rule. Это было на фоне внедрения CSS Custom Properties. Ключевая идея: раз в значение кастомных свойств можно задать что угодно, почему бы не задавать в значении блок деклараций. Тогда с помощью директивы @apply, такие значения (блоки) можно подмешивать в другие блоки. То есть, как несложно догадаться, получаем те самые миксины.

Выглядело это так:

:root {
    --toolbar-theme: {
        background-color: hsl(120, 70%, 95%);
        border-radius: 4px;
        border: 1px solid var(--theme-color, late);
    };
    --toolbar-title-theme: {
        color: green;
    };
}

.toolbar {
    @apply --toolbar-theme;
}

.toolbar > .title {
    @apply --toolbar-title-theme;
}

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

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

Почему эту идею похоронили? Она не решала проблему. Ведь таким образом можно подмешать набор деклараций в конкретный блок, а этого недостаточно. Если, например, мы хотим подмешать стили по принципу миксина, то мы скорее хотим повлиять не только на дефолтное состояние, но и на состояния :hover, :focus и другие, а может и на псевдоэлементы или дочерние элементы. Получалось:

:root {
    --cool-border: {
        border: 1px solid gold;
    };
    --cool-border-hover: {
        border-color: orange;
    };
    --cool-border-focus: {
        border-color: blue;
    }
}

.button {
    @apply --cool-border;
}

.button:hover {
    @apply --cool-border-hover;
}

.button:focus {
    @apply --cool-border-focus;
}

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

И вот теперь на горизонте появляется модуль CSS Nesting, открывающий новые возможности и в этом вопросе. С новыми вводными, предыдущий пример можно здорово упростить:

:root {
    --cool-border: {
        border: 1px solid gold;
        &:hover {
            border-color: orange;
        }
        &:focus {
            border-color: blue;
        }
    };
}

.button {
    @apply --cool-border;
}

Теперь миксин является целостной сущностью, его использование максимально простое и лаконичное, не нужно знать о его устройстве (в теории, естественно). Конечно, остаётся много открытых вопросов. Например, как должен миксин взаимодействовать с содержимым блока (декларациями и блоками), действительно ли миксины должны использовать механизм кастомных свойств для объявления, нужен ли каскад и так далее. С момента появления кастомных свойств в браузерах, появились новые ожидания от них, в частности добавилась новая директива @property. Не исключено, что объявление миксинов будет осуществляться тоже через директиву, например, @mixin.

Поживём — увидим. И хотя это все лишь мои домыслы, кажется CSS Nesting как раз подводит к чему-то такому. Синтаксис же играет второстепенную роль, главное механика, возможности и решается ли проблема.

Когда CSS Nesting появится в браузерах? Скопировать ссылку

На этот вопрос есть два ответа: «никогда» и «не менее чем через два года».

Ответ «никогда» может показаться странным, но это связано с отсутствием сигналов от разработчиков браузеров. Всё, что у нас есть — это черновик спецификации, пусть и официальный и прошедший публичную дискуссию. А вот про мнение производителей браузеров или какие-либо обсуждения пока ничего не известно, по крайней мере мне. В трекерах есть достаточно свежие тикеты (Blink, Firefox, Safari), но подробностей там нет — просто чтобы было. На «Can I Use» тоже пока не добавили.

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

Но допустим, что вендоры браузеров взяли и договорились, что CSS Nesting быть. Допустим, это случилось сегодня. Это не значит, что все тут же кинутся пилить. CSS Nesting — это мега-эпик, а эпики на этот год уже спланированы, вроде инициативы Compat2021. Так что попасть в планы CSS Nesting сможет только ближе к следующему году, а появляться в релизах начнёт не раньше второй половины 2022 года. И это оптимистичные прогнозы.

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

Изменение парсера CSS Скопировать ссылку

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

Матчинг селекторов Скопировать ссылку

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

Расширение CSSOM Скопировать ссылку

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

Переработка атрибута style Скопировать ссылку

Именно «переработка», так как сейчас этот атрибут задаёт стили только для самого элемента, которые не влияют на стили дочерних элементов (кроме наследуемых свойств), не создают псевдоэлементы, не определяют состояний и условий (псевдоклассы), не реагируют на изменение окружения (@media). Этим фактом не могли не воспользоваться для оптимизаций. Очевидно, что часть оптимизаций работать перестанет. Возможно только в случае, если в значении атрибута style используются конструкции из CSS Nesting. Но в любом случае, придётся придумывать новую машинерию, чтобы всё заработало. Чего только стоит ситуация, что для вложенных правил (rule) в style корнем будет является некий виртуальный блок без селектора. Вероятно нужно будет мёржить правила из style с правилами из таблиц стилей, причём этот мёрж должен быть для каждого элемента и на лету. И всё это выглядит как угроза для производительности и в целом всё непросто. Пока этот пункт про style кажется самым сложным и рискованным.

Браузерные DevTools Скопировать ссылку

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

Тесты Скопировать ссылку

Для CSS тесты пишут как общие для спеки (пишут всем миром), так и конкретно под браузер, если нужно протестировать некоторые особенности. Тестов пока нет, и это тоже потребует немало времени и усилий. Без полного набора тестов нельзя будет сказать, корректно ли и полностью ли реализована функциональность.

В общем, объём работы выглядит внушительным. Поэтому на быстрое внедрение рассчитывать не приходится.

Влияние CSS Nesting на разработку Скопировать ссылку

Начнём с того, что все инструменты, которые работают с CSS, сломаются в той или иной степени при использовании CSS Nesting:

  • Редакторы: раскраска, рефакторинг, саджест и другие;
  • Раскраска кода;
  • Сборщики и траспайлеры CSS;
  • Минификаторы CSS;
  • Инструменты по сбору покрытия, критический CSS и прочие;
  • И многие другие.

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

Также CSS Nesting может повлиять на распределение использования инструментов: слабо развивающиеся или заброшенные проекты (но популярные в прошлом), или те которые будут долго тянуть с поддержкой, могут потерять своих пользователей. CSS Nesting — первая такая функциональность, которая серьёзно влияет на обратную совместимость, а точнее её ломает. Но всё это станет актуальным, только когда производители браузеров решат внедрять CSS Nesting. А пока этого не случилось, нет смысла что-либо внедрять, кроме как ради академического интереса.

Есть также мнение, что CSS Nesting, с учётом потенциального расширения возможностей style, укрепит позиции апологетов CSS-in-JS. Чтобы не было путаницы, стоит уточнить, что речь про тех, кто использует одну из библиотек CSS-in-JS. Так вот, для пользователей таких библиотек скорее всего ничего не поменяется. А вот для их авторов потенциально будет проще реализовать некоторые механизмы. То есть CSS Nesting может повлиять только на техническое исполнение, нежели на идеологический аспект. Кстати, многие решения CSS-in-JS выносят статические части стилей в обычные файлы и используют DOM и CSSOM, наравне с атрибутом style. В таких случаях, используются все возможности CSS (состояния, псевдоэлементы, @media и другие).

Но есть ещё и техника, когда все стили помещают в атрибут style с помощью JavaScript. Такое сегодня часто можно увидеть в React-компонентах и виджетах, так как это позволяет не волноваться за поставку и внедрение в страницу сопутствующего CSS. Это тоже своего рода CSS-in-JS. И в таких случаях обычно используют события (JavaScript), чтобы менять состояние (:hover, :focus и другие) или реагировать на изменение окружения (@media). В таких случаях, с CSS Nesting во многих случаях получится избавиться от необходимости в JavaScript. На мой взгляд, это хорошо. И хотя я не разделяю такой подход (когда все стили в атрибуте style) и считаю его антипаттерном, но ситуации бывают разные и иногда это может быть оправдано.

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

Заключение Скопировать ссылку

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


Скомпилировано из публикаций канала «Горшочек варит» Романа Дворнова с разрешения автора — прим. редактора.