Components

Tabs

A set of layered sections of content—known as tab panels—that are displayed one at a time.

Account

Make changes to your account here. Click save when you're done.

Source Code

This consists of 3 components:

  • Tabs - The parent component that holds the tabs and tab content.
  • Tab - The tab component that holds the tab title.
  • TabContent - The tab content component that holds the tab content.

Copy the following code into your project:

Tabs

<template>
  <HTabGroup v-bind="($attrs, $props)" v-slot="{ selectedIndex }" @change="emits('change', $event)">
    <HTabList>
      <slot name="tab" :selectedIndex="selectedIndex"></slot>
    </HTabList>
    <HTabPanels v-slot="{ selectedIndex }">
      <slot name="content" :selectedIndex="selectedIndex"></slot>
    </HTabPanels>
  </HTabGroup>
</template>

<script setup lang="ts">
  const props = withDefaults(
    defineProps<{
      /**
       * The element to render as.
       * @default div
       */
      as?: string;
      /**
       * The default selected index
       * @default 0
       */
      defaultIndex?: number;
      /**
       * The selected index if using as controlled component
       * @default null
       */
      selectedIndex?: number;
      /**
       * Whether the tablist should be vertical
       * @default false
       */
      vertical?: boolean;
      /**
       * Whether the tabpanel should be manually viewed when cycling through the tabs with keyboard.
       * Users would have to press Enter or Space to vie data if this is set to true
       * @default false
       */
      manual?: boolean;
    }>(),
    {
      as: "div",
      defaultIndex: 0,
      vertical: false,
      manual: false,
    }
  );
  const emits = defineEmits<{
    (event: "change", index: number): void;
  }>();
</script>

Tab

<template>
  <HTab
    v-bind="($attrs, $props)"
    :disabled="disabled"
    :class="cn(variants({ type, class: props.class }))"
    v-slot="{ selected }"
  >
    <slot :selected="selected" />
  </HTab>
</template>
<script setup lang="ts">
  import { cva, type VariantProps } from "class-variance-authority";

  const variants = cva(
    "inline-flex items-center justify-center gap-2 text-sm focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed",
    {
      variants: {
        type: {
          underline:
            "z-[1] whitespace-nowrap border-b-2 border-b-transparent px-4 py-3 text-foreground/70 transition focus:outline-none data-[headlessui-state=selected]:border-primary data-[headlessui-state=selected]:text-primary hover:text-primary hover:bg-muted",
          fill: "z-[1] whitespace-nowrap rounded-md px-4 py-2 text-muted-foreground transition hover:text-foreground data-[headlessui-state=selected]:bg-background data-[headlessui-state=selected]:text-foreground data-[headlessui-state=selected]:shadow-sm font-medium",
        },
      },
      defaultVariants: {
        type: "underline",
      },
    }
  );
  type Props = VariantProps<typeof variants>;

  const props = withDefaults(
    defineProps<{
      /**
       * The component to render as.
       * @default button
       */
      as?: string;
      /**
       * Whether the tab is disabled.
       * @default false
       */
      disabled?: boolean;
      /**
       * The type of tab to render.
       * @default underline
       */
      type?: Props["type"];
      /**
       * The class to apply to the tab.
       */
      class?: any;
    }>(),
    { as: "button", disabled: false }
  );
</script>

TabContent

<template>
  <HTabPanel
    v-bind="($attrs, $props)"
    class="rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-border focus-visible:ring-offset-2 focus-visible:ring-offset-background"
    v-slot="{ selected }"
  >
    <slot :selected="selected"></slot>
  </HTabPanel>
</template>

<script setup lang="ts">
  withDefaults(
    defineProps<{
      /**
       * The component to render as.
       * @default div
       */
      as?: string;
      /**
       * Whether the element should ignore the selected index.
       * @default false
       */
      static?: boolean;
      /**
       * Whether the tabpanel should be unmounted when not visible.
       * @default true
       */
      unmount?: boolean;
    }>(),
    {
      as: "div",
      static: false,
      unmount: true,
    }
  );
</script>

Usage

Types

As you can see in the preview above, the type being used was the fill type. I aslo created an underline type. You can create your own types by adding them to the type object in the Tab component.

Account

Make changes to your account here. Click save when you're done.