Skip to content

Сравнение

Здесь ECSS сравнивается с популярными подходами к стилизации компонентов на одном примере — кнопке с вариантами оформления. Главное отличие ECSS: логика «какой стиль применить» живёт в файле стилей, а не в компоненте, и при этом полностью типизирована.

CSS Modules

CSS Modules решают проблему глобальных имён, но логика состояний остаётся в компоненте. Каждый новый вариант — правки в двух местах: CSS-файл и JS-компонент.

css
/* Button.module.css */
.button {
  display: inline-flex;
  padding: 8px 16px;
  border-radius: 6px;
  cursor: pointer;
}

.primary {
  background: #646cff;
  color: #fff;
}

.danger {
  background: #e53e3e;
  color: #fff;
}

.ghost {
  background: transparent;
  border: 1px solid currentColor;
}

.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
tsx
import styles from './Button.module.css';

function Button({ variant, disabled, children }) {
  const className = [
    styles.button,
    variant === 'primary' && styles.primary,
    variant === 'danger' && styles.danger,
    variant === 'ghost' && styles.ghost,
    disabled && styles.disabled,
  ]
    .filter(Boolean)
    .join(' ');

  return <button className={className}>{children}</button>;
}

С ECSS всё объявляется в одном файле, а компонент не знает ни о каких классах:

ecss
/* Button.ecss */
@enum Variant {
  values: "primary", "danger", "ghost";
}

@block Button {
  @param --variant Variant;
  @param --disabled? boolean;

  display: inline-flex;
  padding: 8px 16px;
  border-radius: 6px;
  cursor: pointer;

  @if (--variant == "primary") {
    background: #646cff;
    color: #fff;
  }

  @if (--variant == "danger") {
    background: #e53e3e;
    color: #fff;
  }

  @if (--variant == "ghost") {
    background: transparent;
    border: 1px solid currentColor;
  }

  @if (--disabled) {
    opacity: 0.4;
    cursor: not-allowed;
  }
}
tsx
import { EButton } from './Button.ecss';

<EButton as="button" params={{ variant: 'primary' }}>
  Кнопка
</EButton>;
  • Не нужно вручную собирать className
  • Логика состояний живёт рядом со стилями
  • TypeScript знает допустимые значения variant — передать несуществующее не получится

styled-components

styled-components позволяют писать стили прямо в JS, используя props для вариантов. Однако у подхода есть несколько существенных минусов: CSS генерируется в runtime при каждом рендере, сама библиотека добавляет в бандл десятки килобайт, а шаблонные строки со вставками JS быстро превращаются в трудночитаемую смесь двух языков.

tsx
import styled, { css } from 'styled-components';

const Button = styled.button<{
  variant: 'primary' | 'danger' | 'ghost';
  disabled?: boolean;
}>`
  display: inline-flex;
  padding: 8px 16px;
  border-radius: 6px;
  cursor: pointer;

  ${({ variant }) =>
    variant === 'primary' &&
    css`
      background: #646cff;
      color: #fff;
    `}

  ${({ variant }) =>
    variant === 'danger' &&
    css`
      background: #e53e3e;
      color: #fff;
    `}

  ${({ variant }) =>
    variant === 'ghost' &&
    css`
      background: transparent;
      border: 1px solid currentColor;
    `}

  ${({ disabled }) =>
    disabled &&
    css`
      opacity: 0.4;
      cursor: not-allowed;
    `}
`;

ECSS обрабатывается на этапе сборки — в браузер попадает обычный статичный CSS:

ecss
/* Button.ecss */
@enum Variant {
  values: "primary", "danger", "ghost";
}

@block Button {
  @param --variant Variant;
  @param --disabled? boolean;

  display: inline-flex;
  padding: 8px 16px;
  border-radius: 6px;
  cursor: pointer;

  @if (--variant == "primary") {
    background: #646cff;
    color: #fff;
  }

  @if (--variant == "danger") {
    background: #e53e3e;
    color: #fff;
  }

  @if (--variant == "ghost") {
    background: transparent;
    border: 1px solid currentColor;
  }

  @if (--disabled) {
    opacity: 0.4;
    cursor: not-allowed;
  }
}
tsx
import { EButton } from './Button.ecss';

<EButton as="button" params={{ variant: 'primary' }}>
  Кнопка
</EButton>;
  • Нет runtime-оверхеда: CSS генерируется один раз при сборке
  • Работает с SSR без дополнительной настройки
  • Стили не смешиваются с JS-логикой