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

Responsive

Continuous Max.X plus Theme breakpoints — reflow by default, stepped structure when you need it.

Every widget lays out from the Max.X it is given this frame — that is the continuous layer (Wrap, auto-fit SimpleGrid). For Chakra-style stepped structure (columns={{ base: 1, md: 2, lg: 4 }}), Theme owns named breakpoints and layout props resolve against them with a zero-alloc walk.

Resize the browser on the demos below. Apps override breakpoint sizes with JSON loaded at NewTheme — not registry.json.

The doctrine

Pass the width you have; never invent a fake one. Gio hands every layout a layout.Constraints. lotusui components honor it. The anti-pattern is clamping gtx.Constraints.Max.X to a constant “for the demo.”

Two layers: continuous reflow (Wrap, minChildWidth SimpleGrid) and stepped structure (Theme breakpoints + Cols / Dps / Bools / Show). Prefer continuous when equal tiles should grow smoothly; use stepped when column count or visibility must jump at named widths.

Theme breakpoints

Defaults (dp): base=0, sm=480, md=768, lg=992, xl=1280, 2xl=1536. Mobile-first: each step whose min ≤ Max.X overrides. Resolve is Theme.BreakpointIndex — O(steps), zero alloc. Layout structure only (columns, spans, gaps, page max, show/hide) — not every visual prop.

Go
// App-owned JSON (dp mins) — load at startup:
bp, err := lotusui.ParseBreakpointsJSON(jsonBytes)
th := lotusui.NewTheme(lotusui.WithBreakpoints(bp))

// Stepped columns (Chakra object syntax):
lotusui.Grid{
  Cols: lotusui.Cols(1).At("md", 2).At("lg", 4),
  Gap:  th.Space.SM,
}.Layout(th, gtx, items...)

Readable page column

LayoutPage(th, gtx, …) caps content at th.PageMax (default 920dp) or th.PageMaxAt when set. Inside that column, children still see the column width.

Go
lotusui.LayoutPage(th, gtx, func(gtx C) D {
	return lotusui.VStack(th.Space.MD,
		header,
		lotusui.SimpleGrid(th, gtx, cards, lotusui.SimpleGridProps{
			MinChildWidth: 180, MaxCols: 4, Gap: th.Space.SM,
		}, cardCell),
		lotusui.Wrap(th.Space.SM, layout.Middle, chips...),
	)(gtx)
})

Wrap — flowing chips and labels

Wrap is CSS flex-wrap for Gio: each child is measured at intrinsic width, then packed left-to-right; when the next child would exceed Max.X, a new line starts. Prefer it for badges and filter chips. HStack never wraps.

Go
lotusui.Wrap(th.Space.SM, layout.Middle,
	chip("Design"), chip("Engineering"), chip("Product"),
	chip("Marketing"), chip("Sales"), chip("Support"),
	// …)

SimpleGrid — continuous or stepped

SimpleGrid continuous mode: columns = min(MaxCols, floor(Max.X / MinChildWidth)). Stepped mode: set Columns: Cols(1).At("sm", 2).At("lg", 4) and ignore minChildWidth.

Go
lotusui.SimpleGrid(th, gtx, items, lotusui.SimpleGridProps{
	MinChildWidth: 140, MaxCols: 4, Gap: th.Space.SM,
}, cell)
// or stepped:
lotusui.SimpleGrid(th, gtx, items, lotusui.SimpleGridProps{
	Columns: lotusui.Cols(1).At("sm", 2).At("lg", 4), Gap: th.Space.SM,
}, cell)

Tabs and AnnotatedText

Horizontal Tabs and AnnotatedText use Wrap so labels never squeeze to one character under a narrow Max.X.

Go
tabs := lotusui.Tabs{Options: lotusui.TabOpts(
	"Changes", "Staging", "Production", "Reviews", "Approvals", "History",
)}

Show / hide and Dialog width

Show(th, gtx, Bools(false).At("lg", true), w) hides below a step. Dialog / AlertDialog take Sizes / Widths for stepped card width — not Button density Size.

Go
lotusui.Show(th, gtx, lotusui.Bools(false).At("lg", true), sidebar)

d.Sizes = lotusui.Sizes(lotusui.SizeSM).At("md", lotusui.SizeLG).At("xl", lotusui.Size2XL)

Panes and scrolling

Split panes each get their own Max.X — put Wrap / SimpleGrid / Tabs inside. Scroll helpers are vertical overflow, not a substitute for horizontal reflow.

Go
s.Layout(gtx, th.Space.MD, depth,
	lotusui.SplitBox(th, listPane),
	lotusui.SplitBox(th, func(gtx C) D {
		return lotusui.Wrap(th.Space.SM, layout.Middle, filters...)(gtx)
	}),
)

Choosing the right primitive

NeedUseAvoid
Readable app columnLayoutPage(th, …)Full-bleed Flexed edge-to-edge
Flowing chips / tagsWrapHStack (squeezes)
Equal tiles, smooth NSimpleGrid minChildWidthHard-coded column Flex
Equal tiles, stepped NSimpleGrid Columns / Grid.ColsHand if Max.X trees
Explicit spansGridNested HStacks
Hide below a stepShow + BoolsClamping Max.X to 0
Custom step minsParseBreakpointsJSON + WithBreakpointsEditing registry.json

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