Skip to content

go-widgets

A pure-Go widget toolkit and every surface it paints on: a native window on X11, Wayland, macOS and Windows, a browser canvas, a terminal, an Android APK.

One widget tree, painted by Go, on every surface Go reaches. A native window on X11, Wayland, macOS and Windows; a <canvas> in a browser tab; a terminal; an Android APK. No C toolchain, no webview, no system widget set: every module builds with CGO_ENABLED=0, and every pixel is drawn by the toolkit.

w, err := window.Open(window.Config{Title: "Hello"})
if err != nil {
	return err
}
defer w.Close()

box := toolkit.NewVBox()
box.Append(toolkit.NewLabel("Hello from go-widgets"))
box.Append(toolkit.NewButton("Click me", func() { /* ... */ }))
return w.Run(box) // an X11 or Wayland window, an NSWindow, a Win32 window

That is cmd/windowdemo, shortened. The same box drawn in a browser, a terminal or a PNG is the same code with a different surface underneath it.

Why one toolkit draws everything

A widget that calls the platform’s own button is as portable as the least capable platform it targets, and looks different on each. go-widgets goes the other way: a widget’s Draw only ever talks to a painter.Painter, a handful of primitives (fill a rectangle, stroke one, set a pixel, draw text). Whether those calls become RGBA pixels or terminal cells is the back-end’s business, not the widget’s.

What that costs is said plainly: no native look by default, and no free accessibility — the toolkit publishes its own accessibility tree to each platform. What it buys is one widget set, tested once, with the same behaviour everywhere, and a binary that cross-compiles from any machine to any of them.

Where to go next

A first applicationfrom app-template, or from a bare window
How a widget reaches the screenthe painter seam, the widget set, skins
The painter seamthe primitives every widget is drawn with
The toolkit160 widget types, themes, layouts, text, accessibility
Skinsa widget’s parts and states in a data file
State lives in a view-modelwhy widget fields are not assigned by hand
MVVMobservables, commands, binders, undo
The data spinetyped records, queries, a local or remote store
Where it paintsevery back-end and how it is chosen
A native windowX11, Wayland, Cocoa, Win32, GTK4, Android, wasmbox, a browser tab
An application and its traylifecycle, appearance, a menu-bar icon
A browser taba plain <canvas>, or a wasmdesk window
A terminalcell-native widgets and an interactive runner
Androida real APK painted by a CGO-free process
Snapshotsa widget as an SVG or a PNG
Rules CI enforcesmvvmlint and bricolint
Modulesall nineteen, with their sources and references
Statuswhat is verified where, and what is not done

Licence

BSD-3-Clause.

Created on by tannevaled

Modified on by tannevaled