ShardsUI is in beta. APIs may change before 1.0.

Tabs

Switchable content panels.

A short overview of closures and where they tend to show up throughout everyday code.
<script lang="ts">
  import { Tabs } from '@shardsui/svelte/tabs'

  const tabs = [
    {
      value: 'overview',
      label: 'Overview',
      body: 'A short overview of closures and where they tend to show up throughout everyday code.'
    },
    {
      value: 'transcript',
      label: 'Transcript',
      body: 'A closure is a function that remembers the scope where it was originally created in.'
    },
    {
      value: 'notes',
      label: 'Notes',
      body: 'Revisit the counter example — the returned function keeps its own copy of count.'
    }
  ]
</script>

<Tabs.Root class="w-fit rounded-md border border-gray-200" value="overview">
  <Tabs.List class="relative z-0 flex gap-1 border-b border-gray-200 px-1">
    {#each tabs as tab (tab.value)}
      <Tabs.Tab
        class="flex h-8 items-center justify-center border-0 px-3 text-sm font-normal whitespace-nowrap text-gray-600 outline-hidden select-none before:inset-x-0 before:inset-y-1 before:rounded-xs before:-outline-offset-1 before:outline-gray-950 hover:text-gray-900 focus-visible:relative focus-visible:before:absolute focus-visible:before:outline-2 data-active:text-gray-900"
        value={tab.value}>{tab.label}</Tabs.Tab
      >
    {/each}
    <Tabs.Indicator
      class="absolute top-1/2 left-0 z-[-1] h-6 w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-1/2 rounded-xs bg-gray-100 transition-[translate,width] duration-150 ease-in-out"
    />
  </Tabs.List>
  {#each tabs as tab (tab.value)}
    <Tabs.Panel class="w-0 min-w-full p-4 text-sm/5.75 text-gray-700" value={tab.value}>
      {tab.body}
    </Tabs.Panel>
  {/each}
</Tabs.Root>

Anatomy

<script>
  import { Tabs } from '@shardsui/svelte/tabs'
</script>

<Tabs.Root>
  <Tabs.List>
    <Tabs.Tab value="..." />
    <Tabs.Indicator />
  </Tabs.List>

  <Tabs.Panel value="..." />
</Tabs.Root>

Every Tabs.Tab needs a value, and the Tabs.Panel it controls repeats it.

Examples

Animated panels

Animate panels as they activate using the data-starting-style and data-ending-style attributes. The data-activation-direction attribute indicates which direction the newly active tab is relative to the previously active one, letting panels slide in from the correct side.

Workspace stats and activity.
<script lang="ts">
  import { Tabs } from '@shardsui/svelte/tabs'

  const tabs = [
    { value: 'overview', label: 'Overview', body: 'Workspace stats and activity.' },
    { value: 'projects', label: 'Projects', body: 'Milestones and deadlines.' },
    { value: 'account', label: 'Account', body: 'Profile and preferences.' }
  ]
</script>

<Tabs.Root class="w-fit rounded-md border border-gray-200" value="overview">
  <Tabs.List class="relative z-0 flex gap-1 border-b border-gray-200 px-1">
    {#each tabs as tab (tab.value)}
      <Tabs.Tab
        class="flex h-8 items-center justify-center border-0 px-3 text-sm font-normal whitespace-nowrap text-gray-600 outline-hidden select-none before:inset-x-0 before:inset-y-1 before:rounded-xs before:-outline-offset-1 before:outline-gray-950 hover:text-gray-900 focus-visible:relative focus-visible:before:absolute focus-visible:before:outline-2 data-active:text-gray-900"
        value={tab.value}>{tab.label}</Tabs.Tab
      >
    {/each}
    <Tabs.Indicator
      class="absolute top-1/2 left-0 z-[-1] h-6 w-(--active-tab-width) translate-x-(--active-tab-left) -translate-y-1/2 rounded-xs bg-gray-100 transition-[translate,width] duration-150 ease-in-out"
    />
  </Tabs.List>
  <div class="grid grid-cols-1 overflow-hidden">
    {#each tabs as tab (tab.value)}
      <Tabs.Panel
        class="col-start-1 row-start-1 w-0 min-w-full p-4 text-sm/5.75 text-gray-700 outline-hidden [transition:opacity_175ms_ease,translate_350ms_cubic-bezier(0.22,1,0.36,1)] data-ending-style:opacity-0 data-starting-style:opacity-0 motion-safe:data-ending-style:data-[activation-direction=left]:translate-x-1/2 motion-safe:data-starting-style:data-[activation-direction=left]:-translate-x-1/2 motion-safe:data-ending-style:data-[activation-direction=right]:-translate-x-1/2 motion-safe:data-starting-style:data-[activation-direction=right]:translate-x-1/2"
        value={tab.value}
      >
        {tab.body}
      </Tabs.Panel>
    {/each}
  </div>
</Tabs.Root>

When a tab navigates to a URL instead of toggling a panel, render it as an anchor: set as="a" on <Tabs.Tab>. The href is forwarded to the element, and the tab keeps its role="tab" and its place in the list's keyboard navigation.

<Tabs.Root>
  <Tabs.List>
    <Tabs.Tab as="a" href="/overview" value="overview">Overview</Tabs.Tab>
  </Tabs.List>
</Tabs.Root>

API reference

Root

Groups the tabs and the corresponding panels. Renders a <div> element.

PropTypeDefault
AttributeDescription
data-orientationIndicates the orientation of the tabs.
data-activation-directionDirection of the last activation: 'left' | 'right' | 'up' | 'down' | 'none'.

List

Groups the individual tab buttons into one tab stop; arrow keys move between them. Renders a <div> element with role="tablist".

PropTypeDefault
AttributeDescription
data-orientationIndicates the orientation of the tabs.
data-activation-directionDirection of the last activation: 'left' | 'right' | 'up' | 'down' | 'none'.

Tab

An individual interactive tab button that toggles the corresponding panel. Renders a <button> element.

PropTypeDefault
AttributeDescription
data-activePresent when the tab is active.
data-disabledPresent when the tab is disabled.
data-orientationIndicates the orientation of the tabs.
data-activation-directionDirection of the last activation: 'left' | 'right' | 'up' | 'down' | 'none'.

Indicator

A visual indicator that can be styled to match the position of the currently active tab. Place it inside <Tabs.List> — it is measured against the list — and position it with the CSS variables below. Renders a <span> element; nothing renders while the active value is null.

PropTypeDefault
AttributeDescription
data-orientationIndicates the orientation of the tabs.
data-activation-directionDirection of the last activation: 'left' | 'right' | 'up' | 'down' | 'none'.
CSS VariableDescription
--active-tab-leftDistance from the list's left edge.
--active-tab-rightDistance from the list's right edge.
--active-tab-topDistance from the list's top edge.
--active-tab-bottomDistance from the list's bottom edge.
--active-tab-widthActive tab width.
--active-tab-heightActive tab height.

Panel

A panel displayed when the corresponding tab is active. Renders a <div> element.

PropTypeDefault
AttributeDescription
data-hiddenPresent when the panel is inactive but kept mounted.
data-starting-stylePresent when the panel is animating in.
data-ending-stylePresent when the panel is animating out.
data-orientationIndicates the orientation of the tabs.
data-activation-directionDirection of the last activation: 'left' | 'right' | 'up' | 'down' | 'none'.

Additional types

TabsValue

The value identifying a tab, shared by Tabs.Root's value, Tabs.Tab's value and the Tabs.Panel it controls.

type TabsValue = string | number | null

TabsOrientation

The orientation of Tabs.Root, which decides the arrow keys that move between tabs.

type TabsOrientation = 'horizontal' | 'vertical'

TabsActivationDirection

Which way the active tab moved, handed to the children snippet as activationDirection and mirrored on data-activation-direction.

type TabsActivationDirection = 'left' | 'right' | 'up' | 'down' | 'none'