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

Dialog

The one overlay primitive: dimmed scrim, width-capped card, caller-owned visibility.

A dimmed scrim over the entire window with a width-capped SurfaceCard centered on it. It wraps arbitrary content and knows nothing about what's inside; visibility stays with the caller — the isOpen/onClose contract. Don't call Layout when closed.

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 dialog

Usage

onClose controls backdrop dismissal: pass your close func to let a backdrop click dismiss, or nil to absorb outside clicks — for typed confirmations, anywhere an accidental dismissal costs more than it saves. Lay the dialog out at window constraints (your shell's top layer / portal): inside a content column it inherits that column's constraints — the "scrim only covers part of the window" bug this type exists to end.

Escape dismisses exactly like a backdrop click (both go through onClose, so absorb-only dialogs ignore it too). The first open animates on the shared clock — the scrim fades in while the card settles upward; call Appear() when your isOpen transitions closed→open to play the entrance again.

Go
var d lotusui.Dialog
var isOpen bool

if openBtn.Clicked(gtx) && !isOpen {
	isOpen = true
	d.Appear()
}
if isOpen {
	d.Layout(th, gtx, func() { isOpen = false }, dialogBody)
}

Sizes

Width presets on the shared Size scale — 2XS 280dp through 2XL 840dp; Width stays as the free-form override. Each button below opens the dialog at that size.

Go
d.Size = lotusui.Size2XS // per open — set before Appear()
d.Size = lotusui.Size2XL

Close button

Dismissable dialogs (non-nil onClose) render the corner ✕ automatically; HideClose suppresses it when the footer carries the only exits — the demo opens without the ✕, and Save is the way out. A CUSTOM close control is any button calling your close func.

Go
d.HideClose = true // footer buttons are the only way out
lotusui.Button(th, &done, "Done", lotusui.ButtonOpts{}) // your custom close

Scrollable content

A tall dialog stops short of the window edges; longer content scrolls INSIDE — a height-capped list in the body, title fixed above.

Go
func(gtx C) D {
	gtx.Constraints.Max.Y = gtx.Dp(300)
	return lotusui.ListView(th, &scroll, gtx, len(paras), para)
}

API

OptionTypeDescription
Appear()methodRestart the entrance animation — call on the closed→open transition.
HideCloseboolSuppress the corner ✕ on dismissable dialogs.
SizeSizeWidth preset: Size2XS 280dp … Size2XL 840dp (SizeMD, the default, is 480dp).
Widthunit.DpFree-form width override; zero defers to Size.
onClosefunc()Layout parameter — backdrop-click and Escape dismissal; nil absorbs without dismissing.
contentlayout.WidgetLayout parameter — arbitrary content; the dialog knows nothing about what's inside.

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