Темизация компонентов

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


Подходы к темизации

В Haunted темизация может быть реализована несколькими способами:

  1. Через CSS-переменные Наиболее гибкий и современный подход. Компоненты используют переменные CSS для цветовой схемы, шрифтов и размеров, а родительский элемент или :root задаёт их значения.

    import { html, component } from 'haunted';
    
    const Button = ({ label }) => html`
      <button class="btn">${label}</button>
      <style>
        .btn {
          background-color: var(--btn-bg, #007bff);
          color: var(--btn-color, #fff);
          border: none;
          padding: 0.5em 1em;
          border-radius: 4px;
          cursor: pointer;
          transition: background-color 0.3s;
        }
        .btn:hover {
          background-color: var(--btn-bg-hover, #0056b3);
        }
      </style>
    `;
    
    customElements.define('my-button', component(Button));

    Здесь ключевой момент — гибкость через переменные CSS. Их можно менять на уровне документа или контейнера:

    :root {
      --btn-bg: #28a745;
      --btn-color: #fff;
      --btn-bg-hover: #218838;
    }

    Это позволяет динамически менять тему всего приложения без изменения компонентов.


  1. Использование контекста (Context API) Haunted предоставляет встроенный механизм контекста через provide и useContext. Контекст удобен для передачи темы глубоко вложенным компонентам.

    import { provide, useContext } from 'haunted';
    
    const ThemeContext = Symbol('ThemeContext');
    
    const ThemeProvider = ({ theme, children }) => {
      provide(ThemeContext, theme);
      return html`${children}`;
    };
    
    const ThemedButton = () => {
      const theme = useContext(ThemeContext);
      return html`
        <button style="background: ${theme.primary}; color: ${theme.text}">
          Themed Button
        </button>
      `;
    };

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


  1. Прямое внедрение стилей через свойства компонентов Каждый компонент может принимать объект стилей через свойства, например styles или theme. Это удобно для случаев, когда необходимо настроить тему локально.

    const Card = ({ title, content, styles = {} }) => html`
      <div style="background: ${styles.bg || '#fff'}; color: ${styles.color || '#000'}; padding: 1em; border-radius: 8px;">
        <h3>${title}</h3>
        <p>${content}</p>
      </div>
    `;

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


Динамическая смена темы

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

const darkTheme = {
  '--bg-color': '#121212',
  '--text-color': '#ffffff',
  '--btn-bg': '#1f1f1f',
  '--btn-color': '#ffffff'
};

const applyTheme = theme => {
  Object.entries(theme).forEach(([key, value]) => {
    document.documentElement.style.setProperty(key, value);
  });
});

applyTheme(darkTheme);

После вызова applyTheme(darkTheme) все компоненты, использующие соответствующие CSS-переменные, изменят стиль без дополнительной логики внутри них.


Практические советы

  • Для крупных проектов лучше комбинировать контекст и CSS-переменные. Контекст управляет логикой темы, переменные отвечают за визуальную часть.
  • Для компонент, которые должны быть полностью независимыми, рекомендуется использовать локальные переменные CSS в shadow DOM.
  • Всегда предусматривать значения по умолчанию для переменных и свойств, чтобы компоненты оставались универсальными и не ломались при отсутствии темы.
  • Можно использовать функции-хелперы для генерации CSS на основе объекта темы, что облегчает поддержку множества цветовых схем.

Организация тем

Часто проекты используют несколько заранее определённых тем, например light и dark. Их удобно хранить в виде объектов:

const themes = {
  light: {
    '--bg-color': '#ffffff',
    '--text-color': '#000000',
    '--btn-bg': '#007bff',
    '--btn-color': '#ffffff'
  },
  dark: {
    '--bg-color': '#121212',
    '--text-color': '#ffffff',
    '--btn-bg': '#1f1f1f',
    '--btn-color': '#ffffff'
  }
};

Смена темы сводится к вызову applyTheme(themes.dark) или applyTheme(themes.light).


Итоговые акценты

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