Tabs

Tabs switch between views that sit at the same level of one screen. Primary tabs sit at the top of the content, secondary tabs divide what is inside one of those views.

Usage

Tabs provide a user interface for navigating between distinct sections or pages within an application. Each <Tabs> component contains multiple <Tab> children, and the <Tabs> component itself can be styled as either primary or secondary variants to suit different use cases.

Primary tabs

<script lang="ts">
	import { Tab, Tabs } from 'noph-ui'
</script>

<Tabs value="videos">
	<Tab badge badgeLabel="2" value="videos">Videos</Tab>
	<Tab value="theme">Theme</Tab>
	<Tab badge value="settings">Settings</Tab>
</Tabs>

Icons

<script lang="ts">
	import { Tab, Tabs } from 'noph-ui'
	import { Icon } from 'noph-ui/icons'
</script>

<Tabs value="videos">
	<Tab badge badgeLabel="2" value="videos">
		{#snippet icon()}<Icon>videocam</Icon>{/snippet}Videos
	</Tab>
	<Tab value="theme">{#snippet icon()}<Icon>palette</Icon>{/snippet}Theme</Tab>
	<Tab badge value="settings">{#snippet icon()}<Icon>settings</Icon>{/snippet}Settings</Tab>
</Tabs>

Secondary tabs

<script lang="ts">
	import { Tab, Tabs } from 'noph-ui'
</script>

<Tabs variant="secondary" value="videos">
	<Tab badge badgeLabel="2" value="videos">Videos</Tab>
	<Tab value="theme">Theme</Tab>
	<Tab badge value="settings">Settings</Tab>
</Tabs>

Icons

<script lang="ts">
	import { Tab, Tabs } from 'noph-ui'
	import { Icon } from 'noph-ui/icons'
</script>

<Tabs variant="secondary" value="videos">
	<Tab badge badgeLabel="2" value="videos">
		{#snippet icon()}<Icon>videocam</Icon>{/snippet}Videos
	</Tab>
	<Tab value="theme">{#snippet icon()}<Icon>palette</Icon>{/snippet}Theme</Tab>
	<Tab badge value="settings">{#snippet icon()}<Icon>settings</Icon>{/snippet}Settings</Tab>
</Tabs>

Selection

The value prop on <Tabs> determines which tab is currently selected. Each <Tab> must have a unique value within the same <Tabs> group. When the value of <Tabs> matches a <Tab>'s value, that tab is highlighted as selected. To react to tab changes, use bind:value on <Tabs> to keep track of the selected tab in your component state.

Selected tab: videos
<script lang="ts">
	import { Tab, Tabs } from 'noph-ui'

	let value = $state('videos')
</script>

<div>
	<Tabs bind:value>
		<Tab value="videos">Videos</Tab>
		<Tab value="theme">Theme</Tab>
		<Tab value="settings">Settings</Tab>
	</Tabs>
	<div style="margin-top:1rem;font-size:0.825rem">Selected tab: {value}</div>
</div>

To enable navigation between pages or routes, use the href attribute on <Tab>. This renders the tab as a link, allowing users to navigate to different routes when a tab is clicked. The href attribute works seamlessly with the value prop, so you can track which tab is selected even when navigating between pages. This approach is especially useful for SSR and deep linking.

<script lang="ts">
	import { page } from '$app/state'
	import { Tab, Tabs } from 'noph-ui'
</script>

<div>
	<Tabs variant="secondary" value={page.url.searchParams.get('tab') || 'videos'}>
		<Tab data-sveltekit-noscroll href="/components/tabs?tab=videos" value="videos">Videos</Tab>
		<Tab data-sveltekit-noscroll href="/components/tabs?tab=theme" value="theme">Theme</Tab>
		<Tab data-sveltekit-noscroll href="/components/tabs?tab=settings" value="settings">
			Settings
		</Tab>
	</Tabs>
</div>

Theming

Tabs are drawn from the color roles: --np-color-primary for the indicator and the selected label, --np-color-on-surface-variant for the rest, and --np-color-secondary for the focus ring.

Custom propertyDefault value
--np-tabs-indicator-radius--np-shape-corner-full on primary tabs, 0 on secondary tabs

Accessibility

The strip renders role="tablist" and each tab role="tab" with aria-selected. The tabs share one tab stop: the left and right arrow keys move between them, Home and End jump to the ends, and Tab leaves the strip for the panel.

Where a tab reveals a panel on the same page, give the panel an id, point at it with controls and mark the panel role="tabpanel". Tabs that navigate take an href instead and stay real links. A badge on a tab needs badgeAriaLabel to say what the number counts.

API

Tabs

Attributes

AttributeTypeDefaultDescription
valuenumber | stringValue of the current tab.
variant'primary' | 'secondary''primary'Sets the visual style of the tab. Use 'primary' for the default appearance or 'secondary' for an alternative style.
elementHTMLElementA reference to the root DOM element of the component.

Tab

Attributes

AttributeTypeDefaultDescription
inlineIconbooleanfalseOnly affects the primary variant.
badgebooleanfalse
badgeLabelstring | number | undefinedundefinedA string representing the label to be displayed inside a badge element.
badgeAriaLabelstring | undefinedundefinedAnnounces the badge, for example "2 new videos". Without it the badge is aria-hidden and only the tab label is read out.
controlsstring | undefinedundefinedThe id of the panel this tab controls, rendered as aria-controls.
valuenumber | stringundefinedThe value associated with this tab. Used to identify which tab is selected.
iconSnippet | undefinedundefinedProvides a custom icon for the tab, typically rendered before the tab label. Accepts a Svelte snippet, such as an <Icon> component.
hrefstringundefinedWhen set, the tab will render as a link using the provided URL. This allows navigation to other pages or routes when the tab is clicked.
elementHTMLElementA reference to the root DOM element of the component.