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

Интерфейс

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

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


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

Кнопка переключения темы с плавной анимацией иконок солнца и луны. Сохраняет выбор в 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>

<span class={cn('inset-shadow inline-flex rounded-xl bg-background-inset p-1.5', className)}>
	<button
		type="button"
		class="card relative inline-flex size-9 items-center justify-center rounded-lg border border-border bg-background text-foreground transition-colors duration-150 hover:border-accent/60 active:scale-[0.95]"
		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>
</span>

<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>

<span class={cn('inset-shadow inline-flex rounded-xl bg-background-inset p-1.5', className)}>
	<button
		type="button"
		class="card relative inline-flex size-9 items-center justify-center rounded-lg border border-border bg-background text-foreground transition-colors duration-150 hover:border-accent/60 active:scale-[0.95]"
		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>
</span>

<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>