---
## Tab
Tab container that manages tab switching and content display.
Supports horizontal/vertical orientation, visual variants, and keyboard navigation.
Tab props are a discriminated union on `orientation`. `fullWidth` is
only available on horizontal tabs — combining it with `orientation="vertical"`
fails type-check, since vertical triggers are already full-width by design.
**Import:** `import { Tab } from '@urbicon-ui/blocks';`
### Examples
```svelte
{#snippet tabs()}
Overview
Settings
Billing
{/snippet}
{#snippet panels()}
Overview content
Settings content
Billing content
{/snippet}
```
```svelte
{#snippet tabs()}
Code
Preview
{/snippet}
{#snippet panels()}
Tab panel content
Rendered output
{/snippet}
```
### Variants
- variant: enclosed, line, pills, solid (default: line)
- size: lg, md, sm (default: md)
- fullWidth: true (default: false)
- orientation: horizontal, vertical (default: horizontal)
- tier: commit, modify (default: modify)
### Api
| Prop | Type | Required | Default | Description |
| --- | --- | :---: | --- | --- |
| aria-label | `string` | no | | Accessible name for the tab strip, applied to the inner `role="tablist"` element — not the root wrapper, whose role-less `
` forbids `aria-label`. Recommended whenever a page holds more than one tab strip; screen readers announce tablists only by this name. |
| aria-labelledby | `string` | no | | Id of a visible element that labels the tab strip. Like `aria-label`, it is retargeted onto the inner `role="tablist"` element. Prefer this over `aria-label` when a visible heading already names the strip. |
| class | `string` | no | | Additional CSS class merged onto the root element. In the same Tailwind bucket `class` wins over the `slotClasses` entry for the element it lands on. Presets and provider defaults sit below both. |
| defaultValue | `string` | no | | Default active tab for uncontrolled mode. Ignored when `value` is set. |
| disabled | `boolean` | no | false | Disables all tabs, preventing interaction and dimming the UI. |
| fullWidth | `boolean` | no | false | Stretch triggers to fill the available width. Only meaningful on horizontal tabs. |
| mint | `MintProp` | no | 'none' | Micro-interaction preset applied to each tab trigger (per-item via context). Only applies while the item is not disabled. |
| onValueChange | `(value: string) => void` | no | | Fires when the active tab changes. Receives the new tab value string. |
| orientation | `'horizontal' | 'vertical'` | no | 'horizontal' | Tab strip axis. Determines whether triggers stack horizontally or vertically. |
| panels | `Snippet` | no | | Snippet containing TabPanel components for the content area. |
| preset | `string` | no | | Apply a named preset registered via ``. Prefer this over `class` overrides when the requested look falls outside the semantic intent palette — presets keep hover/active/dark-mode logic coherent and make the custom look reusable across the project. |
| slotClasses | `Partial>` | no | | Per-slot class overrides merged with (or replacing when unstyled) tv() styles. Slots: base | list | trigger | icon | label | badge | panel | indicator. In the same Tailwind bucket `class` wins over the `slotClasses` entry for the element it lands on. Presets and provider defaults sit below both. |
| tabs | `Snippet` | no | | Snippet containing TabItem components for the tab strip. |
| tier | `InteractiveTier` | no | 'modify' | Semantic radius tier propagated to every TabItem. Default `modify` — tabs read as navigation tap surfaces. Set to `commit` (or inherit via TierContext from a wrapping ``) for full-pill tab strips in marketing/product-overview contexts. Only the `pills`, `solid`, and `enclosed` variants render a visible corner; `line` is radius-agnostic. |
| unstyled | `boolean` | no | | Strip all default tv() styles from the container and children. |
| value | `string` | no | | Controlled active tab value. Use with `bind:value` for two-way binding. When set, the component is controlled — you must handle `onValueChange` to update it. |
| variant | `'enclosed' | 'line' | 'pills' | 'solid'` | no | line | Controls the visual style and presentation of the Tab. Determines the component's visual treatment. Available options: enclosed, line, pills, solid. |
| size | `'lg' | 'md' | 'sm'` | no | md | Controls the dimensions, padding, and text size of the Tab. Affects the component's physical footprint. Available options: lg, md, sm. |
### Types
```ts
interface RegisteredTab {
element: HTMLElement;
isDisabled: () => boolean;
}
```
```ts
interface TabContext {
registerTab: (value: string, element: HTMLElement, isDisabled: () => boolean) => () => void;
/**
* Which tab holds the roving tab stop. Normally the active one — but a
* disabled tab cannot hold focus, so the stop moves to the first enabled tab
* rather than stranding there and dropping the tablist out of the tab order.
*/
isTabStop: (value: string) => boolean;
/**
* Called by TabPanel while its element is actually in the DOM (rendering may
* be deferred by `lazy`). TabItem emits `aria-controls` only for a value a
* panel has claimed — consumers may render panel content themselves outside
* `panels`, and an `aria-controls` pointing at a missing id is an axe
* `aria-valid-attr-value` violation. Returns an unregister function.
*/
registerPanel: (value: string) => () => void;
hasPanel: (value: string) => boolean;
selectTab: (value: string) => void;
isActive: (value: string) => boolean;
readonly variant: TabVariants['variant'];
readonly orientation: TabVariants['orientation'];
readonly size: TabVariants['size'];
readonly tier: InteractiveTier;
/**
* Every axis TabItem styles itself with must travel through here — the
* trigger is where `fullWidth` becomes `flex-1`, and a variants test on
* `tabVariants({fullWidth:true})` stays green while the prop is missing
* from the context (the gap this field closes).
*/
readonly fullWidth: boolean;
readonly disabled: boolean;
readonly mint: MintProp;
}
```
```ts
type TabProps = TabPropsHorizontal | TabPropsVertical
```
```ts
type MintProp = | MintName
| { name: MintName; config?: MintConfig & Record }
| Array
| Array }>
```
```ts
type InteractiveTier = 'commit' | 'modify'
```
```ts
type MintName = BuiltinMintName | 'none' | (string & {})
```
```ts
interface MintConfig {
/**
* `hover` and `focus` hold the effect while the pointer/visible focus stays
* on the element; `click` and `load` run it once.
*/
trigger?: 'hover' | 'click' | 'focus' | 'load';
/**
* Effect duration in ms. Written as an inline per-effect custom property
* (`--blocks-mint--duration`), so the CSS transition/animation
* actually runs at this speed; unset, the theme duration tokens apply.
*/
duration?: number;
/** Delay in ms before the effect applies. */
delay?: number;
/**
* CSS easing for the effect (`--blocks-mint--easing` inline);
* unset, the per-effect theme default applies.
*/
easing?: string;
disabled?: boolean;
}
```
```ts
type BuiltinMintName = (typeof BUILTIN_MINT_NAMES)[number]
```
### Slots (slotClasses keys)
`base`, `list`, `trigger`, `icon`, `label`, `badge`, `panel`, `indicator`