Верней оформил 5 декабря 2023 года для всех то, чем его разработчики пользуются уже давно. Позиционируется данный помощник для js-подобных фреймворков типа React, Preact, Solid, lit-html и Angular, однако оперирующие html шаблонами Vue и Svelte тоже могут задействовать StyleX, но после предварительной специальной её кастомизации.

Код в React в итоге выглядит примерно так:

import * as stylex from '@stylexjs/stylex';

const colorStyles = stylex.create({
  red: {
    backgroundColor: 'lightred',
    borderColor: 'darkred',
  },
  green: {
    backgroundColor: 'lightgreen',
    borderColor: 'darkgreen',
  },
});
import * as React from 'react';
import * as stylex from '@stylexjs/stylex';

const styles = stylex.create({ ... });

function ReactDiv({ color, isActive, style }) {
  return <div {...stylex.props(
    styles.main,
    // apply styles conditionally
    isActive && styles.active,
    // choose a style variant based on a prop
    colorStyles[color],
    // styles passed as props
    style,
  )} />;
}

StyleX компилируется в атомарные CSS, для чего используется Babel плагин.

Основная утверждаемая выгода от использования StyleX - переиспользование, типизация, будет всё круто на больших проектах. Ну и получение наконец полного "Single File Component" файла с js, css и html в одном флаконе.

Предполагается, что StyleX теперь будет продвигаться как best practice для React проектов.

Для сравнения DX вот примерно один и тот же код, написанный на React+StyleX и на Vue+CSS

Сравнить

React

import * as React from "react";
import * as stylex from "@stylexjs/stylex";

const colorStyles = stylex.create({
  red: {
    backgroundColor: "lightred",
    borderColor: "darkred",
  },
  green: {
    backgroundColor: "lightgreen",
    borderColor: "darkgreen",
  },
});

const styles = stylex.create({
  main: {
    padding: "1rem",
    border: "1px solid grey",
  },
});

function ReactDiv({ color, isActive, style }) {
  return (
    <div
      {...stylex.props(
        styles.main,
        isActive && styles.active,
        colorStyles[color],
        style,
      )}
    />
  );
}

Vue

<script setup>
import { ref } from "vue";
const color = ref("red");
const props = defineProps(["style", "isActive"]);
</script>

<template>
  <div
    class="main"
    :class="[color, { isActive: props.isActive }, props.style]"
  />
</template>

<style scoped>
.red {
  background-color: "lightred";
  border-color: "darkred";
}
.green {
  background-color: "lightgreen";
  border-color: "darkgreen";
}
.main {
  padding: 1rem;
  border: 1px solid grey;
}
</style>

Ну и интересны причины возникновения StyleX:

Предыдущий сайт Facebook использовал нечто похожее на CSS-модули и страдал от различных проблем, которые и вдохновили на создание CSS-in-JS. Средний посетитель facebook.com загружал десятки мегабайт CSS. Большая часть из них не использовалась. Чтобы оптимизировать первоначальную загрузку, мы лениво загружали наш CSS, что, в свою очередь, приводило к медленному обновлению (или "Interaction to Next Paint"). Использование сложных селекторов приводило к конфликтам или "войнам за специфичность". Инженеры часто прибегали к использованию !important или более сложных селекторов для решения своих проблем, что делало всю систему постепенно хуже.

Несколько лет назад, когда мы перестраивали facebook.com с нуля, используя React, мы поняли, что нам нужно что-то лучшее, и создали StyleX.

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

Использование StyleX стало для нас в Meta значительным улучшением как в плане масштабируемости, так и в плане выразительности. На сайте facebook.com мы смогли сократить количество CSS-пакетов с десятков мегабайт лениво загружаемого CSS до одного пакета размером в пару сотен килобайт.

Мы создали StyleX не только для того, чтобы удовлетворить потребности React-разработчиков в стилях в Интернете, но и для унификации стилей для React в веб-версии и нативной версии.

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


  1. LabEG
    16.12.2023 07:35

    Фейсбук видимо решил прислушаться к моей статье, несмотря на то что любители css мне слили карму
    https://habr.com/ru/articles/707510/

    * сарказм


    1. Scoo909010
      16.12.2023 07:35

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


  1. bolk
    16.12.2023 07:35

    Зумеры изобрели JSSS :))


  1. gun_dose
    16.12.2023 07:35

    Выглядит точь в точь, как стили в react-native. И по своему опыту скажу, что это далеко не самое удобное решение, поэтому не вижу ничего хорошего в том, чтобы тянуть это в веб.

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


    1. gmtd Автор
      16.12.2023 07:35


    1. themen2
      16.12.2023 07:35

      Почему не очень хорошие. Можете раскрыть?


      1. gun_dose
        16.12.2023 07:35

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

        И если для react-native это приемлемо, потому что это не веб-платформа, то в вебе это всё нужно, как собаке пятая нога.


  1. newervi
    16.12.2023 07:35

    только не рассказывайте, что можно использовать 1 .scss файлик назвав его global.scss (а можно ещё даже его разбить на 10 составных файлов подгружая всё в него (модульность уф)) и подгружать его первым всегда, чтобы стили были глобальные, которые они переиспользуют в каждом компоненте, тогда не придётся в каждом компоненте вызывать заново их JS код и писать htmlcss через JS код, что, конечно же, сокращает вес CSS файлов, но не уверен, что сокращает вес JS файлов)

    *небольшой эдит для уточнения - подгружать файл можно 1 раз в index.hml или в +layout.svelte каком-нибудь или ещё где, его легко можно сделать "глобальным" уверен в каждом фреймворке


    1. gun_dose
      16.12.2023 07:35

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

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


      1. newervi
        16.12.2023 07:35

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


        1. gun_dose
          16.12.2023 07:35

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


          1. Semigradsky
            16.12.2023 07:35

            Это же JS, из одного файла экспортнули, в другой импортировали. Не понятно какие могут быть проблемы с переиспользованием.


            1. gun_dose
              16.12.2023 07:35

              Если стиль нужно использовать точно такой же, то проблем нет. А если в один компонент нужно применить стили из двух, то нужно из двух объектов клеить один и присваивать его. А на выходе это скомпилируется в набор CSS-правил, которые уже описаны в двух других местах. То есть итоговое количество стилей увеличится.


      1. Dertamio
        16.12.2023 07:35

        а можно поподробней, почему лучше иметь один файл в 100кб, чем десять по 10кб?


        1. gun_dose
          16.12.2023 07:35

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

          Естественно,что при общем объёме стилей в 10МБ это не сработает, но истина где-то по середине: стили можно дробить, но не слишком мелко. Условно говоря, если в файле всего 10 строчек, то время, затраченное на его загрузку по большей части будет состоять из времени на постановку запроса в очередь и установку соединения, и если его разделить на два файла по 5 строк, то эти два файла суммарно будут грузиться дольше.

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


    1. Semigradsky
      16.12.2023 07:35

      Есть библиотеки развивающие этот подход, Atomic CSS и многочисленные вариации.


  1. nin-jin
    16.12.2023 07:35

    Примечательно, что "типизация" не помешала вам использовать в примере не существующий стиль "active" и несуществующий цвет "lightred". А теперь сравните с $mol_style, где мало того, что поддерживается каскад без проблем со специфичностью, так он ещё и тайпчекается:

    - \Объявляем компонент
    $my_div $mol_view
        attr *
            my_div_color <= color \
            my_div_active <= active false
        sub / <= message \
    
    - \Используем его в другом компоненте
    $my_app $mol_page
        body /
    		<= Message $my_div
    			color \green
    			active true
    			message \Hello!
    namespace $.$$ {
    
    	// Добавляем стили к компоненту
    	$mol_style_define( $my_div, {
    
    		padding: `1rem`,
    		color: `black`,
    		border: {
    			width: `1px`,
    			style: `solid`,
    			color: `grey`,
    		},
    		
    		// Переопределяем стили в зависиимости от атрибутов
    		'@my_div_color': {
    			red: {
    				background: { color: `indianred` },
    				border: { color: `darkred` },
    			},
    			green: {
    				background: { color: `lightgreen` },
    				border: { color: `darkgreen` },
    			},
    		},
    	
    		'@my_div_active': {
    			true: {
    				border: { width: `2px` },
    			}
    		},
    		
    	} )
    	
    	$mol_style_define( $my_div_app, {
    		
    		width: `100%`,
    		height: `100%`,
    		
    		// Перебиваем стили для всех инстансов компонента внутри
    		$my_div: {
    			border: {
    				style: `dotted`,
    			},
    		},
    		
    		// Перебиваем стили для конкретного инстанса компонента внутри
    		Message: {
    			'@my_div_active': {
    				true: {
    					border: { width: `5px` },
    				},
    			},
    		},
    		
    	} )
    	
    }


    1. Yozi
      16.12.2023 07:35

      Классный ascii-art, а можно код посмотреть?


    1. MyraJKee
      16.12.2023 07:35

      Опять вы со своим мол )) как успехи?


  1. markelov69
    16.12.2023 07:35

    Какой "красивый" код получается, всегда мечтал такую лапшу писать. SCSS + CSS modules как был самым лучшим решением 10 лет назад, так и остается по сей день.
    Какой "красивый" код получается, всегда мечтал такую лапшу писать. SCSS + CSS modules как был самым лучшим решением 10 лет назад, так и остается по сей день.


  1. MyraJKee
    16.12.2023 07:35

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


  1. Per_Ardua
    16.12.2023 07:35

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

    Ну, молодцы, что тут ещё сказать...