Перейти к содержимому

Интерфейс

Переключатель темы

Кнопка переключения между светлой и тёмной темой с плавной анимацией иконок.


Переключатель темы

Кнопка переключения темы с плавной анимацией иконок солнца и луны. Сохраняет выбор в localStorage и автоматически синхронизируется с системными настройками.

Превью

<script lang="ts">
	import { cn } from '$lib/utils/cn';
	import { themeStore } from '$lib/stores/theme.svelte';
	import Sun from 'carbon-icons-svelte/lib/Sun.svelte';
	import Moon from 'carbon-icons-svelte/lib/Moon.svelte';

	type Props = {
		class?: string;
	};

	const props = $props();
	const className = $derived((props as Props).class ?? '');
</script>

<button
	type="button"
	class={cn(
		'group transition-scale inset-shadow relative inline-flex size-9 items-center justify-center rounded-sm bg-background-inset text-foreground duration-150 ease-out active:scale-[0.95]',
		className
	)}
	onclick={themeStore.toggle}
	aria-label={themeStore.isDark ? 'Светлая тема' : 'Тёмная тема'}
>
	<span class="sr-only">{themeStore.isDark ? 'Switch to light mode' : 'Switch to dark mode'}</span>
	<span class="theme-toggle-icon theme-toggle-sun">
		<Sun size={16} />
	</span>
	<span class="theme-toggle-icon theme-toggle-moon">
		<Moon size={16} />
	</span>
</button>

<style>
	.theme-toggle-icon {
		position: absolute;
		opacity: 0;
		filter: blur(4px);
		scale: 0.25;
		transition:
			opacity 150ms ease-out,
			filter 150ms ease-out,
			scale 150ms ease-out;
		will-change: opacity, filter, scale;
	}

	.theme-toggle-sun {
		opacity: 1;
		filter: blur(0);
		scale: 1;
	}

	:global(.dark) .theme-toggle-sun {
		opacity: 0;
		filter: blur(4px);
		scale: 0.25;
	}

	:global(.dark) .theme-toggle-moon {
		opacity: 1;
		filter: blur(0);
		scale: 1;
	}
</style>
<script lang="ts">
	import { cn } from '$lib/utils/cn';
	import { themeStore } from '$lib/stores/theme.svelte';
	import Sun from 'carbon-icons-svelte/lib/Sun.svelte';
	import Moon from 'carbon-icons-svelte/lib/Moon.svelte';

	type Props = {
		class?: string;
	};

	const props = $props();
	const className = $derived((props as Props).class ?? '');
</script>

<button
	type="button"
	class={cn(
		'group transition-scale inset-shadow relative inline-flex size-9 items-center justify-center rounded-sm bg-background-inset text-foreground duration-150 ease-out active:scale-[0.95]',
		className
	)}
	onclick={themeStore.toggle}
	aria-label={themeStore.isDark ? 'Светлая тема' : 'Тёмная тема'}
>
	<span class="sr-only">{themeStore.isDark ? 'Switch to light mode' : 'Switch to dark mode'}</span>
	<span class="theme-toggle-icon theme-toggle-sun">
		<Sun size={16} />
	</span>
	<span class="theme-toggle-icon theme-toggle-moon">
		<Moon size={16} />
	</span>
</button>

<style>
	.theme-toggle-icon {
		position: absolute;
		opacity: 0;
		filter: blur(4px);
		scale: 0.25;
		transition:
			opacity 150ms ease-out,
			filter 150ms ease-out,
			scale 150ms ease-out;
		will-change: opacity, filter, scale;
	}

	.theme-toggle-sun {
		opacity: 1;
		filter: blur(0);
		scale: 1;
	}

	:global(.dark) .theme-toggle-sun {
		opacity: 0;
		filter: blur(4px);
		scale: 0.25;
	}

	:global(.dark) .theme-toggle-moon {
		opacity: 1;
		filter: blur(0);
		scale: 1;
	}
</style>

Особенности

  • Плавная анимация переключения иконок через CSS transitions (blur + scale)
  • Автоматическое определение системной темы через prefers-color-scheme
  • Сохранение выбора в localStorage
  • Доступность: кнопка с aria-label для экранных читалок
  • Корректно работает при серверном рендеринге (SSR)

Использование

<script>
	import ThemeToggle from '$lib/components/ui/ThemeToggle.svelte';
</script>

<ThemeToggle />
<script>
	import ThemeToggle from '$lib/components/ui/ThemeToggle.svelte';
</script>

<ThemeToggle />

Пропсы

Пропс Тип По умолчанию Описание
class string '' Дополнительные CSS-классы

Theme Store

Компонент использует themeStore из $lib/stores/theme.svelte.ts:

Свойство / Метод Тип Описание
current 'light' 'dark' Текущая тема
isDark boolean Проверка тёмной темы
set(theme) (theme: Theme) => void Установить тему
toggle() () => void Переключить тему

Прямое использование store

<script>
	import { themeStore } from '$lib/stores/theme.svelte';
</script>

{#if themeStore.isDark}
	<p>Тёмная тема активна</p>
{/if}

<button onclick={() => themeStore.toggle()}>
	Переключить тему
</button>
<script>
	import { themeStore } from '$lib/stores/theme.svelte';
</script>

{#if themeStore.isDark}
	<p>Тёмная тема активна</p>
{/if}

<button onclick={() => themeStore.toggle()}>
	Переключить тему
</button>