Tabs
Swaps between panels of content that share one region of the page.
Tabs shows one panel at a time in a shared region, with a list of tabs to switch between them. Useful when several views belong in the same place and the user decides which one to see.
Three panels sharing one region, and one of them at a time.
<template>
<Tabs.Root v-model="tab" class="flex w-72 flex-col gap-4">
<Tabs.List :class="list" aria-label="Sections">
<Tabs.Tab
v-for="entry in entries"
:key="entry.value"
:class="trigger"
:disabled="entry.disabled"
:value="entry.value"
>
{{ entry.label }}
</Tabs.Tab>
<Tabs.Indicator :class="indicator" />
</Tabs.List>
<Tabs.Content
v-for="entry in entries"
:key="entry.value"
:class="content"
:value="entry.value"
>
{{ entry.body }}
</Tabs.Content>
</Tabs.Root>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Tabs } from '@maas/mirror/vue/Tabs'
const tab = ref('overview')
const entries = [
{
value: 'overview',
label: 'Overview',
body: 'Three panels sharing one region, and one of them at a time.',
},
{
value: 'tokens',
label: 'Tokens',
body: 'Arrow keys move between the tabs and activate as they go.',
},
{ value: 'archive', label: 'Archive', body: 'Nothing here.', disabled: true },
]
const list =
'rounded-component-sm border-surface relative isolate flex gap-1 border p-1'
const trigger = [
'rounded-component-compact-md type-component-sm text-surface-muted relative z-10',
'flex-1 px-3 py-1.5 transition-colors duration-200 ease-in-out',
'border-2 border-[transparent] outline outline-4 outline-transparent',
'focus-visible:focus-ring data-[active=true]:text-surface',
'enabled:not-data-[active=true]:hover:bg-primary-subtle',
'enabled:not-data-[active=true]:hover:text-surface',
'enabled:not-data-[active=true]:active:bg-primary-subtle-hover',
'data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-40',
].join(' ')
const indicator = [
'rounded-component-compact-md bg-primary-muted',
'left-0! translate-x-[var(--mirror-tabs-indicator-left)]',
'transition-transform duration-200 ease-out',
].join(' ')
const content = 'type-surface-body-md text-surface-muted'
</script>
Usage guidelines
- Keep every
Tabs.Tabinside aTabs.List, since the list is the roving-focus group and a tab outside it has nothing to rove within.must - If a content part is expensive to render, set
options.activateOnFocustofalseso the arrow keys move focus without activating and Enter or Space commits.should - Characters outside
A-Z,a-z,0-9,_and-are folded to-when the IDs are derived, so give a tab its ownelementIdwhere two values differ only in punctuation.must