Dark mode реализация

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

Использование writable store для темы

Для управления темой создаётся writable store из svelte/store:

import { writable } from 'svelte/store';

export const theme = writable('light');

theme будет хранить текущее состояние темы: 'light' или 'dark'.

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

theme.subscribe(value => {
    localStorage.setItem('theme', value);
});

При инициализации приложения можно проверять наличие сохранённой темы:

if (localStorage.getItem('theme')) {
    theme.set(localStorage.getItem('theme'));
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    theme.set('dark');
}

Применение темы через CSS-классы

Наиболее удобный способ — добавление CSS-класса на корневой элемент <html> или <body>:

theme.subscribe(value => {
    document.documentElement.classList.toggle('dark', value === 'dark');
});

В CSS создаются соответствующие стили:

:root {
    --background-color: #ffffff;
    --text-color: #000000;
}

.dark {
    --background-color: #121212;
    --text-color: #e0e0e0;
}

body {
    background-color: var(--background-color);
    color: var(--text-color);
    transition: background-color 0.3s ease, color 0.3s ease;
}

Использование CSS-переменных позволяет легко масштабировать тему на все компоненты интерфейса и быстро менять цвета.

Интеграция с компонентами SvelteKit UI libs

Большинство UI-библиотек для SvelteKit поддерживают динамическую передачу классов или стилей. Например, при использовании компонентов типа Button, Card, Modal, достаточно передавать тему через пропсы или классы:

<Button class:dark={$theme === 'dark'}>
    Кнопка
</Button>

Или через глобальные стили:

.dark .button {
    background-color: #1f1f1f;
    color: #f5f5f5;
}

Система переключения темы

Для переключения темы создаётся компонент-кнопка:

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

    const toggleTheme = () => {
        theme.update(current => current === 'light' ? 'dark' : 'light');
    };
</script>

<button on:click={toggleTheme}>
    Переключить тему
</button>

Состояние темы автоматически обновляется через store, и все подписанные компоненты меняют внешний вид.

Работа с SSR в SvelteKit

SvelteKit поддерживает server-side rendering (SSR), поэтому важно учитывать начальное значение темы на сервере. Для этого создаются hooks или используется layout load function, чтобы на сервере определить предпочтение пользователя:

export const load = async ({ cookies }) => {
    const theme = cookies.get('theme') || 'light';
    return { theme };
};

На клиенте store инициализируется полученным значением:

theme.set(data.theme);

Адаптация сторонних библиотек

При использовании UI-библиотек вроде Svelte Material UI, Flowbite Svelte или Shadcn UI:

  • Большинство компонентов поддерживают class и style props, что позволяет применять тёмную тему через классы.
  • Некоторые библиотеки предоставляют собственные темы, которые можно динамически подключать.
  • Для глобального изменения внешнего вида компонентов можно использовать CSS-переменные или темы SCSS, если библиотека их поддерживает.

Плавные переходы и анимации

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

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

Это позволяет избежать резкой смены цветов и делает интерфейс более приятным.

Дополнительные возможности

  • Хранение темы в sessionStorage или через cookie для SSR и синхронизации между устройствами.
  • Реактивное изменение SVG-иконок или изображений в зависимости от темы.
  • Создание нескольких вариантов тем (dark, dim, solarized) с одной системой переключения.

Эта схема позволяет построить масштабируемую, реактивную и удобную систему Dark Mode в проектах на SvelteKit с использованием UI-библиотек.