Tabs

Switch between content views. Underline (default), pills, vertical, and justified variants.

Overview

The Tabs component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.

Why this exists

Tabs organize related content within a page, reducing scroll and cognitive load. They are the standard pattern for switching between related views.

Mental model

Tabs are mutually exclusive content panels within the same context. Selecting a tab replaces the content area without navigating away.

When to use

• Profile sections (Overview, Settings, Activity) • Data views (List, Kanban, Calendar) • Settings categories (General, Security, Billing) • Product detail sections (Description, Reviews, Specs)

When not to use

• Do not use tabs for navigation between pages — use links • Avoid for unrelated content — tabs imply relationship • Do not use more than 7 tabs — it becomes a dropdown

Basic example

The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.

Variants

Each variant communicates a different meaning. Choose based on priority, not aesthetics.

  • underline — underline style variant.
  • pills — pills style variant.
  • vertical — vertical style variant.
  • justified — justified style variant.

Sizes

Size should match the importance and context of the component.

  • sm — sm size.
  • md — md size.
  • lg — lg size.

States

This component reacts seamlessly to the following interaction and data states:

  • default — Resting appearance before interaction.
  • hover — Pointer is over the element — visual feedback via color or shadow.
  • focus-visible — focus-visible state.
  • active — Currently selected or in use.
  • disabled — Non-interactive — grayed out, cursor not-allowed.
  • inactive — Not selected.

Anatomy

• Tab list — container for tab buttons • Tab — each selectable item • Active indicator — underline, pill, or highlight • Tab panel — content area for each tab

Usage guidelines

  • Use underline for page-level tabs, pill for settings.
  • Always have one tab active.
  • Keep tab labels under 2 words.

Do and Don't

Do

  • Always have one tab active.
  • Keep labels short.
  • Use underline or pill style consistently.

Don't

  • Do not use for page navigation.
  • Do not leave all tabs unselected.
  • Do not exceed 7 tabs.

Accessibility

Tab list has role="tablist". Tabs have role="tab" with aria-selected. Panels have role="tabpanel". Arrow keys navigate between tabs.

Role:tablist

ARIA Attributes

  • aria-selected
  • aria-controls
  • aria-labelledby
  • aria-orientation

Keyboard Navigation

  • Tab: Moves focus into the tablist
  • Arrow Left / Right: Switch tabs (horizontal)
  • Arrow Up / Down: Switch tabs (vertical variant)
  • Home / End: Jump to first / last tab
  • Enter / Space: Activate the focused tab

Customization

Active indicator color follows --primary.

Compose with React cva()

Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.

import { cva, cn, type VariantProps } from '@byteion/react';

const myTabs = cva('base classes…', {
 variants: {
  accent: { soft:'border-primary-200', loud:'border-primary-500' },
 },
 defaultVariants: { accent:'soft' },
});

type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myTabs>;

Compose with Vue cva()

Use the exportedcva() +cn() utilities inside standard Vue setup blocks.

<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';

interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();

const myTabs = cva('base classes…', {
  variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});

const cls = computed(() => myTabs({ accent: props.accent, class: attrs.class as string | undefined }));
</script>

Runtime events

Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.

HTML / JS events

  • bi:tabs:hide
  • bi:tabs:show

React callbacks

  • onValueChange(value: string) => void

Vue emits

  • update:modelValue[v: string]

Remote / AJAX

This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.

API Reference

Requires data-bi-component="tabs" for JS behavior.

HTML Data Attributes

  • data-bi-tabs
  • data-bi-tabs-toggle
  • data-bi-tabs-panel

React Props · TabsProps

Extracted directly fromreact/src/components/Tabs.tsx.

export interface TabsProps {
  /** Currently active tab value. Pass `value` + `onValueChange` for controlled. */
  value?: string;
  defaultValue?: string;
  onValueChange?: (value: string) => void;
  variant?: 'underline' | 'pills' | 'vertical' | 'justified';
  tone?: 'neutral' | 'primary';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
  children?: ReactNode;
}

Vue Props · TabsProps

Extracted directly fromvue/src/components/Tabs.vue.

export interface TabsProps {
  modelValue?: string;       // controlled (v-model)
  defaultValue?: string;     // uncontrolled initial
  variant?: 'underline' | 'pills' | 'vertical' | 'justified';
  tone?: 'neutral' | 'primary';
  size?: 'sm' | 'md' | 'lg';
}

Developer tips

• Best practice: changing tabs should not lose form state — use client-side tab switching, not page navigation. • Edge case: if content takes time to load, show a skeleton in the inactive panel.

Common mistakes

  • Using tabs for global page navigation — use Navbar or Sidebar instead.
  • Too many tabs (7+) — users stop scanning after 5–7 items.
  • Unrelated tab content that belongs on separate pages.
  • No active tab — always ensure one tab is selected.
  • Losing unsaved form state when switching tabs — store state externally.
  • Disabled tab without explanation — disable with disabled and add a tooltip or note.
  • Missing aria-controls / aria-labelledby relationships in custom HTML.
  • Tab labels wrapping awkwardly — keep labels short and ensure adequate width.
  • Using raw colors (border-primary-600, bg-primary-50) instead of semantic tokens.
  • Documenting a "justified" variant that is not supported by React or Vue.

Production usage tips

  • Test keyboard navigation — ArrowLeft/Right (horizontal), ArrowUp/Down (vertical), Home/End.
  • Test active tab state in both controlled (value/v-model) and uncontrolled (defaultValue) modes.
  • Test form state preservation when switching tabs — values should not reset unless intended.
  • Test mobile overflow — horizontal tabs should scroll or collapse to an accordion on small screens.
  • Test URL sync if tabs represent shareable views — update query params and restore on page load.
  • Test dark mode and branded themes — token-based colors should adapt automatically.
  • Verify variant support — only underline, pills, and vertical are available in React and Vue. Justified is not implemented.

Premium examples

Advanced patterns available in Byteion Pro:

• Tabs with notification dots — colored dot indicator on tabs with unread content. • Tabs with badges/counts — numeric count inside each tab (inbox, orders, etc.). • Vertical settings tabs — persistent left tab list with content panel for admin settings. • Route-synced tabs — tab value stored in URL query params for shareable links. • Lazy-loaded tab panels — panels load content only when first selected. • Draggable/reorderable tabs — users can reorder tabs by drag and drop. • Responsive tabs-to-accordion pattern — horizontal tabs collapse to an Accordion on mobile. • Dashboard view-mode tabs — tabs to switch between List / Kanban / Calendar dashboard views.

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.

Explore Pro Components