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

В современных веб-приложениях часто требуется поддержка нескольких цветовых тем: светлой, тёмной или пользовательских вариантов. SvelteKit, благодаря своей реактивной архитектуре и интеграции с UI-библиотеками, позволяет реализовать динамическую смену тем с высокой производительностью и гибкостью.

Управление состоянием темы

Первым шагом является создание глобального состояния темы. В SvelteKit для этого используется stores из пакета svelte/store:

import { writable } from 'svelte/store';

export const theme = writable('light');

writable создаёт реактивное хранилище, которое позволяет компонентам подписываться на изменения темы и автоматически обновлять интерфейс при переключении.

Подключение темы к компонентам

Большинство UI-библиотек для SvelteKit (например, Svelte Material UI, Shadcn UI) предоставляют возможность передачи темы через пропсы или контекст. Рассмотрим пример с глобальной CSS-переменной:

<script>
  import { theme } from './stores.js';

  let currentTheme;
  const unsubscribe = theme.subscribe(value => currentTheme = value);
  
  $: document.documentElement.setAttribute('data-theme', currentTheme);
</script>

<button on:click={() => theme.set(currentTheme === 'light' ? 'dark' : 'light')}>
  Сменить тему
</button>

<style>
  :root[data-theme='light'] {
    --background-color: #ffffff;
    --text-color: #111111;
  }

  :root[data-theme='dark'] {
    --background-color: #1e1e1e;
    --text-color: #f0f0f0;
  }

  body {
    background-color: var(--background-color);
    color: var(--text-color);
  }
</style>

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

Интеграция с UI-библиотеками

UI-библиотеки для SvelteKit обычно предоставляют собственные механизмы темизации:

  1. Svelte Material UI (SMUI) SMUI использует тему через SMUITheme и CSS-переменные. Для динамической смены темы можно подключить store к настройкам:

    <SMUITheme theme={$theme}>
      <Button>Пример кнопки</Button>
    </SMUITheme>
  2. Shadcn UI для SvelteKit Shadcn UI применяет темы через class на уровне корневого компонента или через контекст. Для динамической смены темы достаточно менять класс на <body> или контейнере приложения.

    <div class={$theme}>
      <Card>Контент карточки</Card>
    </div>

Локальное сохранение выбора темы

Чтобы пользовательский выбор темы сохранялся между сессиями, можно использовать localStorage:

import { writable } from 'svelte/store';

const storedTheme = localStorage.getItem('theme') || 'light';
export const theme = writable(storedTheme);

theme.subscribe(value => {
  localStorage.setItem('theme', value);
  document.documentElement.setAttribute('data-theme', value);
});

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

Плавные переходы между темами

Для улучшения UX рекомендуется добавить анимацию при смене темы:

body {
  transition: background-color 0.3s ease, color 0.3s ease;
}

Переходы CSS обеспечивают плавное изменение фона и цвета текста без перерисовки компонентов.

Поддержка пользовательских тем

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

export const themes = {
  light: {
    '--background-color': '#ffffff',
    '--text-color': '#111111'
  },
  dark: {
    '--background-color': '#1e1e1e',
    '--text-color': '#f0f0f0'
  },
  ocean: {
    '--background-color': '#2b3a42',
    '--text-color': '#a6c8d1'
  }
};

export function applyTheme(name) {
  const selected = themes[name];
  Object.entries(selected).forEach(([varName, value]) => {
    document.documentElement.style.setProperty(varName, value);
  });
}

Это позволяет добавлять новые темы без изменения компонентов и UI-библиотеки, просто вызывая applyTheme('ocean').

Комбинация с реактивными компонентами

SvelteKit позволяет связывать изменение темы с реактивными переменными в компонентах. Например, переключатель темы может автоматически менять внешний вид всех кнопок и карточек, если их стили зависят от CSS-переменных.

<Button style="background-color: var(--background-color); color: var(--text-color)">
  Реактивная кнопка
</Button>

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

Итоговый паттерн

  • Создать глобальный store для темы.
  • Привязать CSS-переменные или классы к теме.
  • Интегрировать с UI-библиотеками через пропсы или контекст.
  • Добавить сохранение выбора темы через localStorage.
  • При необходимости реализовать плавные переходы и пользовательские темы через объекты с переменными.

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