Компоненты вкладок (Tabs) позволяют структурировать интерфейс, разделяя содержимое на логические секции, доступные без перезагрузки страницы. В экосистеме SvelteKit UI-библиотек (например, Skeleton, Flowbite-Svelte, Radix Svelte) вкладки реализуются с учетом реактивности Svelte и принципа декларативного UI.
Типичная реализация вкладок включает три ключевых элемента:
Пример на Svelte:
<script>
let activeTab = 'tab1';
</script>
<div class="tabs">
<button on:click={() => activeTab = 'tab1'} class:active={activeTab === 'tab1'}>
Вкладка 1
</button>
<button on:click={() => activeTab = 'tab2'} class:active={activeTab === 'tab2'}>
Вкладка 2
</button>
</div>
<div class="tab-content">
{#if activeTab === 'tab1'}
<p>Контент первой вкладки</p>
{:else if activeTab === 'tab2'}
<p>Контент второй вкладки</p>
{/if}
</div>
Ключевая особенность — использование реактивных переменных.
Переменная activeTab управляет отображением. При изменении
значения происходит автоматический ререндер.
В более сложных случаях состояние можно:
store (например, writable)Корректная реализация вкладок требует соблюдения стандартов ARIA:
role="tablist" — контейнер вкладокrole="tab" — кнопкиrole="tabpanel" — панелиaria-selected — активная вкладкаaria-controls — связь вкладки и панелиПример:
<div role="tablist">
<button role="tab" aria-selected={activeTab === 'tab1'}>
Tab 1
</button>
</div>
Это особенно важно при использовании готовых UI-библиотек, где доступность уже встроена.
<TabGroup>
<TabList>
<Tab>Profile</Tab>
<Tab>Settings</Tab>
</TabList>
<TabPanels>
<TabPanel>Profile content</TabPanel>
<TabPanel>Settings content</TabPanel>
</TabPanels>
</TabGroup>
Особенности:
<Tabs>
<Tab title="Tab 1">Content 1</Tab>
<Tab title="Tab 2">Content 2</Tab>
</Tabs>
Поддерживает:
Содержимое вкладки создается только при активации:
{#if activeTab === 'tab1'}
<HeavyComponent />
{/if}
Это уменьшает нагрузку при инициализации.
{#each tabs as tab}
<button on:click={() => activeTab = tab.id}>
{tab.title}
</button>
{/each}
Подходит для CMS-интерфейсов и дашбордов.
Интеграция с SvelteKit:
import { goto } from '$app/navigation';
goto(`/settings?tab=profile`);
Позволяет сохранять состояние при перезагрузке страницы.
Аккордеоны используются для отображения скрытого контента, который раскрывается по клику. Это особенно полезно для FAQ, списков настроек и вложенных структур.
<script>
let open = false;
</script>
<div class="accordion">
<button on:click={() => open = !open}>
Заголовок
</button>
{#if open}
<div class="content">
Скрытый контент
</div>
{/if}
</div>
<script>
let openIndex = null;
function toggle(index) {
openIndex = openIndex === index ? null : index;
}
</script>
{#each items as item, i}
<div>
<button on:click={() => toggle(i)}>
{item.title}
</button>
{#if openIndex === i}
<div>{item.content}</div>
{/if}
</div>
{/each}
let openItems = new Set();
function toggle(id) {
if (openItems.has(id)) {
openItems.delete(id);
} else {
openItems.add(id);
}
}
Позволяет открывать несколько секций одновременно.
Svelte предоставляет встроенные инструменты:
import { slide } from 'svelte/transition';
{#if open}
<div transition:slide>
Контент
</div>
{/if}
Дополнительно:
fadescaleРекомендуется использовать:
aria-expandedaria-controlsid для связи элементов<button aria-expanded={open}>
<Accordion>
<AccordionItem>
<AccordionHeader>Title</AccordionHeader>
<AccordionContent>Content</AccordionContent>
</AccordionItem>
</Accordion>
Особенности:
<Accordion>
<AccordionItem title="Section 1">
Content 1
</AccordionItem>
</Accordion>
Поддерживает:
| Характеристика | Tabs | Accordion |
|---|---|---|
| Отображение | Одна секция | Одна или несколько |
| Пространство | Горизонтальное | Вертикальное |
| Использование | Переключение контекста | Раскрытие деталей |
| UX | Быстрый доступ | Последовательное чтение |
| Подходит для | Панелей, настроек | FAQ, списков |
<Tabs bind:value={activeTab} />
Современные UI-библиотеки используют composition API:
<Tabs>
<Tabs.List>
<Tabs.Trigger />
</Tabs.List>
<Tabs.Content />
</Tabs>
Это повышает гибкость и переиспользуемость.
Tabs и Accordion должны корректно работать при серверном рендеринге:
export function load({ url }) {
return {
tab: url.searchParams.get('tab') ?? 'default'
};
}
Позволяет:
Большинство библиотек используют Tailwind:
class="bg-primary text-white"
<AccordionItem>
<slot name="header" />
<slot />
</AccordionItem>
Позволяет внедрять кастомный контент.
Вынесение логики в composable:
export function useAccordion() {
let open = writable(null);
return { open };
}