Skip to content

Tabs

Sections of content, one shown at a time, chosen from a list of tabs.

Solidtype_heroui.Tabs is Kobalte’s tabs with the classes HeroUI’s own tabsVariants gives: a list of tabs, and a panel for each, shown while its tab, of the same id, is selected. The arrows of its orientation move between the tabs, round from the last to the first, and Home and End to either end, skipping a disabled tab; focus selects the tab it reaches, or Enter and Space do with keyboard_activation=`manual.

The selected tab’s indicator slides from the tab selected before, as HeroUI’s styles animate it. A tab and its panel are named by their id, which each takes; HeroUI’s take one too, without which a panel has no tab.

HeroUI’s examples.

tabs_usage.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-md">
<Tabs.List_container>
<Tabs.List aria_label="Options">
<Tabs.Tab id="overview">"Overview" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="analytics">"Analytics" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="reports">"Reports" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="pt-4" id="overview">
<p>"View your project overview and recent activity."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="analytics">
<p>"Track your metrics and analyze performance data."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="reports">
<p>"Generate and download detailed reports."</p>
</Tabs.Panel>
</Tabs>
<Tabs>
<Tabs.List_container>
<Tabs.List aria_label="Options">
<Tabs.Tab id="...">
<Tabs.Separator /> (* optional *)
<Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel id="..." />
</Tabs>
tabs_vertical.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-lg" orientation=`vertical>
<Tabs.List_container>
<Tabs.List aria_label="Vertical tabs">
<Tabs.Tab id="account">"Account" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="security">"Security" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="notifications">
"Notifications" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="billing">"Billing" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="px-4" id="account">
<h3 class_="mb-2 font-semibold">"Account Settings"</h3>
<p class_="text-sm text-muted">
"Manage your account information and preferences."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="security">
<h3 class_="mb-2 font-semibold">"Security Settings"</h3>
<p class_="text-sm text-muted">
"Configure two-factor authentication and password settings."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="notifications">
<h3 class_="mb-2 font-semibold">"Notification Preferences"</h3>
<p class_="text-sm text-muted">
"Choose how and when you want to receive notifications."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="billing">
<h3 class_="mb-2 font-semibold">"Billing Information"</h3>
<p class_="text-sm text-muted">
"View and manage your subscription and payment methods."
</p>
</Tabs.Panel>
</Tabs>

When the tabs are more than their container holds, Tabs.List_container scrolls them, fades the edge they are hidden past, and shows a chevron at that end which scrolls on by most of a view, in either orientation.

tabs_overflow.mlx
module Tabs = Solidtype_heroui.Tabs
let items =
[|
("overview", "Overview");
("analytics", "Analytics");
("reports", "Reports");
("performance", "Performance");
("engagement", "Engagement");
("audience", "Audience");
("acquisition", "Acquisition");
("retention", "Retention");
("settings", "Settings");
|]
let make () =
<div class_="w-[400px]">
<Tabs>
<Tabs.List_container>
<Tabs.List aria_label="Overflow options">
<Solidtype.For each=(fun () -> items)>
(fun (id, label) _ ->
<Tabs.Tab id=(fun () -> id)>
(fun () -> Solidtype.Jsx.string label) <Tabs.Indicator />
</Tabs.Tab>)
</Solidtype.For>
</Tabs.List>
</Tabs.List_container>
<Solidtype.For each=(fun () -> items)>
(fun (id, label) _ ->
<Tabs.Panel class_="pt-4" id=(fun () -> id)>
<p>(fun () -> Solidtype.Jsx.string (label ^ " panel content."))</p>
</Tabs.Panel>)
</Solidtype.For>
</Tabs>
</div>

A tab is disabled while it or the tabs are; the arrows pass over it.

tabs_disabled.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-md">
<Tabs.List_container>
<Tabs.List aria_label="Tabs with disabled">
<Tabs.Tab id="active">"Active" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab is_disabled=true id="disabled">
"Disabled" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="available">"Available" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="pt-4" id="active">
<p>"This tab is active and can be selected."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="disabled">
<p>"This content cannot be accessed."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="available">
<p>"This tab is also available for selection."</p>
</Tabs.Panel>
</Tabs>

Tabs.Separator inside each tab but the first draws a line between tabs, hidden beside the selected one.

tabs_separator.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-md">
<Tabs.List_container>
<Tabs.List aria_label="Options">
<Tabs.Tab id="overview">"Overview" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="analytics">
<Tabs.Separator /> "Analytics" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="reports">
<Tabs.Separator /> "Reports" <Tabs.Indicator />
</Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="pt-4" id="overview">
<p>"View your project overview and recent activity."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="analytics">
<p>"Track your metrics and analyze performance data."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="reports">
<p>"Generate and download detailed reports."</p>
</Tabs.Panel>
</Tabs>
tabs_secondary.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-md" variant=`secondary>
<Tabs.List_container>
<Tabs.List aria_label="Options">
<Tabs.Tab id="overview">"Overview" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="analytics">"Analytics" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="reports">"Reports" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="pt-4" id="overview">
<p>"View your project overview and recent activity."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="analytics">
<p>"Track your metrics and analyze performance data."</p>
</Tabs.Panel>
<Tabs.Panel class_="pt-4" id="reports">
<p>"Generate and download detailed reports."</p>
</Tabs.Panel>
</Tabs>
tabs_secondary_vertical.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs class_="w-full max-w-lg" orientation=`vertical variant=`secondary>
<Tabs.List_container>
<Tabs.List aria_label="Vertical tabs">
<Tabs.Tab id="account">"Account" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="security">"Security" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="notifications">
"Notifications" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="billing">"Billing" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="px-4" id="account">
<h3 class_="mb-2 font-semibold">"Account Settings"</h3>
<p class_="text-sm text-muted">
"Manage your account information and preferences."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="security">
<h3 class_="mb-2 font-semibold">"Security Settings"</h3>
<p class_="text-sm text-muted">
"Configure two-factor authentication and password settings."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="notifications">
<h3 class_="mb-2 font-semibold">"Notification Preferences"</h3>
<p class_="text-sm text-muted">
"Choose how and when you want to receive notifications."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="billing">
<h3 class_="mb-2 font-semibold">"Billing Information"</h3>
<p class_="text-sm text-muted">
"View and manage your subscription and payment methods."
</p>
</Tabs.Panel>
</Tabs>

A tab’s content is centred, or set to the start or the end by align.

tabs_alignment.mlx
module Tabs = Solidtype_heroui.Tabs
let make () =
<Tabs
align=`start
class_="w-full max-w-lg"
orientation=`vertical
variant=`secondary>
<Tabs.List_container>
<Tabs.List aria_label="Settings">
<Tabs.Tab id="general">"General" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="billing">
"Subscription & Billing" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="appearance">"Appearance" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="notifications">
"Notifications" <Tabs.Indicator />
</Tabs.Tab>
<Tabs.Tab id="privacy">"Privacy" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="px-4" id="general">
<h3 class_="mb-2 font-semibold">"General"</h3>
<p class_="text-sm text-muted">
"Manage your account information and preferences."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="billing">
<h3 class_="mb-2 font-semibold">"Subscription & Billing"</h3>
<p class_="text-sm text-muted">
"View and manage your plan and payment methods."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="appearance">
<h3 class_="mb-2 font-semibold">"Appearance"</h3>
<p class_="text-sm text-muted">
"Choose a theme and adjust the interface density."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="notifications">
<h3 class_="mb-2 font-semibold">"Notifications"</h3>
<p class_="text-sm text-muted">
"Choose how and when you want to receive notifications."
</p>
</Tabs.Panel>
<Tabs.Panel class_="px-4" id="privacy">
<h3 class_="mb-2 font-semibold">"Privacy"</h3>
<p class_="text-sm text-muted">
"Control what you share and who can see your activity."
</p>
</Tabs.Panel>
</Tabs>

class_ is added after HeroUI’s classes, on every part.

tabs_tailwind.mlx
module Tabs = Solidtype_heroui.Tabs
let list_class =
String.concat " "
[
"rounded-xl border border-accent/10 bg-accent-soft/30 p-1";
"**:data-[slot=tabs-tab]:rounded-lg";
"**:data-[slot=tabs-tab]:bg-transparent";
"**:data-[slot=tabs-tab]:text-muted";
"**:data-[slot=tabs-tab]:opacity-100";
"**:data-[slot=tabs-tab]:transition-colors";
"**:data-[slot=tabs-tab]:data-[hovered=true]:not-data-[selected=true]:bg-accent-soft";
"**:data-[slot=tabs-tab]:data-[hovered=true]:not-data-[selected=true]:text-accent-soft-foreground";
"**:data-[slot=tabs-tab]:data-[pressed=true]:not-data-[selected=true]:bg-accent-soft-hover";
"**:data-[slot=tabs-tab]:data-[focus-visible=true]:ring-2";
"**:data-[slot=tabs-tab]:data-[focus-visible=true]:ring-accent/15";
"**:data-[slot=tabs-tab]:data-[selected=true]:font-medium";
"**:data-[slot=tabs-tab]:data-[selected=true]:text-accent-foreground";
"**:data-[slot=tabs-tab]:shadow-none";
"**:data-[slot=tabs-indicator]:rounded-lg";
"**:data-[slot=tabs-indicator]:bg-accent";
"**:data-[slot=tabs-indicator]:shadow-none";
]
let make () =
<Tabs class_="w-full max-w-sm">
<Tabs.List_container class_="rounded-none bg-transparent">
<Tabs.List aria_label="Billing cycle" class_=(fun () -> list_class)>
<Tabs.Tab id="monthly">"Monthly" <Tabs.Indicator /></Tabs.Tab>
<Tabs.Tab id="yearly">"Yearly" <Tabs.Indicator /></Tabs.Tab>
</Tabs.List>
</Tabs.List_container>
<Tabs.Panel class_="pt-3 text-sm text-muted" id="monthly">
"Billed monthly, cancel anytime."
</Tabs.Panel>
<Tabs.Panel class_="pt-3 text-sm text-muted" id="yearly">
"Save 20% with annual billing."
</Tabs.Panel>
</Tabs>

HeroUI’s classes are global, so a stylesheet restyles every tabs, as with HeroUI in React:

@layer components {
.tabs__list {
@apply rounded-xl border border-border bg-surface p-1;
}
.tabs__indicator {
@apply rounded-lg bg-foreground shadow-sm;
}
}
  • .tabs, the root, and .tabs--secondary, .tabs--align-start and .tabs--align-end
  • .tabs__list-container, and inside it .tabs__list-container__scroller, .tabs__list-container__scroll-prev and .tabs__list-container__scroll-next
  • .tabs__list, .tabs__tab, .tabs__indicator, .tabs__separator
  • .tabs__panel
  • Selected: [aria-selected="true"] and [data-selected="true"] on the tab
  • Hover: :hover or [data-hovered="true"] on the tab
  • Focus: :focus-visible or [data-focus-visible="true"] on the tab
  • Disabled: [aria-disabled="true"] and [data-disabled="true"] on the tab
  • Orientation: [data-orientation] on the root, the list and the panel

Each prop beside the name HeroUI’s React component gives it.

React solidtype Type Default Description
selectedKey selected_key string The selected tab’s id, while given
defaultSelectedKey default_selected_key string the first not disabled The one selected at first
onSelectionChange on_selection_change string -> unit Called with the id of the tab selected
orientation orientation [ `horizontal | `vertical ] `horizontal How the tabs lie
variant variant [ `primary | `secondary ] `primary Its look
align align [ `start | `center | `end_ ] `center Where a tab’s content sits
keyboardActivation keyboard_activation [ `automatic | `manual ] `automatic Whether focus selects, or Enter and Space do
isDisabled is_disabled bool false Whether every tab is disabled
className class_ string Added after HeroUI’s classes
render — Not yet
React solidtype Type Description
aria-label aria_label string What the tabs are, for a reader
className class_ string Added after HeroUI’s classes
React solidtype Type Default Description
id id string Its key, and its panel’s; required
isDisabled is_disabled bool false Whether it is disabled
className class_ string Added after HeroUI’s classes
href, render — Not yet
React solidtype Type Description
id id string Its tab’s id; required
className class_ string Added after HeroUI’s classes

Tabs.List_container, Tabs.Indicator, Tabs.Separator

Section titled “Tabs.List_container, Tabs.Indicator, Tabs.Separator”
React solidtype Type Description
className class_ string Added after HeroUI’s classes

Original HeroUI documentation: Tabs.

The same HeroUI Tabs twice, under the same props: in solidtype on the left, in React on the right. Below them, every attribute of the two roots: a marked row is one they disagree on, and a dimmed row is a framework’s own, which no style reads.

A tab is Kobalte’s button where React Aria’s is a div, each of role tab. A panel names no orientation, as React Aria’s names none, so the spacing HeroUI’s styles would give it by its orientation reaches neither, and a panel sits as close to its list as on heroui.com. React Aria marks its root focused while a tab is, which no style reads and the port does not.

solidtype — OCaml, on Solid and Kobalte
About overview.
React — TypeScript, on React Aria
attributesolidtypeReact