> ## Documentation Index
> Fetch the complete documentation index at: https://www.beeq.design/llms.txt
> Use this file to discover all available pages before exploring further.

# Tab

> Tabs organize related content into separate panels under a single view, letting users switch between sections without navigating away from the page.

export const CodeLivePreview = ({code, children, height, removePadding = false, mode = 'shadow'}) => {
  const previewRef = useRef(null);
  const BEEQ_ESM_URL = 'https://esm.sh/@beeq/core/dist/beeq/beeq.esm.js';
  const BEEQ_CSS_URL = 'https://esm.sh/@beeq/core/dist/beeq/beeq.css';
  const BEEQ_ICONS_URL = 'https://esm.sh/@beeq/core/dist/beeq/svg';
  const ensureParentBeeqRuntime = () => {
    if (document.querySelector('script[data-beeq-parent-runtime]')) return;
    const script = document.createElement('script');
    script.type = 'module';
    script.src = BEEQ_ESM_URL;
    script.dataset.beeqParentRuntime = BEEQ_ICONS_URL;
    document.head.appendChild(script);
  };
  const executeSnippetScripts = (root, runtimeWindow, previewRootArg, skipAttr) => {
    root.querySelectorAll('script').forEach(oldScript => {
      if (skipAttr && oldScript.hasAttribute(skipAttr)) return;
      if (oldScript.src) {
        const newScript = runtimeWindow.document.createElement('script');
        newScript.src = oldScript.src;
        oldScript.replaceWith(newScript);
        return;
      }
      runtimeWindow.Function('previewRoot', oldScript.textContent || '')(previewRootArg);
      oldScript.remove();
    });
  };
  const syncIframeMode = iframeDoc => {
    const root = document.documentElement;
    const explicitMode = root.getAttribute('bq-mode');
    const isDark = root.classList.contains('dark');
    const resolvedMode = explicitMode || (isDark ? 'dark' : 'light');
    iframeDoc.documentElement.setAttribute('bq-mode', resolvedMode);
    iframeDoc.body.setAttribute('bq-mode', resolvedMode);
  };
  useEffect(() => {
    if (mode === 'shadow') ensureParentBeeqRuntime();
  }, [mode]);
  useEffect(() => {
    const container = previewRef.current;
    if (!container) return;
    if (removePadding) container.style.padding = '0';
    container.innerHTML = '';
    if (mode === 'iframe') {
      const iframe = document.createElement('iframe');
      iframe.className = 'preview-iframe';
      iframe.style.width = '100%';
      iframe.style.border = '0';
      iframe.style.display = 'block';
      iframe.style.minHeight = height ?? '0px';
      iframe.setAttribute('title', 'Live code preview');
      iframe.setAttribute('loading', 'lazy');
      iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
      container.appendChild(iframe);
      const iframeDoc = iframe.contentDocument;
      const iframeWin = iframe.contentWindow;
      if (!iframeDoc || !iframeWin) return;
      iframeDoc.open();
      iframeDoc.write(['<!doctype html>', '<html>', '<head>', '  <meta charset="utf-8" />', '  <meta name="viewport" content="width=device-width, initial-scale=1" />', '  <style>html,body{margin:0;padding:0;min-height:100%;}</style>', `  <link rel="stylesheet" href="${BEEQ_CSS_URL}" />`, `  <script type="module" src="${BEEQ_ESM_URL}" data-beeq-iframe-runtime="${BEEQ_ICONS_URL}"></script>`, '</head>', '<body>', code, '</body>', '</html>'].join('\n'));
      iframeDoc.close();
      syncIframeMode(iframeDoc);
      const modeObserver = new MutationObserver(() => syncIframeMode(iframeDoc));
      modeObserver.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ['class', 'bq-mode']
      });
      executeSnippetScripts(iframeDoc, iframeWin, iframeDoc, 'data-beeq-iframe-runtime');
      return () => modeObserver.disconnect();
    }
    const shadowRoot = container.shadowRoot ?? container.attachShadow({
      mode: 'open'
    });
    if (!shadowRoot.adoptedStyleSheets.length) {
      const fixSheet = new CSSStyleSheet();
      fixSheet.replaceSync(`
        bq-dropdown::part(panel),
        bq-panel::part(panel),
        bq-select::part(panel),
        bq-date-picker::part(panel) { z-index: 9999; }
        bq-tooltip::part(panel) { position: absolute; }
      `);
      shadowRoot.adoptedStyleSheets = [fixSheet];
    }
    shadowRoot.innerHTML = [`<link rel="stylesheet" href="${BEEQ_CSS_URL}">`, code].join('');
    executeSnippetScripts(shadowRoot, window, shadowRoot);
    return undefined;
  }, [code, mode, height]);
  return <div className="code-live-preview not-prose">
      {}
      <div className="preview" ref={previewRef} style={{
    minHeight: height
  }} />

      {}
      {children && <div className="code">{children}</div>}
    </div>;
};

export const CardTile = ({title, href, imageLightSrc, imageDarkSrc, children, enableImgZoom}) => {
  if (!href) {
    return <div className="card-tile block font-normal group relative my-2 ring-2 ring-transparent rounded-2xl overflow-hidden w-full">
        {imageLightSrc && <img className="w-full m-0 block dark:hidden" src={imageLightSrc} alt={title} {...enableImgZoom ? {
      zoom: true
    } : {
      noZoom: true
    }} />}
        {imageDarkSrc && <img className="w-full m-0 hidden dark:block" src={imageDarkSrc} alt={title} {...enableImgZoom ? {
      zoom: true
    } : {
      noZoom: true
    }} />}
        {title && <h2 className="mt-4 mb-2 px-6 text-base font-semibold">{title}</h2>}
        <div className="px-6 pb-5">{children}</div>
      </div>;
  }
  return <a href={href} className="card-tile block font-normal group relative my-2 ring-2 ring-transparent rounded-2xl overflow-hidden w-full cursor-pointer">
      {imageLightSrc && <img className="w-full m-0 block dark:hidden" src={imageLightSrc} alt={title} noZoom />}
      {imageDarkSrc && <img className="w-full m-0 hidden dark:block" src={imageDarkSrc} alt={title} noZoom />}
      {title && <h2 className="mt-4 mb-2 px-6 text-base font-semibold">{title}</h2>}
      <div className="px-6 pb-5">{children}</div>
    </a>;
};

<Frame className="px-4 py-4" caption="Tab component overview">
  <img className="block dark:hidden" src="https://mintlify.s3.us-west-1.amazonaws.com/beeq/components/images/tab/tab-overview-light.svg" alt="BEEQ Tab component overview" />

  <img className="hidden dark:block" src="https://mintlify.s3.us-west-1.amazonaws.com/beeq/components/images/tab/tab-overview-dark.svg" alt="BEEQ Tab component overview" />
</Frame>

Tabs let users navigate between related sections of content within the same page or view. Each tab reveals one panel at a time, keeping the interface clean while making multiple content sections available without navigation.

<Note>
  `bq-tab` must always be placed inside a `bq-tab-group`. The group manages tab state, keyboard navigation, and propagates `size`, `orientation`, and `placement` to all child tabs automatically.
</Note>

## When to use

<CardGroup cols={2}>
  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="thumbs-up" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Use tabs when
    </span>

    * Content can be clearly organized into a small number of distinct, peer-level sections
    * Users need to compare or switch between sections without losing page context
    * Space is limited and displaying all sections at once would clutter the layout
    * Each section can stand independently without requiring the user to read others first
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="thumbs-down" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Do not use tabs when
    </span>

    * The number of tabs exceeds seven — prefer a sidebar navigation or dropdown instead
    * Content across sections is sequential and users must read them in order — prefer a stepper
    * Sections contain very different content types that do not relate to the same subject
    * The goal is to navigate to a new page or route — prefer anchor links or a navigation component
  </Card>
</CardGroup>

## Anatomy

<Frame className="px-4 py-4" caption="Tab component anatomy">
  <img className="block dark:hidden" src="https://mintlify.s3.us-west-1.amazonaws.com/beeq/components/images/tab/tab-anatomy-light.svg" alt="BEEQ Tab component anatomy" />

  <img className="hidden dark:block" src="https://mintlify.s3.us-west-1.amazonaws.com/beeq/components/images/tab/tab-anatomy-dark.svg" alt="BEEQ Tab component anatomy" />
</Frame>

The tab group renders a list of tab buttons inside a scrollable container. Each individual tab consists of an optional icon, a text label, and an active-state underline indicator.

| Part  | Element            | Description                                                                                                   |
| ----- | ------------------ | ------------------------------------------------------------------------------------------------------------- |
| **1** | Active tab         | The currently selected tab button, visually distinguished by brand color and the selected indicator           |
| **2** | Inactive tab       | A tab button that is not currently selected; clicking it activates it and deactivates the previous tab        |
| **3** | Selected indicator | The underline beneath the active tab that marks the current selection; exposed as the `underline` shadow part |
| **4** | Text label         | The visible label inside each tab button; placed in the default slot and exposed as the `text` shadow part    |

## Design guidelines

### Orientation

<CardGroup cols={2}>
  <CardTile title="Horizontal" imageLightSrc="/components/images/tab/tab-orientation-horizontal-light.svg" imageDarkSrc="/components/images/tab/tab-orientation-horizontal-dark.svg">
    The default orientation. Tabs are arranged in a row across the top of the content area. Best suited for most layouts.
  </CardTile>

  <CardTile title="Vertical" imageLightSrc="/components/images/tab/tab-orientation-vertical-light.svg" imageDarkSrc="/components/images/tab/tab-orientation-vertical-dark.svg">
    Tabs are stacked in a column to the left or right of the content area. Use when there are many tabs or when the layout is sidebar-based.
  </CardTile>
</CardGroup>

### Size

<Steps>
  <Step title="small">Compact tabs with reduced padding. Use in dense interfaces or data-heavy views where space is constrained.</Step>
  <Step title="medium">The default size. Appropriate for most applications and standard-density layouts.</Step>
  <Step title="large">Prominent tabs with increased padding. Use when tabs are a primary navigation element or accessibility requires larger touch targets.</Step>
</Steps>

<Note>
  Setting `size` on `bq-tab-group` propagates the value to all child `bq-tab` elements automatically. You do not need to set `size` on each tab individually.
</Note>

### Placement

Use `placement="start"` (default) to left-align horizontal tabs or top-align vertical tabs. Use `placement="end"` to right-align horizontal tabs or bottom-align vertical tabs.

## Usage

### Default

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
}
</style>
<bq-tab-group value="5">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
<bq-tab tab-id="4" disabled>Tab</bq-tab>
<bq-tab tab-id="5">Tab</bq-tab>
<bq-tab tab-id="6">Tab</bq-tab>
<bq-tab tab-id="7">Tab</bq-tab>
<bq-tab tab-id="8">Tab</bq-tab>
</bq-tab-group>
`}
>
  <CodeGroup>
    ```html HTML icon="html5" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-tab-group value="5">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
      <bq-tab tab-id="4" disabled>Tab</bq-tab>
      <bq-tab tab-id="5">Tab</bq-tab>
      <bq-tab tab-id="6">Tab</bq-tab>
      <bq-tab tab-id="7">Tab</bq-tab>
      <bq-tab tab-id="8">Tab</bq-tab>
    </bq-tab-group>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqTab, BqTabGroup } from "@beeq/react";

    export default () => (
      <BqTabGroup value="5">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
        <BqTab tabId="4" disabled>Tab</BqTab>
        <BqTab tabId="5">Tab</BqTab>
        <BqTab tabId="6">Tab</BqTab>
        <BqTab tabId="7">Tab</BqTab>
        <BqTab tabId="8">Tab</BqTab>
      </BqTabGroup>
    );
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqTab, BqTabGroup } from "@beeq/angular/standalone";

    @Component({
      selector: "app-root",
      standalone: true,
      imports: [BqTab, BqTabGroup],
      template: `
        <bq-tab-group value="5">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
          <bq-tab tab-id="4" disabled>Tab</bq-tab>
          <bq-tab tab-id="5">Tab</bq-tab>
          <bq-tab tab-id="6">Tab</bq-tab>
          <bq-tab tab-id="7">Tab</bq-tab>
          <bq-tab tab-id="8">Tab</bq-tab>
        </bq-tab-group>
      `,
    })
    export class AppComponent {}
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqTab, BqTabGroup } from "@beeq/vue";
    </script>

    <template>
      <BqTabGroup value="5">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
        <BqTab tabId="4" disabled>Tab</BqTab>
        <BqTab tabId="5">Tab</BqTab>
        <BqTab tabId="6">Tab</BqTab>
        <BqTab tabId="7">Tab</BqTab>
        <BqTab tabId="8">Tab</BqTab>
      </BqTabGroup>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

## Options

### With icons

Tabs support an optional icon displayed before the label via the `icon` slot.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
}
</style>
<bq-tab-group value="5" size="small">
<bq-tab tab-id="1">
  <bq-icon name="pulse" slot="icon"></bq-icon>
  Tab
</bq-tab>
<bq-tab tab-id="2">
  <bq-icon name="bell" slot="icon"></bq-icon>
  Tab
</bq-tab>
<bq-tab tab-id="3">
  <bq-icon name="airplane-in-flight" slot="icon"></bq-icon>
  Long Tab name
</bq-tab>
<bq-tab tab-id="4" disabled>
  <bq-icon name="airplane-tilt" slot="icon"></bq-icon>
  Tab
</bq-tab>
<bq-tab tab-id="5">
  <bq-icon name="align-right-simple" slot="icon"></bq-icon>
  Tab
</bq-tab>
<bq-tab tab-id="6">
  <bq-icon name="anchor" slot="icon"></bq-icon>
  Tab
</bq-tab>
<bq-tab tab-id="7">
  <bq-icon name="android-logo" slot="icon"></bq-icon>
  Tab
</bq-tab>
</bq-tab-group>
`}
>
  <CodeGroup>
    ```html HTML icon="html5" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-tab-group value="5" size="small">
      <bq-tab tab-id="1">
        <bq-icon name="pulse" slot="icon"></bq-icon>
        Tab
      </bq-tab>
      <bq-tab tab-id="2">
        <bq-icon name="bell" slot="icon"></bq-icon>
        Tab
      </bq-tab>
      <bq-tab tab-id="3">
        <bq-icon name="airplane-in-flight" slot="icon"></bq-icon>
        Long Tab name
      </bq-tab>
      <bq-tab tab-id="4" disabled>
        <bq-icon name="airplane-tilt" slot="icon"></bq-icon>
        Tab
      </bq-tab>
      <bq-tab tab-id="5">
        <bq-icon name="align-right-simple" slot="icon"></bq-icon>
        Tab
      </bq-tab>
      <bq-tab tab-id="6">
        <bq-icon name="anchor" slot="icon"></bq-icon>
        Tab
      </bq-tab>
      <bq-tab tab-id="7">
        <bq-icon name="android-logo" slot="icon"></bq-icon>
        Tab
      </bq-tab>
    </bq-tab-group>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqIcon, BqTab, BqTabGroup } from "@beeq/react";

    export default () => (
      <BqTabGroup value="5" size="small">
        <BqTab tabId="1">
          <BqIcon name="pulse" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="2">
          <BqIcon name="bell" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="3">
          <BqIcon name="airplane-in-flight" slot="icon" />
          Long Tab name
        </BqTab>
        <BqTab tabId="4" disabled>
          <BqIcon name="airplane-tilt" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="5">
          <BqIcon name="align-right-simple" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="6">
          <BqIcon name="anchor" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="7">
          <BqIcon name="android-logo" slot="icon" />
          Tab
        </BqTab>
      </BqTabGroup>
    );
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqIcon, BqTab, BqTabGroup } from "@beeq/angular/standalone";

    @Component({
      selector: "app-root",
      standalone: true,
      imports: [BqIcon, BqTab, BqTabGroup],
      template: `
        <bq-tab-group value="5" size="small">
          <bq-tab tab-id="1">
            <bq-icon name="pulse" slot="icon"></bq-icon>
            Tab
          </bq-tab>
          <bq-tab tab-id="2">
            <bq-icon name="bell" slot="icon"></bq-icon>
            Tab
          </bq-tab>
          <bq-tab tab-id="3">
            <bq-icon name="airplane-in-flight" slot="icon"></bq-icon>
            Long Tab name
          </bq-tab>
          <bq-tab tab-id="4" disabled>
            <bq-icon name="airplane-tilt" slot="icon"></bq-icon>
            Tab
          </bq-tab>
          <bq-tab tab-id="5">
            <bq-icon name="align-right-simple" slot="icon"></bq-icon>
            Tab
          </bq-tab>
          <bq-tab tab-id="6">
            <bq-icon name="anchor" slot="icon"></bq-icon>
            Tab
          </bq-tab>
          <bq-tab tab-id="7">
            <bq-icon name="android-logo" slot="icon"></bq-icon>
            Tab
          </bq-tab>
        </bq-tab-group>
      `,
    })
    export class AppComponent {}
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqIcon, BqTab, BqTabGroup } from "@beeq/vue";
    </script>

    <template>
      <BqTabGroup value="5" size="small">
        <BqTab tabId="1">
          <BqIcon name="pulse" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="2">
          <BqIcon name="bell" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="3">
          <BqIcon name="airplane-in-flight" slot="icon" />
          Long Tab name
        </BqTab>
        <BqTab tabId="4" disabled>
          <BqIcon name="airplane-tilt" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="5">
          <BqIcon name="align-right-simple" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="6">
          <BqIcon name="anchor" slot="icon" />
          Tab
        </BqTab>
        <BqTab tabId="7">
          <BqIcon name="android-logo" slot="icon" />
          Tab
        </BqTab>
      </BqTabGroup>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

### Sizes

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
  gap: var(--bq-spacing-m) !important;
}
</style>
<bq-tab-group size="small" value="1">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
</bq-tab-group>
<bq-tab-group size="medium" value="1">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
</bq-tab-group>
<bq-tab-group size="large" value="1">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
</bq-tab-group>
`}
>
  <CodeGroup>
    ```html HTML icon="html5" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <!-- Small -->
    <bq-tab-group size="small" value="1">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
    </bq-tab-group>

    <!-- Medium (default) -->
    <bq-tab-group size="medium" value="1">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
    </bq-tab-group>

    <!-- Large -->
    <bq-tab-group size="large" value="1">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
    </bq-tab-group>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqTab, BqTabGroup } from "@beeq/react";

    export default () => (
      <>
        {/* Small */}
        <BqTabGroup size="small" value="1">
          <BqTab tabId="1">Tab</BqTab>
          <BqTab tabId="2">Tab</BqTab>
          <BqTab tabId="3">Long Tab name</BqTab>
        </BqTabGroup>

        {/* Medium (default) */}
        <BqTabGroup size="medium" value="1">
          <BqTab tabId="1">Tab</BqTab>
          <BqTab tabId="2">Tab</BqTab>
          <BqTab tabId="3">Long Tab name</BqTab>
        </BqTabGroup>

        {/* Large */}
        <BqTabGroup size="large" value="1">
          <BqTab tabId="1">Tab</BqTab>
          <BqTab tabId="2">Tab</BqTab>
          <BqTab tabId="3">Long Tab name</BqTab>
        </BqTabGroup>
      </>
    );
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqTab, BqTabGroup } from "@beeq/angular/standalone";

    @Component({
      selector: "app-root",
      standalone: true,
      imports: [BqTab, BqTabGroup],
      template: `
        <bq-tab-group size="small" value="1">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
        </bq-tab-group>

        <bq-tab-group size="medium" value="1">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
        </bq-tab-group>

        <bq-tab-group size="large" value="1">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
        </bq-tab-group>
      `,
    })
    export class AppComponent {}
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqTab, BqTabGroup } from "@beeq/vue";
    </script>

    <template>
      <!-- Small -->
      <BqTabGroup size="small" value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
      </BqTabGroup>

      <!-- Medium (default) -->
      <BqTabGroup size="medium" value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
      </BqTabGroup>

      <!-- Large -->
      <BqTabGroup size="large" value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
      </BqTabGroup>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

### Vertical

Set `orientation="vertical"` on `bq-tab-group` to stack tabs in a column beside the content area.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  align-items: flex-start !important;
}
</style>
<bq-tab-group orientation="vertical" value="1">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
<bq-tab tab-id="4" disabled>Tab</bq-tab>
</bq-tab-group>
`}
>
  <CodeGroup>
    ```html HTML icon="html5" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-tab-group orientation="vertical" value="1">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
      <bq-tab tab-id="4" disabled>Tab</bq-tab>
    </bq-tab-group>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqTab, BqTabGroup } from "@beeq/react";

    export default () => (
      <BqTabGroup orientation="vertical" value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
        <BqTab tabId="4" disabled>Tab</BqTab>
      </BqTabGroup>
    );
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqTab, BqTabGroup } from "@beeq/angular/standalone";

    @Component({
      selector: "app-root",
      standalone: true,
      imports: [BqTab, BqTabGroup],
      template: `
        <bq-tab-group orientation="vertical" value="1">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
          <bq-tab tab-id="4" disabled>Tab</bq-tab>
        </bq-tab-group>
      `,
    })
    export class AppComponent {}
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqTab, BqTabGroup } from "@beeq/vue";
    </script>

    <template>
      <BqTabGroup orientation="vertical" value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
        <BqTab tabId="4" disabled>Tab</BqTab>
      </BqTabGroup>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

### No divider

Use `disable-divider` to hide the underline separator between the tab list and the content area.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
}
</style>
<bq-tab-group disable-divider value="1">
<bq-tab tab-id="1">Tab</bq-tab>
<bq-tab tab-id="2">Tab</bq-tab>
<bq-tab tab-id="3">Long Tab name</bq-tab>
</bq-tab-group>
`}
>
  <CodeGroup>
    ```html HTML icon="html5" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-tab-group disable-divider value="1">
      <bq-tab tab-id="1">Tab</bq-tab>
      <bq-tab tab-id="2">Tab</bq-tab>
      <bq-tab tab-id="3">Long Tab name</bq-tab>
    </bq-tab-group>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqTab, BqTabGroup } from "@beeq/react";

    export default () => (
      <BqTabGroup disableDivider value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
      </BqTabGroup>
    );
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqTab, BqTabGroup } from "@beeq/angular/standalone";

    @Component({
      selector: "app-root",
      standalone: true,
      imports: [BqTab, BqTabGroup],
      template: `
        <bq-tab-group disable-divider value="1">
          <bq-tab tab-id="1">Tab</bq-tab>
          <bq-tab tab-id="2">Tab</bq-tab>
          <bq-tab tab-id="3">Long Tab name</bq-tab>
        </bq-tab-group>
      `,
    })
    export class AppComponent {}
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqTab, BqTabGroup } from "@beeq/vue";
    </script>

    <template>
      <BqTabGroup disableDivider value="1">
        <BqTab tabId="1">Tab</BqTab>
        <BqTab tabId="2">Tab</BqTab>
        <BqTab tabId="3">Long Tab name</BqTab>
      </BqTabGroup>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

## Best practices

<CardGroup cols={2}>
  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Keep tab labels short and parallel — prefer noun phrases like "Overview", "Details", or "Settings" rather than full sentences.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Don't use tabs with only one or two items — a single visible section or a toggle is simpler and less visually heavy.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Always provide a `controls` attribute on each `bq-tab` pointing to the `id` of its associated panel element to meet ARIA requirements.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Don't mix tabs of different sizes in the same tab group. Set `size` on `bq-tab-group` and let it propagate uniformly.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Use consistent tab labels across your interface — if some tabs have icons, apply icons to all tabs in the same group.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Don't use tabs for actions or commands. Tabs are for navigation between content views, not for triggering operations like "Save" or "Delete".
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Choose the orientation that matches your layout — use horizontal tabs for top-of-content placement and vertical tabs for sidebar-style navigation.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Don't rely solely on the active-indicator underline for visual differentiation — ensure sufficient color contrast on the active tab label as well.
  </Card>
</CardGroup>

## Accessibility

`bq-tab-group` and `bq-tab` implement the [ARIA Tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/):

* The tab list container has `role="tablist"`.
* Each `bq-tab` renders as a `<button role="tab">` with `aria-selected` and `aria-disabled` set automatically.
* The `controls` prop maps to `aria-controls`, linking each tab to its panel. Always provide a matching `id` on the panel element and `role="tabpanel"` for correct screen reader announcements.
* Keyboard navigation is handled by the tab group:
  * `ArrowRight` / `ArrowDown` — move focus to the next enabled tab
  * `ArrowLeft` / `ArrowUp` — move focus to the previous enabled tab
* Focus management follows the roving `tabindex` pattern: only the active tab is in the tab order; other tabs are reachable via arrow keys.
* Disabled tabs have `aria-disabled="true"` and `disabled` set on the underlying button, removing them from interaction.

**Your responsibilities:**

* Provide a meaningful `controls` value on each `bq-tab` that matches the `id` of the corresponding panel.
* Add `role="tabpanel"` to each panel element.
* When using icons without a visible label, add a descriptive `aria-label` to the `bq-tab` so screen readers can identify the tab.

## API reference

### `bq-tab-group`

#### Properties

| Property         | Attribute         | Description                                   | Type                             | Default        |
| ---------------- | ----------------- | --------------------------------------------- | -------------------------------- | -------------- |
| `debounceTime`   | `debounce-time`   | Delay in milliseconds before `bqChange` fires | `number`                         | `0`            |
| `disableDivider` | `disable-divider` | If true, the underline divider is hidden      | `boolean`                        | `false`        |
| `orientation`    | `orientation`     | Layout direction of the tab list              | `"horizontal" \| "vertical"`     | `"horizontal"` |
| `placement`      | `placement`       | Alignment of tabs within the group            | `"start" \| "end"`               | `"start"`      |
| `size`           | `size`            | Size applied to all child tabs                | `"small" \| "medium" \| "large"` | `"medium"`     |
| `value`          | `value`           | The `tab-id` of the currently selected tab    | `string`                         | —              |

#### Events

| Event      | Description                         | Type                                                       |
| ---------- | ----------------------------------- | ---------------------------------------------------------- |
| `bqChange` | Fired when the selected tab changes | `CustomEvent<{ target: HTMLBqTabElement; value: string }>` |

#### Slots

| Slot        | Description                                   |
| ----------- | --------------------------------------------- |
| *(default)* | The `bq-tab` items to render inside the group |

#### Shadow parts

| Part   | Description                                        |
| ------ | -------------------------------------------------- |
| `base` | The outer `<div>` wrapper                          |
| `tabs` | The `<div role="tablist">` holding all tab buttons |

***

### `bq-tab`

#### Properties

| Property                | Attribute     | Description                                                   | Type                             | Default        |
| ----------------------- | ------------- | ------------------------------------------------------------- | -------------------------------- | -------------- |
| `active`                | `active`      | If true, the tab is rendered in its active state              | `boolean`                        | `false`        |
| `controls` *(required)* | `controls`    | The `id` of the panel element this tab controls               | `string`                         | —              |
| `disabled`              | `disabled`    | If true, the tab is non-interactive                           | `boolean`                        | `false`        |
| `orientation`           | `orientation` | Layout direction — propagated automatically by `bq-tab-group` | `"horizontal" \| "vertical"`     | `"horizontal"` |
| `placement`             | `placement`   | Alignment — propagated automatically by `bq-tab-group`        | `"start" \| "end"`               | `"start"`      |
| `size`                  | `size`        | Size — propagated automatically by `bq-tab-group`             | `"small" \| "medium" \| "large"` | `"medium"`     |
| `tabId` *(required)*    | `tab-id`      | Unique identifier used to match the tab to its panel          | `string`                         | —              |

#### Events

| Event       | Description                                          | Type                            |
| ----------- | ---------------------------------------------------- | ------------------------------- |
| `bqClick`   | Fired when the tab is clicked or activated           | `CustomEvent<HTMLBqTabElement>` |
| `bqFocus`   | Fired when the tab receives focus                    | `CustomEvent<HTMLBqTabElement>` |
| `bqBlur`    | Fired when the tab loses focus                       | `CustomEvent<HTMLBqTabElement>` |
| `bqKeyDown` | Fired when a key is pressed while the tab is focused | `CustomEvent<KeyboardEvent>`    |

#### Methods

| Method     | Description                                            | Signature                   |
| ---------- | ------------------------------------------------------ | --------------------------- |
| `vClick()` | Simulates a click on the underlying `<button>` element | `vClick() => Promise<void>` |
| `vFocus()` | Sets focus on the underlying `<button>` element        | `vFocus() => Promise<void>` |
| `vBlur()`  | Removes focus from the underlying `<button>` element   | `vBlur() => Promise<void>`  |

#### Slots

| Slot        | Description                                  |
| ----------- | -------------------------------------------- |
| *(default)* | The tab label text                           |
| `icon`      | An optional icon to display before the label |

#### Shadow parts

| Part        | Description                                                   |
| ----------- | ------------------------------------------------------------- |
| `base`      | The `<button>` element                                        |
| `content`   | The `<div>` wrapping the icon and text                        |
| `icon`      | The `<div>` holding the slotted icon                          |
| `text`      | The `<div>` holding the slotted label text                    |
| `underline` | The element that renders the active-state underline indicator |

#### CSS custom properties

<Expandable title="CSS variables" defaultOpen={true}>
  | Variable                              | Description                            | Default                               |
  | ------------------------------------- | -------------------------------------- | ------------------------------------- |
  | `--bq-tab--font-size`                 | Font size of the tab label             | `var(--bq-font-size--m)`              |
  | `--bq-tab--font-weight`               | Font weight of the tab label           | `var(--bq-font-weight--medium)`       |
  | `--bq-tab--line-height`               | Line height of the tab label           | `var(--bq-font-line-height--regular)` |
  | `--bq-tab--label-icon-gap`            | Gap between the icon and the label     | `var(--bq-spacing-xs)`                |
  | `--bq-tab--padding-vertical-small`    | Vertical padding for the small size    | `var(--bq-spacing-xs2)`               |
  | `--bq-tab--padding-horizontal-small`  | Horizontal padding for the small size  | `var(--bq-spacing-m)`                 |
  | `--bq-tab--padding-vertical-medium`   | Vertical padding for the medium size   | `var(--bq-spacing-xs)`                |
  | `--bq-tab--padding-horizontal-medium` | Horizontal padding for the medium size | `var(--bq-spacing-l)`                 |
  | `--bq-tab--padding-vertical-large`    | Vertical padding for the large size    | `var(--bq-spacing-s)`                 |
  | `--bq-tab--padding-horizontal-large`  | Horizontal padding for the large size  | `var(--bq-spacing-l)`                 |
  | `--bq-tab--icon-size-small`           | Icon size for the small variant        | `20px`                                |
  | `--bq-tab--icon-size-medium`          | Icon size for the medium variant       | `24px`                                |
  | `--bq-tab--icon-size-large`           | Icon size for the large variant        | `24px`                                |
</Expandable>

<Tip>
  Learn more about [styling with shadow parts](/guides/styles) and [CSS custom properties](/theming/global-css-variables).
</Tip>

## Resources

<CardGroup cols={2}>
  <Card horizontal title="Interactive playground" icon="code" href="https://storybook.beeq.design/?path=/story/components-tabs--default">
    Explore Tab variants and states in Storybook
  </Card>

  <Card horizontal title="Source code" icon="github" href="https://github.com/Endava/BEEQ/tree/main/packages/beeq/src/components/tab">
    View the component source on GitHub
  </Card>
</CardGroup>
