CSS-модули

Haunted — это современная библиотека для создания веб-компонентов на базе функционального подхода с использованием Hooks, вдохновлённых React. Встроенные возможности Haunted позволяют использовать CSS-модули для стилизации компонентов, обеспечивая локальный scope стилей и предотвращая их конфликт с глобальными стилями страницы.


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

CSS-модули — это отдельные файлы .css или .module.css, которые при сборке проекта преобразуются в объекты с уникальными именами классов. В Haunted их подключение выглядит следующим образом:

import { html, component } from 'haunted';
import styles from './Button.module.css';

function Button({ label }) {
  return html`
    <button class="${styles.button}">
      ${label}
    </button>
  `;
}

customElements.define('my-button', component(Button));

Особенности:

  • styles.button содержит уникальное имя класса, сгенерированное сборщиком (например, Webpack или Vite).
  • Внутри шаблона Haunted используется стандартная привязка классов через ${}.
  • CSS-модули автоматически изолируют стили, что предотвращает нежелательное влияние на другие компоненты.

Динамические классы

Haunted поддерживает динамическое формирование классов при помощи тернарных операторов и шаблонных литералов:

import { html, component } from 'haunted';
import styles from './Alert.module.css';

function Alert({ message, type }) {
  const alertClass = type === 'error' ? styles.error : styles.success;
  return html`
    <div class="${alertClass}">
      ${message}
    </div>
  `;
}

customElements.define('my-alert', component(Alert));

Ключевые моменты:

  • Любые условия для классов обрабатываются на этапе рендера.

  • Использование шаблонных литералов позволяет комбинировать несколько классов:

    class="${styles.base} ${type === 'warning' ? styles.warning : ''}"

Scoped стили и инкапсуляция

В отличие от глобальных стилей, CSS-модули создают локальные имена классов, что гарантирует отсутствие конфликтов:

/* Card.module.css */
.card {
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
import styles from './Card.module.css';

function Card({ content }) {
  return html`
    <div class="${styles.card}">
      ${content}
    </div>
  `;
}

Каждый класс .card при сборке превращается в уникальный идентификатор, например: Card_card__3f7X2. Это делает стили полностью изолированными, даже если на странице есть другие компоненты с классом card.


Интеграция с Haunted Hooks

Haunted использует Hooks для управления состоянием компонентов. CSS-модули легко сочетаются с ними:

import { html, component } from 'haunted';
import { useState } from 'haunted';
import styles from './Toggle.module.css';

function Toggle() {
  const [active, setActive] = useState(false);

  return html`
    <button
      class="${active ? styles.active : styles.inactive}"
      @click=${() => setActive(!active)}
    >
      ${active ? 'Включено' : 'Выключено'}
    </button>
  `;
}

customElements.define('my-toggle', component(Toggle));

Особенности взаимодействия:

  • Состояние компонента (useState) напрямую влияет на класс через привязку ${}.
  • Перерисовка шаблона автоматически обновляет класс, сохраняя локальную область видимости CSS.

Модульные анимации

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

/* Fade.module.css */
.fadeIn {
  animation: fadeIn 0.5s ease-in-out;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
import styles from './Fade.module.css';

function FadeBox() {
  return html`
    <div class="${styles.fadeIn}">
      Появление с анимацией
    </div>
  `;
}

Преимущества:

  • Анимации автоматически локализуются, не пересекаются с другими анимациями на странице.
  • Можно комбинировать с условными классами для динамических эффектов.

Композиция классов

CSS-модули поддерживают композицию стилей, что позволяет комбинировать базовые и специфические классы:

/* Button.module.css */
.base {
  padding: 8px 16px;
  border-radius: 4px;
}

.primary {
  composes: base;
  background-color: blue;
  color: white;
}
import styles from './Button.module.css';

function PrimaryButton({ label }) {
  return html`<button class="${styles.primary}">${label}</button>`;
}

Особенности:

  • composes: base; наследует все свойства класса base.
  • Это облегчает повторное использование общих стилей между компонентами.

Настройка сборки

Для использования CSS-модулей в Haunted проекте требуется поддержка сборщика:

  • Webpack: loader css-loader с опцией modules: true.
  • Vite: встроенная поддержка CSS-модулей без дополнительной конфигурации.
  • Parcel: автоматически распознаёт файлы с .module.css.

Пример конфигурации Webpack:

module.exports = {
  module: {
    rules: [
      {
        test: /\.module\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: { modules: true }
          }
        ]
      }
    ]
  }
};

Совместное использование с глобальными стилями

Иногда нужно сочетать локальные CSS-модули и глобальные CSS-файлы. Для этого:

import './global.css';
import styles from './LocalComponent.module.css';

function Component() {
  return html`
    <div class="${styles.container} global-class">
      Комбинация локальных и глобальных стилей
    </div>
  `;
}
  • Локальные классы управляются через styles.
  • Глобальные классы можно добавлять напрямую, обеспечивая гибкость при оформлении страницы.

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