A minimal, rfw-native UI component library. Components are composition-style
builders: each constructor creates real DOM elements and returns a chainable
node. Every node exposes Element() dom.Element, so it satisfies
composition.Node and can be appended anywhere the rfw composition package
accepts nodes. No RTML templates are required.
Styling ships as a single embedded stylesheet driven by CSS custom
properties (monochrome by default). The first component you build injects it
once; ui.InjectCSS() does it explicitly.
This library is v0: the API is unstable and may change without notice. rfw itself is pre-1.0, so the usual pre-1.0 rfw caveats apply here too.
go get github.com/rfwlab/uiAll DOM-touching code is behind //go:build js && wasm; build your app with
GOOS=js GOARCH=wasm.
ui.Mount("#app",
ui.Card().Title("Hello").Text("Built with rfw-ui."),
ui.Button("Save").Primary().OnClick(func() { /* ... */ }),
)ui.Mount injects the stylesheet and appends the nodes to the first element
matching the selector. Nodes also compose with composition.Bind:
composition.Bind("#app", func(e composition.El) {
e.Append(ui.Badge("beta").Primary())
})ui.Button("Save").Primary().OnClick(func() { save() })
ui.Button("Delete").Danger().Small()Label plus validation state; binds to a state.Signal[string] if you want.
name := ui.Input("Name").Placeholder("Ada Lovelace").OnInput(func(v string) {
if v == "" {
name.Invalid("Name is required")
} else {
name.Valid()
}
})Native <select> wrapper.
ui.Select("Runner").Options("wine", "proton").Value("wine").
OnChange(func(v string) { setRunner(v) })ui.Checkbox("Enable DXVK").Checked(true).OnChange(func(on bool) { toggle(on) })ui.Switch("Dark mode").On(false).OnChange(func(on bool) { setTheme(on) })ui.Card().Title("Settings").
Body(ui.Switch("Telemetry")).
Footer(ui.Button("Apply").Primary())Visibility is driven by a boolean signal; Open/Close are shorthands for
setting it.
m := ui.Modal("Confirm").Text("Delete this bottle?").
Footer(ui.Button("Delete").Danger().OnClick(func() { m.Close() }))
ui.Mount("body", m)
m.Open() // or m.Signal().Set(true)The active index lives in an int signal, available via Active().
ui.Tabs().
Tab("General", ui.Input("Name")).
Tab("Advanced", ui.Switch("Experimental"))Column definitions plus row data.
ui.Table(ui.Column{Title: "Name"}, ui.Column{Title: "Runner", Width: "10rem"}).
Rows([][]string{{"Photoshop", "wine"}, {"Steam", "proton"}}).
Striped()ui.Badge("beta").Primary()
ui.Badge("failing").Danger()ui.Spinner().Large().Label("Loading bottles")A toast-like inline alert.
ui.Notice("Settings saved.").Success().Dismissible()
ui.Notice("Connection lost.").Title("Network").Error()CSS-only, rendered from a data attribute on hover and focus.
ui.Tooltip("Runs the bottle", ui.Button("Run"))
ui.Tooltip("Shown below", ui.Badge("info")).Bottom()All components read from CSS custom properties declared on :root.
Override them anywhere in your own stylesheet (on :root, or scoped to a
container) to retheme every component at once:
:root {
--ui-accent: #4f46e5; /* primary buttons, switches, active tabs */
--ui-accent-fg: #ffffff; /* text on accent surfaces */
--ui-bg: #0f0f0f; /* control and card background */
--ui-fg: #ededed; /* default text */
--ui-muted: #9a9a9a; /* secondary text */
--ui-border: #2c2c2c; /* borders and dividers */
--ui-surface: #1a1a1a; /* subtle fills (hover, table header) */
--ui-danger: #f87171;
--ui-success: #4ade80;
--ui-warning: #facc15;
--ui-info: #60a5fa;
--ui-radius: 6px;
--ui-font: system-ui, sans-serif;
}The default palette is monochrome: black accent on white, with color reserved for danger, success, warning and info states.
GNU Affero General Public License v3.0, the same license as rfw. Copyright (C) Mirko Brombin. See LICENSE.