--- ## 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`