CSS-модули

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

Основные концепции

CSS-модули — это механизм, при котором каждый CSS-файл компилируется в объект, где ключи соответствуют локальным классам, а значения — уникальным идентификаторам. Это обеспечивает изоляцию стилей: внешний код не может случайно повлиять на внутренние стили компонента, а внутренние стили не «протекут» наружу.

SkateJS использует реактивный подход к связыванию стилей с компонентами, что позволяет динамически применять CSS-модули через свойства компонента или через shadow DOM.

Подключение CSS-модулей

Для использования CSS-модулей в SkateJS необходим инструмент сборки, поддерживающий такую функциональность (например, Webpack или Vite). Типичный пример импорта CSS-модуля выглядит следующим образом:

import { define, props, h } from 'skatejs';
import styles from './button.module.css';

class MyButton extends HTMLElement {
  static props = {
    label: props.string
  };

  renderCallback() {
    return (
      <button className={styles.button}>
        {this.label}
      </button>
    );
  }
}

define('my-button', MyButton);

Здесь styles.button — это автоматически сгенерированное уникальное имя класса, которое обеспечит изоляцию стилей. В CSS-файле button.module.css может быть задано:

.button {
  background-color: #007bff;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.button:hover {
  background-color: #0056b3;
}

Локальная изоляция и динамика

SkateJS позволяет использовать CSS-модули не только для статических стилей, но и для реактивного изменения классов на основе свойств компонента. Например:

class MyAlert extends HTMLElement {
  static props = {
    type: props.string
  };

  renderCallback() {
    const alertClass = this.type === 'error' ? styles.error : styles.info;
    return (
      <div className={alertClass}>
        {this.textContent}
      </div>
    );
  }
}

В этом примере класс элемента меняется в зависимости от свойства type. Такой подход упрощает управление состояниями компонентов без глобальных классов и лишней логики в JavaScript.

Интеграция с shadow DOM

Использование shadow DOM с CSS-модулями позволяет полностью изолировать стили компонента от глобального контекста страницы:

import { define, props, h } from 'skatejs';
import styles from './card.module.css';

class MyCard extends HTMLElement {
  static props = {
    title: props.string
  };

  renderCallback() {
    return (
      <div className={styles.card}>
        <h2 className={styles.title}>{this.title}</h2>
        <slot></slot>
      </div>
    );
  }

  createdCallback() {
    this.attachShadow({ mode: 'open' });
  }
}

define('my-card', MyCard);

Каждый класс CSS автоматически локализуется внутри shadow DOM, что исключает перекрытие стилей со страницы и позволяет компонента полностью автономно управлять своим внешним видом.

Совмещение нескольких CSS-модулей

SkateJS поддерживает объединение нескольких CSS-модулей для одного компонента. Это полезно для разделения базовых стилей и темизации:

import baseStyles from './base.module.css';
import themeStyles from './theme.module.css';

class MyComponent extends HTMLElement {
  renderCallback() {
    const combinedClass = `${baseStyles.container} ${themeStyles.dark}`;
    return (
      <div className={combinedClass}>
        Контент компонента
      </div>
    );
  }
}

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

Динамические классы и условное применение

CSS-модули в SkateJS легко комбинируются с динамическими условиями. Например, с использованием тернарного оператора или библиотек вроде classnames:

import classNames from 'classnames';
import styles from './toggle.module.css';

class ToggleSwitch extends HTMLElement {
  static props = {
    active: props.boolean
  };

  renderCallback() {
    const toggleClass = classNames(styles.toggle, {
      [styles.active]: this.active
    });

    return <div className={toggleClass}></div>;
  }
}

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

Преимущества использования CSS-модулей в SkateJS

  • Изоляция стилей: предотвращает конфликт имен классов и непредсказуемое наследование стилей.
  • Поддержка реактивности: возможность менять классы на лету в зависимости от свойств компонента.
  • Совместимость с shadow DOM: обеспечивает полную автономность компонентов.
  • Упрощение темизации и модификаторов: легко комбинировать базовые стили и темы.

Особенности сборки

При работе с CSS-модулями важно настроить сборщик так, чтобы он корректно обрабатывал файлы .module.css. В Webpack используется css-loader с опцией modules: true, а в Vite — встроенная поддержка CSS-модулей через импорт *.module.css. Это позволяет автоматически генерировать уникальные имена классов и использовать их в SkateJS-компонентах без дополнительных ручных преобразований.


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