A widget that calls the platform's own button is as portable as the least capable platform it targets. go-widgets draws every widget itself, through one small painter interface, and lets a back-end decide whether that becomes the pixels of a window, a browser canvas, an Android surface โ or the cells of a terminal. Pure Go, CGO_ENABLED=0. BSD-3-Clause.
OpenCGO=0 on every target# one demo, cross-compiled from one machine, cgo off $ for t in linux/amd64 linux/riscv64 \ linux/s390x darwin/arm64 windows/arm64 \ android/arm64 js/wasm; do CGO_ENABLED=0 GOOS=${t%/*} GOARCH=${t#*/} \ go build -o /dev/null ./cmd/windowdemo \ && echo "ok $t" done ok linux/amd64 ok linux/riscv64 ok linux/s390x ok darwin/arm64 ok windows/arm64 ok android/arm64 ok js/wasm # the one Go itself refuses $ CGO_ENABLED=0 GOOS=android GOARCH=arm \ go build ./cmd/windowdemo android/arm requires external (cgo) linking, but cgo is not enabled
Its Draw talks to a painter: fill a rectangle, stroke one, set a pixel, draw text. Whether those calls become pixels or terminal cells is the back-end's business. That is the whole reason one widget tree runs in a native window, a browser and a terminal without a line of conditional code.
No webview, no system widget set, no C toolchain. 160 widget types โ a data suite with Table, Kanban, Gantt and an event calendar, eight charts, menus, dialogs โ drawn the same way on every surface, tested once.
window.Open takes no back-end argument. It reads the environment โ the platform, $WAYLAND_DISPLAY, $DISPLAY, an Android host's socket โ and opens the window that can work. The same Android binary dials its host inside an APK and opens a Linux window under Termux.
X11 is the core protocol written from scratch over the socket. macOS is reached through the Objective-C runtime, Windows through the process's own DLLs, both from Go. Android, which hands no surface to a CGO-free process, gets a thin Java host that only blits.
Observables, commands and lists, bound to widgets once. The core imports no widget package, so one view-model drives the pixel widgets and the terminal ones. mvvmlint fails a pull request that assigns a widget's field by hand.
A toolkit that paints its own pixels gives a screen reader nothing โ unless it says what it drew. Widgets report a role, a name and a value; the back-ends publish them: AT-SPI on Linux, a virtual view hierarchy on Android, pulled only when something reads it.
One call switches the whole UI to anti-aliased, shaped text in a bundled face, rasterised by go-opentype in Go โ no FreeType, no system font lookup, the same in a browser. GTK and libadwaita themes load as a Theme.
A skin describes a widget's parts, states and transitions in JSON, rendered by one engine. The toolkit's own button, switch and card, written as skins, render pixel for pixel the same.
A typed store queries an in-process proxy or a gRPC one over a WebSocket, and the conformance test requires the two to return byte-identical views โ so a wasm client and a native one see the same rows.
Pixels sampled from a real X11 server under Xvfb and from a macOS window in CI, clicks driven through a real Win32 window procedure on Windows 11, composited pixels read back from the wasmdesk compositor in Chromium, blits timed on an Android device. What is only compiled is said too.
Wayland (xdg-shell) or X11 (the core protocol over the socket, MIT-SHM), chosen from the session; GTK4 on request.
An NSWindow through the Objective-C runtime with purego; a menu-bar NSStatusItem for the tray.
A Win32 window through the process’s own DLLs and a Go window procedure; Shell_NotifyIcon for the tray.
A plain <canvas>, through webcanvas or window.Open itself, or a window of the wasmdesk compositor over a MessagePort and a shared buffer.
The cell painter, serialised as 24-bit ANSI; an interactive runner with raw mode and the alternate screen.
A real APK: a Java host blits a memfd the Go process paints; input and accessibility cross a socket. arm64.
toolkit v0.329.0, counted at that tagwindow.OpenCGO_ENABLED=0painter v0.16.0The seam every widget draws through: seven primitives, optional capabilities (clip, paths, images, masks, faces), a pixel painter and a cell painter. Docs โ
toolkit v0.329.0160 widget types: inputs, containers with swappable layouts, menus and dialogs, a data suite (Table, TreeTable, Kanban, Gantt, Agenda), eight charts. GTK themes, OpenType text, touch, accessibility. Docs โ
skin v0.3.0A widget’s parts, states and transitions in a .skin.json file. The toolkit’s button, check, switch, chip and card, rewritten as skins, render pixel for pixel the same. Docs โ
isoicons v0.4.0Isometric icon packs โ 44 cloud-native, 9 AWS โ for the toolkit’s isometric diagrams. Assets vendored unmodified, each under its own licence. Docs โ
mvvm v0.15.0Observable, Command, ObservableList, binders, and an undo stack. Generics, no reflection; the core imports no widget package, so one view-model drives pixels and cells. Docs โ
mvvmtk v0.15.0One call binds a toolkit widget to a view-model, with the widget’s real field and callback names filled in. Every binder returns an unbind. Docs โ
data v0.4.0Typed records, a pure query engine, and a store whose in-process and gRPC proxies return byte-identical views โ natively and in wasm. Docs โ
window v0.89.1One Open/Run over X11, Wayland, GTK4, Cocoa, Win32, Android, wasmbox and a browser tab. X11 is the core protocol written from scratch over the socket; macOS and Windows are reached without cgo. Docs โ
application v0.8.1The lifecycle above a window: the run loop, a tray icon attached once the first frame is up, the live system appearance, the accessibility tree, a ready hook. Docs โ
tray v0.15.0A menu-bar or notification-area icon with menus, submenus and checkboxes: NSStatusItem, Shell_NotifyIcon, StatusNotifierItem over DBus. No build tag; ErrNoBackend where there is none. Docs โ
webcanvas v0.4.1A scene in a plain browser <canvas>: no compositor, no SharedArrayBuffer, no cross-origin isolation. Any static host serves it. Docs โ
tui v0.62.0The same widgets through the cell painter, an interactive runner (raw mode, alternate screen, resize), and cell-native widgets, a syntax-highlighting editor among them. Docs โ
android v0.16.0An installable APK whose whole interface is painted by a CGO_ENABLED=0 Go process; a thin Java host blits a shared memfd and owns nothing else. Docs โ
svg v0.7.0Any RGBA render as a self-contained, bit-exact SVG, or as a PNG. The widget catalogue rendered this way: go-widgets.github.io/svg. Docs โ
mvvmlint v0.4.2A go vet check that fails a pull request assigning a widget’s state field by hand, or shipping widgets with no view-model. Docs โ
bricolint v0.4.1A go vet check against hand-drawn UI: no painter primitives in application code, no widget rebuilt on every frame. An exemption must give its reason. Docs โ
app-template v0.5.0The starting point: a browser app with its state in a view-model, a 100% coverage gate and the MVVM gate, green from the first commit. Docs โ
No cgo, no webview, no runtime to install. One go build per target, from any machine, and the same widget tree in a window, a browser tab, a terminal or an APK. Start from app-template: MVVM-compliant and green from the first commit.