Navigation Menu

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

<script lang="ts">
  import { page } from '$app/stores';
  import { derived } from 'svelte/store';

  const navItems = [
    { name: 'Главная', href: '/' },
    { name: 'О нас', href: '/about' },
    { name: 'Услуги', href: '/services' },
    { name: 'Контакты', href: '/contact' }
  ];

  const activeRoute = derived(page, $page => $page.url.pathname);
</script>

<nav>
  <ul class="nav-list">
    {#each navItems as item}
      <li class:active={$activeRoute === item.href}>
        <a href={item.href}>{item.name}</a>
      </li>
    {/each}
  </ul>
</nav>

<style>
  .nav-list {
    display: flex;
    gap: 1.5rem;
    list-style: none;
    padding: 0;
    margin: 0;
  }

  .nav-list li a {
    text-decoration: none;
    color: #333;
    font-weight: 500;
  }

  .nav-list li.active a {
    color: #0070f3;
    border-bottom: 2px solid #0070f3;
  }
</style>

Использование реактивных переменных и сторов

SvelteKit интегрирует стор page для отслеживания текущего URL. При построении меню важно динамически подсвечивать активный пункт. Это достигается с помощью derived store, который позволяет вычислять значение на основе текущего состояния page.

const activeRoute = derived(page, $page => $page.url.pathname);

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

Динамическая генерация пунктов меню

Массив navItems позволяет легко добавлять и удалять ссылки, делая компонент масштабируемым:

const navItems = [
  { name: 'Главная', href: '/' },
  { name: 'Блог', href: '/blog' },
  { name: 'Контакты', href: '/contact' }
];

Использование #each гарантирует, что изменения в массиве автоматически отразятся в интерфейсе.

{#each navItems as item}
  <li class:active={$activeRoute === item.href}>
    <a href={item.href}>{item.name}</a>
  </li>
{/each}

Адаптивное меню и мобильная версия

Для мобильных устройств меню часто скрывается за гамбургер-иконкой. SvelteKit позволяет легко управлять состоянием открытия меню через реактивные переменные.

<script lang="ts">
  let isOpen = false;
</script>

<button on:click={() => isOpen = !isOpen} class="hamburger">
  &#9776;
</button>

<ul class:open={isOpen} class="mobile-nav">
  {#each navItems as item}
    <li>
      <a href={item.href} on:click={() => isOpen = false}>{item.name}</a>
    </li>
  {/each}
</ul>

<style>
  .mobile-nav {
    display: none;
    flex-direction: column;
    list-style: none;
    padding: 0;
  }

  .mobile-nav.open {
    display: flex;
  }

  .hamburger {
    font-size: 2rem;
    background: none;
    border: none;
    cursor: pointer;
  }
</style>

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

Популярные Svelte UI библиотеки, такие как Svelte Material UI (SMUI) и Flowbite Svelte, предоставляют готовые компоненты навигации с анимацией и адаптивностью.

Пример с SMUI:

<script>
  import { TopAppBar, TopAppBarRow, TopAppBarSection, TopAppBarTitle } from '@smui/top-app-bar';
  import { Button } from '@smui/button';

  const navItems = ['Главная', 'Блог', 'Контакты'];
</script>

<TopAppBar>
  <TopAppBarRow>
    <TopAppBarSection align="start">
      <TopAppBarTitle>Мой сайт</TopAppBarTitle>
    </TopAppBarSection>
    <TopAppBarSection align="end">
      {#each navItems as item}
        <Button href={"/" + item.toLowerCase()}>{item}</Button>
      {/each}
    </TopAppBarSection>
  </TopAppBarRow>
</TopAppBar>

Использование UI-библиотек позволяет:

  • Автоматически обрабатывать адаптивность;
  • Добавлять эффекты наведения и анимации;
  • Снижать количество кастомного CSS.

Состояние навигации и хранение в сторе

Для сложных приложений меню может зависеть от авторизации пользователя или других динамических данных. Использование Svelte stores позволяет централизованно управлять доступными пунктами.

import { writable } from 'svelte/store';

export const userNav = writable([
  { name: 'Профиль', href: '/profile' },
  { name: 'Выход', href: '/logout' }
]);

Подключение к компоненту:

{#each $userNav as item}
  <li>
    <a href={item.href}>{item.name}</a>
  </li>
{/each}

Это обеспечивает динамическое изменение меню без перезагрузки страницы.

Анимация открытия/закрытия меню

Svelte предоставляет встроенные функции для анимации: transition:fade и animate:flip помогают создать плавное появление элементов меню.

<ul class="mobile-nav" transition:fade>
  {#each navItems as item (item.href)}
    <li>{item.name}</li>
  {/each}
</ul>

Использование ключей (item.href) обеспечивает корректную работу анимаций при изменении массива.

Поддержка вложенных меню

Для многоуровневой навигации применяются вложенные списки с управлением раскрытием:

{#each navItems as item}
  <li>
    <a href={item.href}>{item.name}</a>
    {#if item.submenu}
      <ul class="submenu">
        {#each item.submenu as sub}
          <li><a href={sub.href}>{sub.name}</a></li>
        {/each}
      </ul>
    {/if}
  </li>
{/each}

CSS для раскрывающихся подменю:

.submenu {
  display: none;
  position: absolute;
  list-style: none;
  padding: 0.5rem;
}

li:hover > .submenu {
  display: block;
}

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


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