Стилизация веб-компонентов

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


Shadow DOM и локальные стили

В Haunted каждый компонент может быть изолированным с помощью Shadow DOM. Это изоляция влияет на стили:

  • Стили, определённые внутри Shadow DOM, не влияют на внешний документ.
  • Внешние стили не применяются автоматически к элементам внутри компонента.

Создание Shadow DOM в Haunted обычно выполняется через attachShadow внутри функции-компонента с использованием html шаблонного тега:

import { html, component } from 'haunted';

function MyButton() {
  return html`
    <style>
      button {
        background-color: #007bff;
        color: white;
        border: none;
        padding: 0.5em 1em;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s ease;
      }
      button:hover {
        background-color: #0056b3;
      }
    </style>
    <button>Нажми меня</button>
  `;
}

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

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

  • Тег <style> должен находиться внутри возвращаемого шаблона html.
  • Все селекторы применяются только к элементам внутри компонента, что предотвращает конфликты с другими компонентами.

Глобальные стили и CSS Variables

Хотя Shadow DOM изолирует стили, существуют методы передачи глобальных настроек:

  • CSS-переменные (--variable-name) можно объявлять в глобальном стиле документа и использовать внутри компонента:
function MyCard() {
  return html`
    <style>
      div {
        background-color: var(--card-bg, #f0f0f0);
        color: var(--card-color, #333);
        padding: 1em;
        border-radius: 8px;
        box-shadow: 0 2px 5px rgba(0,0,0,0.1);
      }
    </style>
    <div>Контент карточки</div>
  `;
}
  • Внешние темы и настройки легко меняются через CSS-переменные, при этом компоненты остаются реактивными.

Динамическая стилизация с использованием состояния

Haunted позволяет связывать CSS с состоянием компонента, используя хуки useState и useEffect. Например, можно динамически менять цвет кнопки при клике:

import { html, component } from 'haunted';
import { useState } from 'haunted';

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

  return html`
    <style>
      button {
        background-color: ${active ? '#28a745' : '#dc3545'};
        color: white;
        padding: 0.5em 1em;
        border: none;
        border-radius: 4px;
        cursor: pointer;
      }
    </style>
    <button @click=${() => setActive(!active)}>
      ${active ? 'Активно' : 'Неактивно'}
    </button>
  `;
}

customElements.define('toggle-button', component(ToggleButton));

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

  • Любое изменение состояния автоматически вызывает перерасчёт шаблона.
  • Стили, использующие переменные или выражения внутри ${}, обновляются вместе с DOM.

Стилизация с внешними CSS-файлами

Для крупных проектов удобно отделять стили в отдельные файлы. Haunted поддерживает инъекцию внешних стилей через adoptedStyleSheets в Shadow DOM:

import { html, component } from 'haunted';
import buttonStyles from './button.css.js';

function MyStyledButton() {
  const div = document.createElement('div');
  const shadow = div.attachShadow({ mode: 'open' });
  shadow.adoptedStyleSheets = [buttonStyles];
  shadow.innerHTML = `<button>Кнопка с внешним стилем</button>`;
  return div;
}

customElements.define('my-styled-button', component(MyStyledButton));
  • Для этого CSS-файл конвертируется в объект CSSStyleSheet через JavaScript.
  • Такой подход позволяет управлять стилями централизованно и использовать преимущества Shadow DOM.

Псевдоклассы и адаптивные стили

Shadow DOM поддерживает стандартные CSS-псевдоклассы (:hover, :focus, :active) и медиазапросы:

function ResponsiveCard() {
  return html`
    <style>
      div {
        padding: 1em;
        border-radius: 6px;
        background-color: #fafafa;
      }
      @media (max-width: 600px) {
        div {
          background-color: #e0e0e0;
        }
      }
    </style>
    <div>Адаптивная карточка</div>
  `;
}
  • Медиазапросы работают корректно даже внутри Shadow DOM.
  • Псевдоклассы применяются локально к элементам компонента.

Инкапсуляция и повторное использование стилей

Haunted поддерживает создание повторно используемых шаблонов стилей через функции:

export const cardStyles = html`
  <style>
    div {
      padding: 1em;
      border-radius: 8px;
      box-shadow: 0 2px 6px rgba(0,0,0,0.1);
    }
  </style>
`;

function MyCard() {
  return html`
    ${cardStyles}
    <div>Карточка с повторно используемыми стилями</div>
  `;
}
  • Такой подход облегчает поддержку большого количества компонентов.
  • Можно комбинировать несколько блоков стилей, сохраняя реактивность и локальность.

Итоговые принципы стилизации в Haunted

  1. Shadow DOM обеспечивает локальную изоляцию стилей.
  2. CSS-переменные позволяют передавать глобальные темы внутрь компонента.
  3. Динамические стили можно связывать с состоянием через шаблонные выражения.
  4. Внешние CSS-файлы интегрируются через adoptedStyleSheets для масштабируемых проектов.
  5. Псевдоклассы и медиазапросы работают без ограничений внутри Shadow DOM.
  6. Повторно используемые блоки стилей упрощают поддержку и унифицируют дизайн.

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