Tabs
Organize related content into separate panels that users switch between without leaving the page. Tabs are ideal for settings screens, detail views, and any layout where showing everything at once would feel overwhelming. The active tab is controlled via v-model, so you can read or set it programmatically.
import { CoarTabGroup, CoarTab } from '@cocoar/vue-ui';Basic Tabs
Define your tabs inside a CoarTabGroup and render the matching panel content with v-if on the active tab ID. Users can click tabs or use the keyboard to switch between them.
Overview
This is the overview content. It provides a high-level introduction to the component.
<template>
<div>
<CoarTabGroup v-model="activeTab">
<CoarTab id="overview">Overview</CoarTab>
<CoarTab id="features">Features</CoarTab>
<CoarTab id="api">API</CoarTab>
<CoarTab id="examples">Examples</CoarTab>
</CoarTabGroup>
<div style="padding: 16px; border: 1px solid var(--coar-border-neutral-secondary); border-top: none; border-radius: 0 0 8px 8px; min-height: 100px;">
<div v-if="activeTab === 'overview'">
<h4 style="margin: 0 0 8px;">Overview</h4>
<p style="font-size: 13px;">This is the overview content. It provides a high-level introduction to the component.</p>
</div>
<div v-else-if="activeTab === 'features'">
<h4 style="margin: 0 0 8px;">Features</h4>
<ul style="padding-left: 16px; display: flex; flex-direction: column; gap: 4px; font-size: 13px;">
<li>Keyboard navigation (Arrow keys, Home, End)</li>
<li>ARIA tab panel pattern</li>
<li>Controlled and uncontrolled modes</li>
<li>Custom content in each panel</li>
</ul>
</div>
<div v-else-if="activeTab === 'api'">
<h4 style="margin: 0 0 8px;">API</h4>
<p style="font-size: 13px;">Use <code>v-model</code> on <code>CoarTabGroup</code> to control the active tab programmatically.</p>
</div>
<div v-else-if="activeTab === 'examples'">
<h4 style="margin: 0 0 8px;">Examples</h4>
<p style="font-size: 13px;">See the demos below for more usage patterns.</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { CoarTabGroup, CoarTab } from '@cocoar/vue-ui';
const activeTab = ref('overview');
</script>
Settings Pattern
Tabs shine in settings pages where each category has its own form section. This pattern keeps the page compact and scannable while giving each section room to breathe.
General Settings
<template>
<div>
<CoarTabGroup v-model="settingsTab">
<CoarTab id="general">General</CoarTab>
<CoarTab id="security">Security</CoarTab>
<CoarTab id="notifications">Notifications</CoarTab>
<CoarTab id="billing">Billing</CoarTab>
</CoarTabGroup>
<div style="padding: 16px; border: 1px solid var(--coar-border-neutral-secondary); border-top: none; border-radius: 0 0 8px 8px; min-height: 100px;">
<div v-if="settingsTab === 'general'">
<h4 style="margin: 0 0 8px;">General Settings</h4>
<div style="display: flex; flex-direction: column; gap: 8px;">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 16px;">
<span style="font-size: 13px;">Display name</span>
<input type="text" value="John Doe" style="padding: 4px 8px; border: 1px solid var(--coar-border-neutral-secondary); border-radius: 6px; background: var(--coar-background-neutral-primary); color: var(--coar-text-neutral-primary); font-size: 13px; min-width: 160px;" />
</div>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 16px;">
<span style="font-size: 13px;">Email</span>
<input type="email" value="john@example.com" style="padding: 4px 8px; border: 1px solid var(--coar-border-neutral-secondary); border-radius: 6px; background: var(--coar-background-neutral-primary); color: var(--coar-text-neutral-primary); font-size: 13px; min-width: 160px;" />
</div>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 16px;">
<span style="font-size: 13px;">Language</span>
<select style="padding: 4px 8px; border: 1px solid var(--coar-border-neutral-secondary); border-radius: 6px; background: var(--coar-background-neutral-primary); color: var(--coar-text-neutral-primary); font-size: 13px; min-width: 160px;">
<option>English</option>
<option>German</option>
</select>
</div>
</div>
</div>
<div v-else-if="settingsTab === 'security'">
<h4 style="margin: 0 0 8px;">Security Settings</h4>
<p style="font-size: 13px; color: var(--coar-text-neutral-secondary);">Manage your password and two-factor authentication settings.</p>
</div>
<div v-else-if="settingsTab === 'notifications'">
<h4 style="margin: 0 0 8px;">Notification Preferences</h4>
<p style="font-size: 13px; color: var(--coar-text-neutral-secondary);">Choose how you want to be notified about account activity.</p>
</div>
<div v-else-if="settingsTab === 'billing'">
<h4 style="margin: 0 0 8px;">Billing & Plans</h4>
<p style="font-size: 13px; color: var(--coar-text-neutral-secondary);">Manage your subscription and payment methods.</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { CoarTabGroup, CoarTab } from '@cocoar/vue-ui';
const settingsTab = ref('general');
</script>
Disabled Tabs
Disable individual tabs to prevent access to sections that are not yet available, require a higher permission level, or depend on completing a previous step first.
<template>
<CoarTabGroup>
<CoarTab id="active1">Active</CoarTab>
<CoarTab id="disabled1" :disabled="true">Disabled</CoarTab>
<CoarTab id="active2">Active</CoarTab>
<CoarTab id="disabled2" :disabled="true">Disabled</CoarTab>
</CoarTabGroup>
</template>
<script setup lang="ts">
import { CoarTabGroup, CoarTab } from '@cocoar/vue-ui';
</script>
Accessibility
Keyboard Navigation
| Key | Action |
|---|---|
Left Arrow / Right Arrow | Move between tabs |
Home | First tab |
End | Last tab |
Tab | Move to panel content |
API
CoarTabGroup Props
| Prop | Type | Default | Description |
|---|---|---|---|
v-model | string | first tab id | ID of the active tab |
CoarTab Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique tab identifier (required) |
disabled | boolean | false | Disable this tab |
loadingStrategy | 'lazy' | 'eager' | 'lazy' | lazy: content only rendered when active; eager: always in DOM |