Tabs
Tabbed interface with underline, pills, and enclosed variants.
Rendering Tabs…
Installation
Copy the source into your project with the CLI. It lands under @/components/ui and is yours to edit.
npx mlola-ui add tabsUsage
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
export default function Example() {
return (
<Tabs defaultValue="overview">
<TabsList aria-label="Project">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="activity">Activity</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="overview">Everything at a glance.</TabsContent>
<TabsContent value="activity">Who changed what, and when.</TabsContent>
<TabsContent value="settings">Names, members and billing.</TabsContent>
</Tabs>
);
}Styling comes from the engine stylesheet and the nearest theme. Set it once at the root:
<html data-theme="graphite" data-mode="light">Examples
Vertical
For settings with many sections: arrow keys follow the orientation.
Rendering Vertical…
Pills
A lighter strip for switching views inside a card or a panel.
Rendering Pills…
HTML
Without React, write this markup and load @mlola-ui/behavior once. It runs the keyboard, focus and state for every element marked data-ml="tabs", including markup added later, and needs no build step.
<div class="ml-tabs" data-ml="tabs" data-variant="default" data-orientation="horizontal">
<div class="ml-tabs-list" role="tablist" aria-label="Project" aria-orientation="horizontal">
<button class="ml-tabs-trigger" type="button" role="tab" id="tab-a" aria-controls="panel-a" aria-selected="true" data-state="active" tabindex="0">Overview</button>
<button class="ml-tabs-trigger" type="button" role="tab" id="tab-b" aria-controls="panel-b" aria-selected="false" data-state="inactive" tabindex="-1">Activity</button>
<button class="ml-tabs-trigger" type="button" role="tab" id="tab-c" aria-controls="panel-c" aria-selected="false" data-state="inactive" tabindex="-1">Settings</button>
</div>
<div class="ml-tabs-content" role="tabpanel" id="panel-a" aria-labelledby="tab-a" tabindex="0" data-state="active">Overview panel</div>
<div class="ml-tabs-content" role="tabpanel" id="panel-b" aria-labelledby="tab-b" tabindex="0" data-state="inactive" hidden>Activity panel</div>
<div class="ml-tabs-content" role="tabpanel" id="panel-c" aria-labelledby="tab-c" tabindex="0" data-state="inactive" hidden>Settings panel</div>
</div><script type="module">
import { observe } from "@mlola-ui/behavior";
observe();
</script>Options
| Prop | Values | Default |
|---|---|---|
orientation | horizontalvertical | horizontal |
variant | defaultpillsenclosed | default |
Every prop with a fixed set of values, read from the exported TypeScript types with its doc comment. Open the Code tab above for the rest of the API.
Classes and attributes
A class names each element; data-* and aria-* attributes carry its variant and state. The values listed are the ones the stylesheet draws; the default needs no attribute.
| Class | Attributes |
|---|---|
ml-tabs | data-orientation="vertical"data-variant="enclosed | pills" |
ml-tabs-list | — |
ml-tabs-trigger | aria-selected="true"data-state="active" |
ml-tabs-content | — |
Accessibility and theming
- Semantic elements, accessible names and state relationships are part of the contract.
- Keyboard, pointer and touch paths are operable; focus stays visible in every theme.
- Light and dark modes pass WCAG AA contrast in all 5 canonical themes.
- Reduced motion and forced colors keep meaning and operation.
Dependencies
Installed alongside this component. No third-party UI runtime is ever added.
@mlola-ui/behavior