Tabs

Easily create accessible, fully customizable tab interfaces, with robust focus management and keyboard navigation support.

Open in separate tab

Example

<script lang="ts">
  import { createTabs } from 'svelte-headlessui'

  interface Post {
    id: number
    title: string
    date: string
    commentCount: number
    shareCount: number
  }

  // data driven, but could be static html
  const categories: Record<string, Post[]> = {
    Recent: [
      {
        id: 1,
        title: 'Does drinking coffee make you smarter?',
        date: '5h ago',
        commentCount: 5,
        shareCount: 2,
      },
      {
        id: 2,
        title: "So you've bought coffee... now what?",
        date: '2h ago',
        commentCount: 3,
        shareCount: 2,
      },
    ],
    Popular: [
      {
        id: 1,
        title: 'Is tech making coffee better or worse?',
        date: 'Jan 7',
        commentCount: 29,
        shareCount: 16,
      },
      {
        id: 2,
        title: 'The most innovative things happening in coffee',
        date: 'Mar 19',
        commentCount: 24,
        shareCount: 12,
      },
    ],
    Trending: [
      {
        id: 1,
        title: 'Ask Me Anything: 10 answers to your questions about coffee',
        date: '2d ago',
        commentCount: 9,
        shareCount: 5,
      },
      {
        id: 2,
        title: "The worst advice we've ever heard about coffee",
        date: '4d ago',
        commentCount: 1,
        shareCount: 2,
      },
    ],
  }

  const keys = Object.keys(categories)
  const tabs = createTabs({ selected: 'Recent' })
</script>

<div class="flex w-full flex-col items-center justify-center">
  <div class="w-full max-w-md px-2 py-16 sm:px-0">
    <div use:tabs.list class="flex space-x-1 rounded-xl bg-blue-900/20 p-1">
      {#each keys as value}
        {@const active = $tabs.active === value}
        {@const selected = $tabs.selected === value}
        <button
          use:tabs.tab={{ value }}
          class="w-full rounded-lg py-2.5 text-sm font-medium leading-5 ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2 {selected
            ? 'bg-white text-blue-700 shadow'
            : active
              ? 'bg-white/[0.12] text-white'
              : 'text-blue-100 hover:bg-white/[0.12] hover:text-white'}">{value}</button
        >
      {/each}
    </div>
    <div class="mt-2">
      {#each keys as value}
        {@const selected = $tabs.selected === value}
        <div
          use:tabs.panel
          class="rounded-xl bg-white p-3 ring-white ring-opacity-60 ring-offset-2 ring-offset-blue-400 focus:outline-none focus:ring-2 {selected
            ? 'block'
            : 'hidden'}"
        >
          {#if selected}
            <ul>
              {#each categories[$tabs.selected] as post}
                <li class="relative rounded-md p-3 hover:bg-gray-100">
                  <h3 class="text-sm font-medium leading-5">{post.title}</h3>
                  <ul class="mt-1 flex space-x-1 text-xs font-normal leading-4 text-gray-500">
                    <li>{post.date}</li>
                    <li>&middot;</li>
                    <li>{post.commentCount} comments</li>
                    <li>&middot;</li>
                    <li>{post.shareCount} shares</li>
                  </ul>
                  <a
                    href="#"
                    class="absolute inset-0 rounded-md ring-blue-400 focus:z-10 focus:outline-none focus:ring-2"
                  ></a>
                </li>
              {/each}
            </ul>
          {/if}
        </div>
      {/each}
    </div>
  </div>
</div>