Skip to content

Transitions & Animation

Animate style changes with .Transition(...). The component does not rerun for each frame.

#Hover transitions

Put the transition on the base style so it applies when the pointer enters and leaves.

func HoverAction() *ui.Element {
	return ui.Button().Child("Continue").
		Padding(12).
		BorderRadius(8).
		BackgroundColor("#334155").
		TextColor("white").
		Opacity(0.85).
		Transition("background-color 160ms ease-out, opacity 160ms ease-out").
		Hover(func(s ui.StyleBuilder) ui.StyleBuilder { return s.BackgroundColor("#2563eb").Opacity(1) })
}

The same declaration works with Active, Focus, Expanded, and DragOver.

#State-driven animation

Change the target with a signal or .When. Keep the node mounted while it animates.

func AnimatedSelection() *ui.Element {
	selected, setSelected := ui.CreateSignal(false)
	return ui.View().FlexCol().Gap(12).Children(
		ui.Button().Child("Toggle emphasis").OnClick(func() { setSelected(!selected()) }),
		ui.View().Child("Your selection").
			Padding(20).
			BorderRadius(12).
			BackgroundColor("#334155").
			TextColor("white").
			Opacity(0.6).
			Transition("background-color 180ms ease-out, opacity 180ms ease-out").
			When(selected, ui.Style().BackgroundColor("#2563eb"), ui.Style().Opacity(1)),
	)
}

Reversing mid-transition continues from the current visible value.

#Timing and frame rate

Use ui.TransitionDeclaration when several properties share one timing.

func CappedTransition() *ui.Element {
	return ui.Button().Child("Hover me").Padding(12).BackgroundColor("#334155").TextColor("white").Opacity(0.8).
		Transition(ui.TransitionDeclaration{
			Properties: []string{"background-color", "opacity"},
			Duration:   180 * time.Millisecond,
			Easing:     "ease-out",
			MaxFps:     30,
		}).
		Hover(func(s ui.StyleBuilder) ui.StyleBuilder { return s.BackgroundColor("#2563eb").Opacity(1) })
}
FieldUsage
.TransitionProperty(...)Property names, as a comma-separated string or []string.
.TransitionDuration(...)A number of milliseconds, a time.Duration, or "180ms" / "0.18s". Durations are limited to 0–10 seconds.
.TransitionEasing(...)"linear", "ease", "ease-in", "ease-out", or "ease-in-out".
.TransitionMaxFps(...)Optional frame-rate cap. Omit it to follow the display.

A node has one duration, easing, and frame-rate cap for all selected properties. Delays, cubic-bezier(), and per-property timings are not supported.

ui.Style().Transition(180 * time.Millisecond) animates background, border, and text colors. "all" includes every supported paint property. "none" removes the shorthand.

#Supported properties

Transition nameStyle method
background-color or background.BackgroundColor(...) or a solid .Background(...)
border-color.BorderColor(...)
border-width.BorderWidth(...)
border-radius.BorderRadius(...)
color.TextColor(...)
opacity.Opacity(...)
transform.Transform(...)
box-shadow.BoxShadow(...)

Width, height, padding, and gradient stops do not interpolate. For a sliding switch thumb, use .Transform("translateX(20px)") with .Transition("transform 160ms ease-out").

#Mounting and reduced motion

A transition animates a change on an existing node. ui.Show removes children immediately; it does not wait for a fade-out. Opacity zero still receives layout and input.

For dialogs, EnterDuration and ExitDuration control how long closing content stays mounted. See Dialog.

Transitions follow the system Reduce Motion setting. Set native.WindowOptions.ReduceMotion to override it for one window.

#Animated images

Pass a GIF or WebP path to ui.Image. Playback follows the file; you do not need a timer.

func AnimatedImage(path string) *ui.Element {
	return ui.Image().Value(path).
		Width(96).Height(96).ObjectFit("contain")
}

Package files with the CLI resources option. See Bundled Resources and Image.