lotusui the ikaito design language for Gio — desktop, mobile, web
GitHub

Tabs

Three variants and an explicit Update contract.

Tab labels in a row; the active one styled by variant. Update must run before anything reads the selection in the same frame — Layout deliberately does NOT process clicks, so a consumer that forgets Update gets dead tabs (impossible to miss) instead of subtle one-frame lag (which survives review).

Like every choice component, the selection is a VALUE: the cursor is unexported, Value()/SetValue() read and write it, the zero value picks the first option, and an unknown value clears rather than falling back to option 0 — see Select for the full contract.

Installation

Use the module import (the default), or own the source: lotusui add vendors the component into your app and lotusui update keeps the copy mergeable — see Registry.

Go
go get github.com/ikaito-com/lotusui

# or vendor the source into your app:
go run github.com/ikaito-com/lotusui/cmd/lotusui add tabs

Usage

The default look: the strip sits in a muted rounded well and the active tab is a raised panel inside it.

Go
tabs := lotusui.Tabs{Options: lotusui.TabOpts("Account", "Password")}

tabs.Update(gtx) // BEFORE anything reads the selection
body := accountBody
if tabs.Value() == "Password" {
	body = passwordBody
}
tabs.Layout(th, gtx)

Line variant

TabsLine is the classic underline strip: quiet labels, a 2dp brand-ink bar under the active tab, hover pre-shadowed.

Go
tabs := lotusui.Tabs{Variant: lotusui.TabsLine}

Subtle variant

TabsSubtle renders pill-styled labels without the well — the quietest strip.

Go
tabs := lotusui.Tabs{Variant: lotusui.TabsSubtle}

Vertical

Vertical stacks the strip as a column — pair it with your content beside it.

Go
tabs := lotusui.Tabs{Vertical: true}

Icons

Each option's Icon renders before its label, in the tab's own ink.

Go
tabs := lotusui.Tabs{Options: []lotusui.TabOption{
	{Label: "Files", Icon: lotusui.IconFile},
	{Label: "Changes", Icon: lotusui.IconChanges},
	{Label: "Settings", Icon: lotusui.IconSettings},
}}

Disabled tab

A disabled option dims, drops the pointer cursor, and its clicks are skipped by Update.

Go
tabs := lotusui.Tabs{Options: []lotusui.TabOption{
	{Label: "Overview"},
	{Label: "Archived", Disabled: true},
	{Label: "Settings"},
}}

Wrapping strip

The horizontal strip uses Wrap so whole tabs flow to the next line at intrinsic width — never a squeezed 1-character label under a narrow Max.X (Split pane, or a narrow window). The enclosed well grows with wrapped height. Resize the demo box: the strip reflows. Vertical stays a column Flex.

Go
tabs := lotusui.Tabs{Options: lotusui.TabOpts(
	"Changes", "Staging", "Production", "Reviews", "Approvals", "History",
)}
// under a narrow Max.X the strip wraps to two lines

API

OptionTypeDescription
Options[]TabOptionThe tabs: Label, Value, Icon and Disabled. TabOpts("a","b") builds label-only strips.
Update(gtx)methodProcesses clicks. MUST run before anything reads the selection in a frame — Layout never processes clicks.
Value() / SetValue(v)stringThe selected tab's value; SetValue with an unknown value clears.
Clear() / Chosen()methodLeave nothing selected; whether a tab is selected.
VariantTabsVariantTabsDefault (muted well, raised active tab), TabsLine (underline strip), TabsSubtle (pill-styled).
VerticalboolStacks the strip as a column (Flex). Horizontal uses Wrap.

Every demo on this site is the real Go component compiled to WebAssembly — one gallery app, one bundle, addressed by URL hash.