Spinner и skeleton loaders

В интерфейсах на базе SvelteKit визуальные индикаторы загрузки выполняют две ключевые задачи: информирование о процессе ожидания и снижение субъективного ощущения задержки. Наиболее распространённые подходы — spinner (индикатор вращения) и skeleton loaders (скелетные заглушки). Они отличаются по восприятию и сценариям применения.

  • Spinner сигнализирует о процессе без уточнения структуры данных
  • Skeleton loader имитирует будущий контент, создавая ощущение постепенной отрисовки

Обе категории активно реализуются в UI-библиотеках для SvelteKit: через компоненты, директивы или CSS-утилиты.


Spinner: принципы и реализация

Базовая структура

Spinner представляет собой анимированный элемент, обычно реализуемый через CSS:

<div class="spinner"></div>

<style>
.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(0,0,0,0.1);
  border-top-color: #333;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
</style>

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

Spinner часто применяется в связке с реактивными переменными:

<script>
  let loading = true;

  async function loadData() {
    loading = true;
    await fetch('/api/data');
    loading = false;
  }
</script>

{#if loading}
  <Spinner />
{:else}
  <Content />
{/if}

Интеграция с load-функциями

В SvelteKit загрузка данных происходит через load:

export async function load({ fetch }) {
  const res = await fetch('/api/data');
  const data = await res.json();

  return { data };
}

Spinner используется на уровне компонента страницы, пока данные не доступны.


UI-библиотеки: Spinner

Skeleton UI

Компонент:

<Spinner size="lg" />

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

  • параметры размера и цвета
  • интеграция с Tailwind
  • минималистичный API

Flowbite-Svelte

<Spinner color="blue" size="6" />

Поддержка:

  • разных цветовых схем
  • accessibility (aria-label)

Carbon Components Svelte

<Loading active={true} />

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

  • строгий корпоративный стиль
  • встроенные состояния загрузки

Skeleton loaders: концепция

Skeleton loader отображает структурную модель контента:

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

Это снижает когнитивную нагрузку, так как пользователь «видит», что именно загружается.


Базовая реализация skeleton

<div class="skeleton"></div>

<style>
.skeleton {
  width: 100%;
  height: 16px;
  background: linear-gradient(
    90deg,
    #eee 25%,
    #ddd 37%,
    #eee 63%
  );
  background-size: 400% 100%;
  animation: shimmer 1.4s ease infinite;
}

@keyframes shimmer {
  100% {
    background-position: -400% 0;
  }
}
</style>

Композиция skeleton-компонентов

Реалистичные skeleton-интерфейсы создаются через композицию:

<div class="card">
  <div class="image skeleton"></div>
  <div class="text skeleton"></div>
  <div class="text skeleton short"></div>
</div>

<style>
.image {
  height: 150px;
}

.text {
  height: 16px;
  margin-top: 8px;
}

.short {
  width: 60%;
}
</style>

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

{#if loading}
  <SkeletonCard />
{:else}
  <RealCard {data} />
{/if}

Skeleton-элементы должны максимально повторять структуру итогового компонента.


UI-библиотеки: Skeleton loaders

Skeleton UI

<Skeleton height="20px" width="100%" />

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

  • декларативный API
  • встроенная анимация shimmer
  • поддержка темизации

Flowbite-Svelte

<Skeleton class="w-full h-4" />

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

  • Tailwind-first подход
  • гибкость кастомизации

Svelte Material UI

<LinearProgress indeterminate />

Хотя это не классический skeleton, используется как альтернатива.


Сравнение Spinner и Skeleton

Критерий Spinner Skeleton loader
Информативность Низкая Высокая
Восприятие скорости Медленнее Быстрее
Сложность реализации Простая Средняя
UX Базовый Продвинутый

Практики использования

Когда использовать spinner

  • короткие операции (< 1 секунды)
  • фоновые действия (отправка формы)
  • отсутствие заранее известной структуры

Когда использовать skeleton

  • загрузка списков или карточек
  • медленные API-запросы
  • SSR + hydration сценарии

Гибридный подход

Комбинация двух подходов:

{#if loading}
  <SkeletonList />
  <Spinner small />
{:else}
  <Content />
{/if}

Применяется для:

  • длительных операций
  • критически важных данных

Управление состояниями загрузки

В SvelteKit часто используются store:

import { writable } from 'svelte/store';

export const isLoading = writable(false);

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

<script>
  import { isLoading } from './store';
</script>

{#if $isLoading}
  <Spinner />
{/if}

Accessibility (доступность)

Spinner

<div role="status" aria-live="polite">
  Loading...
</div>

Skeleton

  • не должен вводить в заблуждение screen reader
  • рекомендуется скрывать через aria-hidden="true"

Производительность

Skeleton loaders:

  • увеличивают DOM-дерево
  • требуют оптимизации при больших списках

Решения:

  • виртуализация (например, через svelte-virtual)
  • ограничение количества placeholder-элементов

Анимации и восприятие

Типы анимаций:

  • shimmer (градиент)
  • pulse (пульсация)
  • fade-in (появление контента)

Пример pulse:

@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.4; }
  100% { opacity: 1; }
}

SSR и загрузка

В SvelteKit:

  • при SSR skeleton может не понадобиться (данные уже есть)
  • при CSR skeleton критичен

Комбинированный подход:

{#if data}
  <Content />
{:else}
  <Skeleton />
{/if}

Кастомизация через Tailwind

<div class="animate-pulse bg-gray-300 h-4 w-full rounded"></div>

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

  • минимальный CSS
  • интеграция с UI-библиотеками

Паттерны проектирования

Skeleton как отдельный компонент

<SkeletonCard />
<SkeletonList count={5} />

Привязка к реальным компонентам

<Card loading={true} />

Внутри:

{#if loading}
  <Skeleton />
{:else}
  <RealContent />
{/if}

Ошибки и антипаттерны

  • использование spinner при длительной загрузке
  • несоответствие skeleton реальному UI
  • слишком агрессивные анимации
  • отсутствие fallback при ошибке загрузки

Итоговая архитектура загрузки

  1. Store состояния загрузки
  2. UI-компоненты (Spinner / Skeleton)
  3. Интеграция с load-функциями
  4. Учет SSR/CSR
  5. Accessibility

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