Темизация и переменные CSS

Темизация в контексте Mithril не привязана к самому фреймворку: Mithril отвечает за представление и логику, а визуальная адаптация полностью лежит на CSS. Это даёт гибкость — можно использовать нативные CSS-переменные, препроцессоры или CSS-in-JS, не нарушая архитектуру приложения.

Ключевая идея современной темизации — использование CSS Custom Properties (переменных), которые динамически меняются без пересборки DOM и без пересчёта компонентов Mithril.


CSS Custom Properties как основа темы

CSS-переменные объявляются в селекторах и доступны в рантайме:

:root {
  --color-bg: #ffffff;
  --color-text: #222222;
  --color-primary: #3b82f6;
  --border-radius: 6px;
}

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

  • работают в реальном времени
  • наследуются по DOM-дереву
  • могут изменяться через JavaScript
  • не требуют пересборки стилей

Mithril не вмешивается в работу CSS-переменных, что делает их идеальным инструментом для темизации.


Связь компонентов Mithril и CSS-переменных

Компоненты Mithril используют обычные классы и inline-стили. Рекомендуемый подход — никогда не жёстко задавать цвета и размеры в компонентах, а опираться на переменные.

const Button = {
  view: () =>
    m("button.btn", "Кнопка")
}
.btn {
  background-color: var(--color-primary);
  color: var(--color-bg);
  border-radius: var(--border-radius);
}

Таким образом:

  • компонент не знает о конкретной теме
  • смена темы не требует перерисовки компонентов
  • стили остаются декларативными

Глобальная и локальная темизация

Глобальная тема

Глобальная тема задаётся на :root или html:

html[data-theme="dark"] {
  --color-bg: #0f172a;
  --color-text: #e5e7eb;
  --color-primary: #60a5fa;
}

Переключение темы — это смена атрибута:

document.documentElement.setAttribute("data-theme", "dark")

Mithril при этом не требует m.redraw(), так как DOM не меняется.


Локальная тема на уровне компонента

CSS-переменные можно переопределять на любом узле:

.card {
  --color-bg: #f8fafc;
  background-color: var(--color-bg);
}
const Card = {
  view: () =>
    m(".card",
      m("p", "Контент карточки")
    )
}

Это позволяет:

  • создавать вложенные темы
  • переопределять палитру для отдельных модулей
  • избегать сложных BEM-конструкций

Динамическое управление темами через состояние Mithril

Состояние темы можно хранить в модели или глобальном сторе:

const Theme = {
  current: "light",
  set(name) {
    this.current = name
    document.documentElement.dataset.theme = name
  }
}

Компоненты используют только вызов:

m("button", {
  onclick: () => Theme.set("dark")
}, "Dark")

CSS реагирует автоматически. Mithril используется только как связка логики.


Реактивность без перерисовки

Изменение CSS-переменных:

  • не вызывает view
  • не трогает виртуальный DOM
  • не создаёт лишних диффов

Это особенно важно для:

  • анимаций
  • live-переключения тем
  • accessibility-настроек (контраст, масштаб)

Пример изменения переменной напрямую:

document.documentElement.style.setProperty(
  "--border-radius",
  "12px"
)

Темизация и серверный рендеринг (SSR)

При использовании SSR важно:

  • заранее определить тему
  • отрендерить корректные CSS-переменные до первого paint

Подход:

  1. тема определяется на сервере
  2. в HTML добавляется data-theme
  3. CSS применяет нужные значения
<html data-theme="dark">

Это устраняет «мигание» темы при загрузке.


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

Частые параметры:

  • цветовая схема
  • размер шрифта
  • плотность интерфейса
:root {
  --font-size-base: 16px;
  --spacing-base: 8px;
}
body {
  font-size: var(--font-size-base);
}

Настройки хранятся в localStorage, а при старте приложения применяются до монтирования Mithril:

const theme = localStorage.getItem("theme")
if (theme) {
  document.documentElement.dataset.theme = theme
}

Сочетание CSS-переменных и inline-стилей Mithril

Иногда требуется вычисляемое значение:

m("div", {
  style: {
    "--progress": "70%"
  }
})
.progress {
  width: var(--progress);
}

Это позволяет:

  • передавать данные из логики в CSS
  • сохранять визуальную логику в стилях
  • избегать генерации классов

Анимации и переходы при смене темы

CSS-переменные поддерживают transitions:

* {
  transition:
    background-color 0.3s,
    color 0.3s;
}

При смене темы интерфейс плавно адаптируется без вмешательства Mithril.


Организация структуры тем

Рекомендуемая структура:

/* base.css */
:root {
  --color-bg: ...
}

/* theme-light.css */
html[data-theme="light"] {
  ...
}

/* theme-dark.css */
html[data-theme="dark"] {
  ...
}

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

  • чистое разделение ответственности
  • простое добавление новых тем
  • отсутствие JS-логики в стилях

Типичные ошибки

Жёсткие значения в компонентах

  • ломают переиспользуемость
  • усложняют масштабирование

Использование классов вместо переменных

  • приводит к каскаду условий
  • усложняет поддержку

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

  • избыточная нагрузка
  • противоречит философии Mithril

Итоговая модель взаимодействия

  • Mithril управляет состоянием и событиями
  • CSS-переменные управляют визуалом
  • DOM остаётся стабильным
  • тема — это данные, а не логика компонентов

Такой подход масштабируется от простых SPA до сложных интерфейсов без усложнения архитектуры.