Skip to content

Styling & Layout

Chain style methods on elements or on ui.Style(). Append children with .Child(...) or .Children(...), and reuse a style with .Style(shared).

return ui.View().Children(ui.Text("hello"), ui.Input().Value("xxx")).
	FlexCol().PaddingLeft(20).RoundedLg().TextAlign("center")

#Style modifiers

Later values override the same property. Lengths are logical pixels; strings also accept percentages.

func Card() *ui.Element {
	return ui.View().Child(ui.Text("Hello").TextColor("#e2e8f0")).
		Padding(20).BorderRadius(12).Bg("#111827")
}

Helpers such as .FlexCol(), .P4(), .GridCols(3), and .RoundedLg() match the TypeScript presets.

#Merging styles

.Style(base).Style(overrides) applies declarations in order. Use .Merge(...) to combine reusable builders.

func MergedCard() *ui.Element {
	base := ui.Style().Padding(20).BorderRadius(8).Bg("#111827")
	overrides := ui.Style().BorderRadius(12)
	return ui.View().Child("Merged styles").Style(base).Style(overrides)
}

#Reusable styles

ui.Style() returns a value. Methods return a new value, so a shared style is never mutated. Assign the result when you derive a variation.

func SharedCard() *ui.Element {
	card := ui.Style().Padding(20).BorderRadius(12)
	emphasized := card.PaddingLeft(28).Bg("#1e293b")
	return ui.View().Gap(12).Children(
		ui.View().Child("Hello").Style(card).Bg("#111827"),
		ui.View().Child("Another card").Style(emphasized),
	)
}

.When(condition, style) applies extra styles while the condition is true. For a reactive condition, pass a getter: .When(selected, ui.Style().Bg("#2563eb")). Passing width() during setup captures the current number; pass width (the getter) when it should stay live.

#Flexbox

.Flex(), .FlexRow(), or .FlexCol() start a flex layout. Give scrolling children .MinHeight(0) or .MinWidth(0) so they can shrink.

func Row() *ui.Element {
	return ui.View().Children(
		ui.Text("Flexible content").Flex1().MinWidth(0),
		ui.Button().Child("Action").FlexShrink(0),
	).Flex().Gap(12).ItemsCenter()
}

#Grid

func Grid() *ui.Element {
	return ui.View().Children(ui.Text("Sidebar"), ui.Text("Content")).
		Display("grid").GridTemplateColumns("220px 1fr").Gap(16)
}

.GridCols(3) and .ColSpanFull() are also available.

#Text and color

Typography inherits to children. Colors accept hex, rgb() / rgba(), or packed RGBA values. Padding and borders do not inherit.

func TypographyExample() *ui.Element {
	return ui.View().Children(
		ui.Text("Inbox").FontSize(24).FontWeight(600),
		ui.Text("All caught up."),
	).TextColor("#334155").FontSize(14).Padding(20)
}

#Interaction states

Use .Hover(...), .Active(...), .FocusStyle(...), .DisabledStyle(...), and .SelectedStyle(...). Add .Transition(...) on the base style to animate paint changes. See Transitions & Animation.

func SelectableButton() *ui.Element {
	selected, setSelected := ui.CreateSignal(false)
	button := ui.Style().Padding(12).Bg("#e5e7eb").
		Hover(func(s ui.StyleBuilder) ui.StyleBuilder { return s.Opacity(0.85) })
	return ui.Button().Child("Toggle selection").Style(button).
		When(selected(), ui.Style().Bg("#2563eb").TextColor("white")).
		OnClick(func() { setSelected(!selected()) })
}

#Groups and named group hover

.Group(true) marks an unnamed group; .Group("card") names it. .GroupHover(...) follows the nearest ancestor group. .GroupHoverNamed("card", ...) follows the nearest ancestor with that name.

func GroupExample() *ui.Element {
	return ui.View().Child(
		ui.Text("Changes when the card is hovered").
			TextColor("#64748b").
			GroupHoverNamed("card", func(s ui.StyleBuilder) ui.StyleBuilder {
				return s.TextColor("#2563eb")
			}),
	).Group("card").Padding(20)
}

.GroupActive(...) and .FocusWithin(...) work the same way for pressed and focused groups. These states accept paint properties; they cannot set the cursor.