theme

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: AGPL-3.0 Imports: 25 Imported by: 0

README

ui/theme

颜色、尺寸刻度、字体和系统动态效果偏好。用户用法与参数表见 主题。

文件 职责
theme.go、gofonts.go 默认文字样式、Gio Material 主题与兜底字体
palette.go 调色板副本、Apply / Scope、版本号与语义色
registry.go、themes/ 内置主题、注册表、JSON 解析
watch.go 主题目录轮询和当前主题热重载
scale.go 间距、圆角、字号和阴影刻度
fonts.go、fetch_*.go 字体加载与浏览器字体下载
motion.go 系统减少动画与应用覆盖值、滚动条偏好

依赖 Gio 和 ui/internal/loop。Apply 保留 Material 指针、字体和排版器,同步调色板、递增 Revision(),并请求所有窗口重绘;它不自行取得帧锁。Scope 临时切换颜色,不重绘、不递增版本号。

el、kit、window、markdown 读取主题。颜色和版本号的读写遵守 线程规则,组件缓存使用 cx.Cache 或把主题版本纳入键。

验证入口:go run ./examples/components -section theme -theme dark。

Documentation

Overview

Package theme holds the colors, text sizes and fonts every UI module reads. Apply changes the global palette under the UI frame lock.

Index

Constants

View Source
const (
	RadiusSm   = 4  // small marks: links, keycaps, compact chips
	RadiusMd   = 6  // controls: buttons, fields, menu items
	RadiusLg   = 8  // cards, popovers, menus, navigation rows
	RadiusXl   = 12 // dialogs, sheets, chat bubbles
	RadiusFull = 9999
)

Radius steps, in dp. RadiusFull makes a pill or a circle: el clamps a radius to half the shorter side.

View Source
const (
	TextXs      = 11 // axis labels, badges
	TextSm      = 12 // captions, errors, group titles
	TextMd      = 13 // secondary text
	TextControl = 14 // buttons, tabs, toggles
	TextBody    = 15 // body text
	TextLg      = 17 // panel and dialog titles
	TextXl      = 20 // page titles
	TextHeading = 22
)

Text size steps, in sp. BodySize, SmallSize and HeadingSize are TextBody, TextMd and TextHeading.

View Source
const (
	SpaceXxs = 2  // hairline gaps: stacked labels, tight icon pairs
	SpaceXs  = 4  // inside compact controls, between a label and its hint
	SpaceSm  = 6  // between an icon and its text
	SpaceMd  = 8  // between controls in a row, list rows
	SpaceLg  = 12 // control padding, between form fields
	SpaceXl  = 16 // card padding, between groups
	Space2xl = 24 // dialog and page padding, between sections
	Space3xl = 32 // between page regions
)

Spacing steps, in dp, for gaps, padding and margins. Most layouts need only Xs to Xl; off-scale values are for optical adjustments such as centering an icon.

View Source
const (
	BodySize    unit.Sp = 15
	SmallSize   unit.Sp = 13
	HeadingSize unit.Sp = 22

	// ControlHeight is the height of every single-line field: inputs,
	// selects, pickers and search boxes, so fields side by side line up.
	ControlHeight unit.Dp = 36
)

Text sizes.

View Source
const Face font.Typeface = "PingFang SC, Hiragino Sans GB, Microsoft YaHei, Noto Sans CJK SC, Noto Sans SC, Go"

Face lists font families in priority order. Pinning a CJK family avoids tofu from a system fallback font that lacks some simplified Chinese glyphs.

View Source
const MonoFace font.Typeface = "SF Mono, Menlo, Cascadia Mono, Consolas, DejaVu Sans Mono, Go Mono, PingFang SC, Microsoft YaHei, Noto Sans CJK SC"

MonoFace lists monospaced families for code, numbers in columns and keycaps; CJK falls back to Face's fonts.

Variables

View Source
var (
	ElevationSm = Elevation{Offset: 1, Blur: 3}   // cards that lift on hover
	ElevationMd = Elevation{Offset: 4, Blur: 12}  // popovers, menus, dropdowns, toasts
	ElevationLg = Elevation{Offset: 12, Blur: 32} // dialogs, sheets, command palette
)

Elevation steps: menus and popovers float a little, dialogs more.

View Source
var (
	Bg           = RGB(0xf5f6f8) // window background
	Surface      = RGB(0xffffff) // cards and fields
	Border       = RGB(0xe3e5e8) // borders and dividers
	Text         = RGB(0x1f2328) // body text
	Muted        = RGB(0x6b7280) // secondary text, hints, unchecked icons
	Primary      = RGB(0x2563eb) // primary buttons, links, focus, checked icons
	DangerText   = RGB(0xb91c1c) // danger text on surfaces
	PrimaryText  = RGB(0x1d4ed8) // text on selected surfaces
	CodeBg       = RGB(0xf0f1f3)
	CodeText     = RGB(0x1f2328)
	PrimaryHover = RGB(0x1d4ed8)
	DangerHover  = RGB(0xb91c1c)
	SubtleHover  = RGB(0xe2e5e9)
	Success      = RGB(0x15803d)                                   // positive status
	Warning      = RGB(0xa16207)                                   // caution status
	Info         = RGB(0x0369a1)                                   // informational status
	Danger       = RGB(0xdc2626)                                   // danger buttons
	Subtle       = RGB(0xeceef1)                                   // secondary buttons
	OnColor      = RGB(0xffffff)                                   // text on Primary and Danger
	Highlight    = RGB(0xdbeafe)                                   // selected rows and options
	Scrim        = color.NRGBA{A: 0x66}                            // dims the window behind a dialog
	Shadow       = color.NRGBA{R: 0x10, G: 0x18, B: 0x28, A: 0x2e} // tints raised surfaces' shadows
	// Chart is the categorical order for data series; see Palette.Chart.
	Chart = Light().Chart
	// BgGradient and PrimaryGradient are optional; see Palette.
	BgGradient, PrimaryGradient Gradient
)

Colors. Components read them at layout time.

View Source
var Material = newMaterial()

Material is the underlying Gio theme: text shaper and icons.

View Source
var ReducedMotion bool

ReducedMotion is the effective preference. It follows the system by default; SetReducedMotion gives the application an explicit override.

Functions

func Apply

func Apply(p Palette)

Apply synchronously replaces the global palette and redraws every window. Call before opening windows or from a UI callback under the frame lock. Other goroutines must use core.Update(func() { theme.Apply(p) }). Apply never acquires the frame lock itself, so callbacks cannot deadlock on it. Fonts, the text shaper and the Material pointer remain unchanged.

func CurrentName

func CurrentName() string

CurrentName is the theme last chosen with Use; "light" by default. Apply with an unnamed palette does not change it.

func FetchFonts

func FetchFonts(urls ...string) error

FetchFonts is for WebAssembly builds, where it downloads font files with the browser. Elsewhere it returns an error: read the files and LoadFonts.

func FollowSystemMotion

func FollowSystemMotion()

FollowSystemMotion removes the application override and applies the latest system value. Unsupported platforms default to allowing motion.

func LoadFonts

func LoadFonts(files ...[]byte) error

LoadFonts adds font files (TTF, OTF or TTC collections) to the text shaper and redraws every window. Text picks them by family name from Face, so load a family listed there, such as Noto Sans SC. A web build needs this: the browser gives a WebAssembly app no system fonts, and without a CJK font Chinese shows as boxes. Desktop apps can use it to ship a font.

Call it before window.Main or from a callback; from another goroutine wrap it in core.Update, like Apply.

func Names

func Names() []string

Names lists the registered palettes in registration order.

func NewShaper

func NewShaper(extra ...font.FontFace) *text.Shaper

NewShaper builds an independent shaper with extra faces ahead of the fonts loaded through LoadFonts and the Go fallback fonts. System font fallback follows the same platform defaults as Material.Shaper.

func RGB

func RGB(c uint32) color.NRGBA

RGB converts 0xRRGGBB to an opaque color.

func Register

func Register(name string, p Palette)

Register adds a named palette, or replaces one, for Named and Names; an app offers them in its theme picker. Built in: light, dark, nord, paper, solarized-dark, high-contrast and aurora (with gradients).

func Revision

func Revision() uint64

Revision changes whenever Apply replaces the palette. Use it in custom render cache keys. Read under the UI lock, like Current.

func Scope

func Scope(p Palette) (restore func())

Scope sets the palette for a part of the window and returns the function that puts the previous one back. el.Themed uses it around a subtree's Render and paint, so the rest of the window keeps its colors; it does not redraw anything. Call it under the frame lock and always restore.

func SetReducedMotion

func SetReducedMotion(reduce bool)

SetReducedMotion overrides the system preference. Call under the UI frame lock.

func SetSystemReducedMotion

func SetSystemReducedMotion(reduce bool)

SetSystemReducedMotion is the window backend's preference bridge. Native changes are remembered while an application override is active.

func SetSystemScrollbarsAutoHide added in v0.0.5

func SetSystemScrollbarsAutoHide(hide bool)

SetSystemScrollbarsAutoHide is the window backend's bridge for the platform's "hide scroll bars at rest" setting; el's ScrollbarSystem mode follows it. Safe from any goroutine.

func SystemScrollbarsAutoHide added in v0.0.5

func SystemScrollbarsAutoHide() bool

SystemScrollbarsAutoHide reports the platform's last known setting.

func Use

func Use(name string) error

Use applies the registered palette name and remembers it, so CurrentName reports it and a ThemeWatcher reapplies it when its file changes. Call it like Apply: before opening windows or under the frame lock.

Types

type Elevation

type Elevation struct{ Offset, Blur float32 }

Elevation is a soft shadow under a raised surface, in dp: Offset moves it down, Blur is how far it fades out. Its color is Shadow.

type Gradient

type Gradient struct {
	From, To color.NRGBA
	Angle    float32
}

Gradient is a linear blend from From to To. Angle is in degrees: 0 runs left to right, 90 top to bottom. The zero Gradient means none.

func (Gradient) IsZero

func (g Gradient) IsZero() bool

IsZero reports whether g is unset.

type Palette

type Palette struct {
	PrimaryText, DangerText, CodeBg, CodeText      color.NRGBA
	Bg, Surface, Border, Text, Muted               color.NRGBA
	Primary, PrimaryHover, Danger, DangerHover     color.NRGBA
	Success, Warning, Info                         color.NRGBA
	Subtle, SubtleHover, OnColor, Highlight, Scrim color.NRGBA
	// Shadow tints the shadows of raised surfaces (Elevation); dark themes
	// need a stronger one to read against a dark background.
	Shadow color.NRGBA
	// Chart is the categorical order for data series: slot i always means
	// series i. Validated for color-vision deficiency against Surface.
	Chart [8]color.NRGBA
	// BgGradient, when set, paints the window background instead of Bg;
	// PrimaryGradient paints primary buttons, progress bars and the user's
	// chat bubbles instead of Primary. Bg and Primary stay the solid colors
	// for everything else, so set them to a color from the gradient.
	BgGradient, PrimaryGradient Gradient
}

Palette contains every global color token. Start from Light or Dark when customizing: Apply replaces all colors, including zero (transparent) values.

func Current

func Current() Palette

Current returns a copy of the current colors. Read it under the UI lock, like the public color variables, or before opening the first window.

func Dark

func Dark() Palette

Dark returns an independent copy of the dark palette.

func Light

func Light() Palette

Light returns an independent copy of the default light palette.

func Named

func Named(name string) (Palette, bool)

Named returns a copy of a registered palette.

func ParseTheme

func ParseTheme(data []byte) (name string, p Palette, err error)

ParseTheme reads a theme file: a base palette ("light" or "dark", or any registered name) and the colors it changes, by Palette field name in any case, as #rgb, #rgba, #rrggbb or #rrggbbaa. Chart takes up to eight colors; bgGradient and primaryGradient take {"from", "to", "angle"}.

{"name": "Nord", "base": "dark",
 "colors": {"bg": "#2e3440", "surface": "#3b4252", "primary": "#88c0d0",
            "bgGradient": {"from": "#2e3440", "to": "#3b4252", "angle": 90}}}

type ThemeWatcher

type ThemeWatcher struct {
	// contains filtered or unexported fields
}

ThemeWatcher keeps the themes in a directory registered while their files change, e.g. so a designer sees a theme file update the running app.

func WatchThemes

func WatchThemes(dir string, interval time.Duration, onChange func(names []string, err error)) (*ThemeWatcher, error)

WatchThemes registers every *.json theme file in dir now, then checks the directory every interval (default one second) for added, changed or removed files. Changes are registered on the next frame, under the frame lock; if the theme in use (CurrentName) changed, it is applied again. onChange, if set, runs then too with the changed theme names, or with the error of a file that failed to parse; the other files still load.

It polls instead of using file system events, so it needs nothing but the standard library and works the same on every platform.

func (*ThemeWatcher) Stop

func (w *ThemeWatcher) Stop()

Stop ends the watching; registered themes stay.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL