Skip to content

Theming

mike-ward edited this page May 17, 2026 · 1 revision

Theming

Go-gui's theme system controls every visual dimension of the framework: colors, typography, spacing, border radius, scrollbar size, and more. Themes are values, not classes — you can build, swap, and generate them at runtime.


Built-in themes

Six themes ship out of the box:

Variable Description
gui.ThemeDark Dark background, borderless (default)
gui.ThemeDarkNoPadding Dark, no widget padding
gui.ThemeDarkBordered Dark with visible borders
gui.ThemeLight Light background, borderless
gui.ThemeLightNoPadding Light, no widget padding
gui.ThemeLightBordered Light with visible borders

Set the theme once before creating windows — or swap it at runtime per window:

// At startup (global)
gui.SetTheme(gui.ThemeDarkBordered)

// Per-window, from any callback
w.SetTheme(gui.ThemeLight)

// Read the active theme anywhere
t := gui.CurrentTheme()

Typography tokens

The Theme struct exposes named text styles:

Token Weight Size
N1 Normal XLarge
N2 Normal Large
N3 Normal Medium
N4 Normal Small
N5 Normal XSmall
B1 Bold XLarge
B2 Bold Large
B3 Bold Medium

Use them on any Text or TextInput:

t := gui.CurrentTheme()

gui.Text(gui.TextCfg{Text: "Section header",  TextStyle: t.B2})
gui.Text(gui.TextCfg{Text: "Body paragraph",  TextStyle: t.N3})
gui.Text(gui.TextCfg{Text: "Caption",         TextStyle: t.N5})

The Opt[T] pattern

Widget config structs use Opt[T] for all numeric and stylistic fields. A zero value means "use the theme default"; gui.SomeF(v) or gui.Some(v) provides an explicit override:

gui.Button(gui.ButtonCfg{
    // radius and border from theme — no override needed
    Padding: gui.SomeP(8, 20, 8, 20),   // explicit padding
    Radius:  gui.SomeF(4),              // explicit corner radius
})

This makes widgets look consistent by default without requiring every call site to repeat spacing constants.


ThemePicker

gui.ThemePicker(gui.ThemePickerCfg{...}) is a built-in widget that lets users switch between registered themes at runtime. Drop it into a settings panel:

gui.ThemePicker(gui.ThemePickerCfg{
    ID:      "theme-picker",
    IDFocus: 50,
    Sizing:  gui.FillFit,
})

ThemePicker automatically lists every theme registered with the framework.


Custom themes with ThemeMaker

gui.ThemeMaker(cfg gui.ThemeCfg) gui.Theme builds a complete theme from a config struct. Every sizing, color, and typography decision is derived from a small set of seed values:

cfg := gui.ThemeDarkCfg          // start from a known baseline
cfg.ColorSelect = gui.Color{...} // accent colour
cfg.SizeBorder  = 1.5
cfg.Radius       = 6
myTheme := gui.ThemeMaker(cfg)
gui.SetTheme(myTheme)

Seed-color theme generation

The showcase demonstrates runtime theme generation from a single seed color. Pick a color and a palette strategy; the framework derives all accent, background, and text colors:

Strategy Palette rule
mono Variations of the seed hue
complement Seed + complementary hue
analogous Seed + two adjacent hues
triadic Three evenly-spaced hues
warm Seed shifted toward warm tones
cool Seed shifted toward cool tones
cfg := generateThemeCfg(
    seedColor,      // gui.Color — the starting hue
    "complement",   // strategy string
    true,           // isDark
    0,              // tint factor (0–100)
    textColor,      // override text colour (or zero)
    6,              // corner radius
    1.5,            // border width
)
w.SetTheme(gui.ThemeMaker(cfg))

generateThemeCfg is the helper shown in the showcase (demo_theme.go). In your own application you can call gui.ThemeMaker directly with any ThemeCfg you construct.


Color utilities

gui.Color is an RGBA struct. Useful constructors:

gui.ColorFromHex("#3a86ff")
gui.ColorFromHSV(h, s, v)
gui.White
gui.Black

Colors can be blended, lightened, darkened, and converted to/from HSV — useful when deriving palette variants programmatically.

Clone this wiki locally