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

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

Определение стилей внутри компонента

В Atomico стили задаются через функцию css или в виде объекта внутри компонента. Основная особенность — возможность связывать CSS с реактивными свойствами.

import { c, css, useState } from "atomico";

const buttonStyles = css`
    :host {
        display: inline-block;
        padding: 10px 20px;
        border-radius: 5px;
        background-color: var(--bg-color, #3498db);
        color: var(--text-color, #fff);
        cursor: pointer;
        transition: background-color 0.3s;
    }
`;

function Button() {
    const [hover, setHover] = useState(false);

    return (
        <host shadowDom>
            <buttonStyles />
            <button
                onMouseEn ter={() => setHover(true)}
                onMouseLe ave={() => setHover(false)}
                style={{
                    "--bg-color": hover ? "#2980b9" : "#3498db"
                }}
            >
                Кнопка
            </button>
        </host>
    );
}

customElements.define("x-button", c(Button));

Особенности подхода:

  • :host позволяет задавать стили для самого веб-компонента.
  • Использование CSS-переменных позволяет динамически изменять внешний вид без переписывания классов.
  • Реактивное состояние через useState напрямую влияет на свойства стиля.

Связывание реактивных данных с CSS

Atomico поддерживает реактивное обновление стилей через свойства компонента. Можно определять стили на основе пропсов или состояния компонента, используя объектную форму style или css с функцией.

function Card({ highlight }) {
    return (
        <host shadowDom>
            <style>
                {`
                    :host {
                        display: block;
                        padding: 20px;
                        border-radius: 10px;
                        background-color: ${highlight ? "#f1c40f" : "#ecf0f1"};
                        transition: background-color 0.5s;
                    }
                `}
            </style>
            <div>Контент карточки</div>
        </host>
    );
}

customElements.define("x-card", c(Card));

Ключевой момент: использование шаблонных литералов позволяет вычислять значения стилей динамически при каждом рендере, что обеспечивает гибкость интерфейса.

Стили через функцию styleMap

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

import { useState } from "atomico";
import { styleMap } from "atomico/utils";

function Box() {
    const [size, setSize] = useState(100);

    return (
        <host shadowDom>
            <div
                style={styleMap({
                    width: `${size}px`,
                    height: `${size}px`,
                    backgroundColor: size > 150 ? "#e74c3c" : "#2ecc71",
                    transition: "all 0.3s"
                })}
            >
                Динамический блок
            </div>
            <button onCl ick={() => setSize(size + 10)}>Увеличить</button>
        </host>
    );
}

customElements.define("x-box", c(Box));

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

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

Инкапсуляция стилей с Shadow DOM

Atomico по умолчанию работает с Shadow DOM, что обеспечивает изоляцию стилей компонентов. Это позволяет не беспокоиться о конфликте с глобальными CSS и упрощает управление динамическими стилями.

function Badge({ active }) {
    return (
        <host shadowDom>
            <style>
                {`
                    :host {
                        display: inline-block;
                        padding: 5px 15px;
                        border-radius: 12px;
                        background-color: ${active ? "#27ae60" : "#95a5a6"};
                        color: #fff;
                        font-weight: bold;
                    }
                `}
            </style>
            Статус
        </host>
    );
}

customElements.define("x-badge", c(Badge));

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

Поддержка темизации

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

function ThemedPanel({ theme }) {
    return (
        <host shadowDom>
            <style>
                {`
                    :host {
                        display: block;
                        padding: 20px;
                        border-radius: 10px;
                        background-color: var(--bg-color, #fff);
                        color: var(--text-color, #000);
                    }
                `}
            </style>
            <div
                style={{
                    "--bg-color": theme === "dark" ? "#2c3e50" : "#ecf0f1",
                    "--text-color": theme === "dark" ? "#ecf0f1" : "#2c3e50"
                }}
            >
                Панель с темой {theme}
            </div>
        </host>
    );
}

customElements.define("x-themed-panel", c(ThemedPanel));

Взаимодействие с CSS-классами

Помимо прямого назначения стилей через style, Atomico позволяет управлять CSS-классами динамически с помощью привязки через объект или функцию:

function ToggleButton({ active }) {
    return (
        <host shadowDom>
            <style>
                {`
                    .active {
                        background-color: #e67e22;
                        color: #fff;
                    }
                    .inactive {
                        background-color: #bdc3c7;
                        color: #2c3e50;
                    }
                `}
            </style>
            <button class={active ? "active" : "inactive"}>
                {active ? "Включено" : "Выключено"}
            </button>
        </host>
    );
}

customElements.define("x-toggle-button", c(ToggleButton));

Такой подход позволяет комбинировать преимущества CSS-классов с реактивностью JavaScript и динамическими переменными.

Особенности производительности

  • Изменение CSS-переменных минимизирует перерасчет стилей браузером.
  • Shadow DOM изолирует стили и уменьшает необходимость глубокого селектора.
  • Использование реактивного состояния через useState обеспечивает эффективный ререндер только изменяющихся частей.

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