Инкапсуляция стилей в компонентах

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


Синтаксис встроенных стилей

Каждый компонент Riot может содержать собственные CSS-правила внутри блока <style>:

<my-button>
  <button>{opts.label}</button>

  <style>
    button {
      background-color: #3498db;
      color: white;
      border: none;
      padding: 10px 20px;
      cursor: pointer;
      border-radius: 4px;
    }

    button:hover {
      background-color: #2980b9;
    }
  </style>
</my-button>

CSS, определённый внутри компонента, применяется только к элементам этого компонента. Это достигается за счёт автоматической генерации уникальных атрибутов для каждого компонента, которые Riot добавляет к селекторам.


Shadow DOM и scoped CSS

Riot.js поддерживает два механизма инкапсуляции стилей:

  1. Scoped CSS — стандартный режим, при котором фреймворк автоматически добавляет уникальные атрибуты к элементам и стилям.
  2. Shadow DOM — полностью изолированная область стилей и разметки, предотвращающая любые внешние влияния и обеспечивающая строгую изоляцию.

Пример использования Shadow DOM:

<my-card shadow>
  <div class="card">
    <h2>{opts.title}</h2>
    <p>{opts.content}</p>
  </div>

  <style>
    .card {
      padding: 20px;
      border: 1px solid #ccc;
      border-radius: 8px;
      background-color: #f9f9f9;
    }
    h2 {
      margin: 0 0 10px;
      font-size: 18px;
    }
  </style>
</my-card>

Атрибут shadow активирует использование Shadow DOM, изолируя стили компонента полностью от глобального CSS.


Преимущества инкапсуляции стилей

  • Отсутствие конфликтов: Каждый компонент работает со своими стилями независимо.
  • Повторное использование: Компоненты можно легко переносить между проектами без необходимости правки CSS.
  • Локальная поддержка: Изменения стилей ограничены компонентом, что снижает риск непреднамеренного влияния на другие части приложения.

Работа с глобальными стилями

Иногда необходимо подключать общие стили, например, переменные CSS или темы. Riot.js позволяет комбинировать локальные и глобальные стили:

<theme-wrapper>
  <div class="container">
    <my-button label="Click me"></my-button>
  </div>

  <style>
    :global(.container) {
      padding: 40px;
      background-color: #ecf0f1;
    }
  </style>
</theme-wrapper>

Использование псевдокласса :global позволяет определять правила, которые выходят за рамки компонента и применяются к всему документу.


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

Стили в Riot.js могут быть динамическими, изменяясь в зависимости от состояния компонента:

<status-indicator>
  <span class={stateClass}>{status}</span>

  <style>
    span {
      padding: 5px 10px;
      border-radius: 4px;
      color: white;
    }
    .active {
      background-color: green;
    }
    .inactive {
      background-color: red;
    }
  </style>

  <script>
    export default {
      status: 'active',
      get stateClass() {
        return this.status === 'active' ? 'active' : 'inactive';
      }
    }
  </script>
</status-indicator>

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


Взаимодействие с CSS-переменными

Riot.js поддерживает использование CSS-переменных для настройки стилей на уровне компонентов:

<color-box>
  <div class="box"></div>

  <style>
    .box {
      width: 100px;
      height: 100px;
      background-color: var(--box-color, #3498db);
    }
  </style>

  <script>
    export default {
      onMounted() {
        this.root.style.setProperty('--box-color', '#e74c3c');
      }
    }
  </script>
</color-box>

Использование CSS-переменных позволяет динамически менять стили компонента без изменения классов и селекторов.


Практические рекомендации

  1. Для малых и автономных компонентов достаточно использовать scoped CSS.
  2. Для больших виджетов или библиотек UI, где требуется полная изоляция, предпочтительнее Shadow DOM.
  3. Общие темы и переменные удобно держать в глобальном CSS с использованием :global.
  4. Динамические стили лучше реализовывать через классы и CSS-переменные, чтобы минимизировать переписывание стилей в JavaScript.

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