Documentation
¶
Overview ¶
Package tela is the public API of Tela, a desktop UI framework written in pure Go: no cgo, no webview, a React-like component model and Tailwind CSS styling.
A UI is a tree of Node values built by function components. Elements (Div, Button, Text...) live in the companion package h, designed to be dot-imported; this package holds the application (App, Run), hooks and component boundaries (UseState, C, CP...), events (MouseEvent, PointerEvent, DragEvent, KeyboardEvent with the Key and Code constants, FocusEvent, and the ChangeEvent, InputEvent, CompositionEvent, SelectEvent and SubmitEvent of text fields and forms), ElementRef, the typed style props for h.Style, colors, and the Previews that `tela snapshot` and `tela inspect` render.
import (
"github.com/kuronosu/tela"
. "github.com/kuronosu/tela/h"
)
func Counter() tela.Node {
count, setCount := tela.UseState(0)
return Div(Class("flex flex-col items-center gap-6 p-10"),
H1(Class("text-5xl font-bold"), Textf("Count: %d", count)),
Button(Class("px-6 py-3 rounded-xl bg-indigo-500 text-white"),
OnClick(func(tela.MouseEvent) { setCount.Set(count + 1) }),
Text("Increment")),
)
}
func main() {
tela.Run(tela.App{Title: "Demo", Width: 800, Height: 600, Root: Counter})
}
Styling ¶
Static styling uses Tailwind v4 classes (h.Class). Dynamic values go through typed props, which apply after the classes:
Div(Class("rounded-xl p-4"), Style(tela.Width(w), tela.Bg(tela.Hex("#1e1e2e"))))
Values that change every frame (a drag position, a progress bar) can be deferred to a later phase with TranslateFn, OpacityFn, WidthFn and HeightFn: a UseValue read inside them redoes only that element's layout or paint when it changes. Colors are extended sRGB; see Color.
Components ¶
A component is a function returning a Node. Wrapped in C (no props) or CP (comparable props), it becomes a component boundary: an element with its own hook state, rebuilt on its own when that state changes. CP skips the build when its props are == to the previous ones and nothing it reads changed (automatic skipping, as in Jetpack Compose); Memo does the same with a comparison function. Calling a component function directly runs it inside the caller, as a custom hook.
Div(tela.C(Header), tela.CP(Card, CardProps{Title: "Inbox"}))
Hooks ¶
Hooks keep state and run code across builds, with React's names and semantics:
- UseState (and UseStateSet, UseStateUpdate) and UseReducer hold state; their Setter and Dispatcher are comparable, stable and safe from any goroutine.
- UseEffect runs code after the frame is presented and UseLayoutEffect after layout, with cleanups; UseEffect(f, nil) runs only on mount.
- UseMemo caches a computation; UseCallback gives a stable Callback that always calls the latest function; UseRef keeps a mutable box.
- CreateContext and UseContext pass values down the tree.
- UseId gives a stable unique ID.
- UseElementRef gives an ElementRef to attach with h.Ref, to focus or scroll an element from handlers and effects.
- UseValue holds an observable Value for what changes every frame: read in the component's body, a change builds it again; read inside a deferred style closure, a change only lays out or repaints that element.
- UseScroll gives Values that follow an element's scroll offsets, for effects tied to scrolling that repaint without rebuilding.
- UseBounds gives a Value of an element's bounds in the window (a Rect, moved by scrolling and transforms), a ResizeObserver that also sees position; in a handler, ElementRef.Bounds reads them.
Scrolling ¶
An element with overflow-auto or overflow-scroll (per axis: overflow-x-*, overflow-y-*) scrolls with the wheel, the touchpad (with a kinetic fling when the fingers lift), touch, the keyboard (arrows, Page Up/Down, Home/End, Space) and its scrollbars; overflow-hidden scrolls only from code. The window itself scrolls like a browser page when the content is taller or wider than it (App.RootClass "overflow-hidden" turns that off); h-screen and h-full on the outer element still mean the window's height. Wheel scrolling chains to the scrolling ancestors at an edge unless overscroll-contain stops it. Scrollbars are thin overlay bars that appear while scrolling; App.ClassicScrollbars (or the class scrollbar-classic) draws always-visible classic bars that take space. User scrolling animates; App.InstantScroll (or scroll-instant on an element) turns animation and flings off, scroll-smooth turns them back on, and the system's reduced-motion setting always does. Code scrolls with ElementRef's ScrollTo, ScrollBy and ScrollIntoView (animated with scroll-smooth, like CSS), and h.OnScroll and h.OnWheel observe it:
list := tela.UseElementRef()
_, y := tela.UseScroll(list)
header := Div(Class("h-16 bg-white shadow"), Style(tela.OpacityFn(func() float32 {
return 1 - min(y.Get()/200, 1) // repaints only
})))
return Div(Class("flex h-screen flex-col"), header,
Div(Ref(list), Class("grow overflow-y-auto"), rows),
Button(OnClick(func(tela.MouseEvent) { list.ScrollTo(0, 0) }), Text("Top")))
Long lists use h.VirtualList, which builds only the rows in view, keeps the scroll position stable while rows of unknown height are measured, and scrolls to a row with ElementRef's ScrollToIndex and a ScrollAlign.
Text can be deferred the same way with h.TextFn: a Value read inside it lays out only the paragraph that holds the text, without building the component (a clock, a time label next to a seek bar).
A Value moved from a handler or a goroutine repaints only the element whose closure reads it:
pos := tela.UseValue(0.0)
Div(Class("size-8 rounded-full bg-indigo-500"),
Style(tela.TranslateFn(func() (x, y float32) { return float32(pos.Get()), 0 })))
// later, from anywhere: pos.Set(120)
Hooks follow React's rules: call them at the top level of a component function, in the same order on every build, never inside conditions or loops. A hook called outside a component build panics with an explanation; in dev mode (TELA_DEV=1) a change of the hook order panics with both call sites. Everything runs on the UI thread except the Setter, Dispatcher and Value methods and Post, which are safe from any goroutine.
Text fields and forms ¶
h.Input and h.Textarea are editable text fields with the editing, selection, clipboard, undo and IME behavior of a browser's; h.Form submits on Enter or a submit button (h.OnSubmit), and h.Label names and focuses its field. Their events are ChangeEvent (every edit, React's onChange), InputEvent (before an edit, cancelable), CompositionEvent, SelectEvent and SubmitEvent; ElementRef reads and selects their text (Value, Select, SetSelectionRange, SelectionRange). See fields.go for an example.
Snapshots and inspection ¶
A package that defines `func Preview() tela.Node`, or a Previews variable, can be rendered headlessly with `tela snapshot`, and `tela inspect` prints its semantics tree as a Playwright aria snapshot (roles, names and states set by the h.Role and h.Aria... props or implied by the elements) or, with --layout, its layout tree. See the documentation of cmd/tela.
Stability ¶
Node, Child, Prop, StyleProp, Color, Shadow, Gradient, GradientStop, Setter, Dispatcher, Callback, Ref, Context, Value, ElementRef, Rect, ScrollAlign, MouseEvent, PointerEvent, DragEvent, KeyboardEvent, FocusEvent, WheelEvent, ScrollEvent, InputEvent, ChangeEvent, CompositionEvent, SelectEvent, SubmitEvent, Key, Code, KeyLocation, MouseButton, MouseButtons and PointerKind are aliases of types defined in Tela's internal packages (the packages cannot be imported, but their exported fields and methods are part of the API shown here). Construct them with the functions of this package rather than with composite literals where one exists.
Index ¶
- Constants
- Variables
- func Post(fn func())
- func Run(app App) error
- func UseContext[T any](ctx *Context[T]) T
- func UseEffect(effect func() func(), deps ...any)
- func UseId() string
- func UseLayoutEffect(effect func() func(), deps ...any)
- func UseMemo[T any](compute func() T, deps ...any) T
- func UseStateSet[T any](initial T) (T, func(T))
- func UseStateUpdate[T any](initial T) (T, func(func(T) T))
- type App
- type Callback
- type ChangeEvent
- type Child
- type Code
- type Color
- type CompositionEvent
- type Context
- type Dispatcher
- type DragEvent
- type ElementRef
- type FocusEvent
- type Gradient
- type GradientStop
- type InputEvent
- type Key
- type KeyLocation
- type KeyboardEvent
- type MouseButton
- type MouseButtons
- type MouseEvent
- type Node
- type PointerEvent
- type PointerKind
- type Previews
- type Prop
- type Rect
- type Ref
- type ScrollAlign
- type ScrollEvent
- type SelectEvent
- type Setter
- type Shadow
- type StyleProp
- func BackgroundGradient(g Gradient) StyleProp
- func Bg(c Color) StyleProp
- func BorderColor(c Color) StyleProp
- func BorderWidth(px float32) StyleProp
- func Bottom(px float32) StyleProp
- func BoxShadow(layers ...Shadow) StyleProp
- func FontSize(px float32) StyleProp
- func FontWeight(w float32) StyleProp
- func Gap(px float32) StyleProp
- func Height(px float32) StyleProp
- func HeightFn(fn func() float32) StyleProp
- func Left(px float32) StyleProp
- func Margin(px float32) StyleProp
- func MaxHeight(px float32) StyleProp
- func MaxWidth(px float32) StyleProp
- func MinHeight(px float32) StyleProp
- func MinWidth(px float32) StyleProp
- func Opacity(v float32) StyleProp
- func OpacityFn(fn func() float32) StyleProp
- func Padding(px float32) StyleProp
- func PaddingXY(x, y float32) StyleProp
- func Radius(px float32) StyleProp
- func Right(px float32) StyleProp
- func Rotate(deg float32) StyleProp
- func Scale(x, y float32) StyleProp
- func TextColor(c Color) StyleProp
- func Top(px float32) StyleProp
- func Translate(x, y float32) StyleProp
- func TranslateFn(fn func() (x, y float32)) StyleProp
- func Width(px float32) StyleProp
- func WidthFn(fn func() float32) StyleProp
- func ZIndex(z int) StyleProp
- type SubmitEvent
- type Value
- type WheelEvent
Examples ¶
Constants ¶
const ( // MouseButtonNone is no button, as in a move or enter event. MouseButtonNone = core.MouseButtonNone // MouseButtonLeft is the primary button: the left button (the right // one on a left-handed setup), a touch contact or a pen tip. MouseButtonLeft = core.MouseButtonLeft // MouseButtonRight is the secondary button. MouseButtonRight = core.MouseButtonRight // MouseButtonMiddle is the wheel button. MouseButtonMiddle = core.MouseButtonMiddle // MouseButtonBack is the "back" side button. MouseButtonBack = core.MouseButtonBack // MouseButtonForward is the "forward" side button. MouseButtonForward = core.MouseButtonForward )
The mouse buttons.
const ( PointerMouse = core.PointerMouse // a mouse or touchpad cursor PointerTouch = core.PointerTouch // a finger on a touchscreen PointerPen = core.PointerPen // a stylus )
The pointer kinds.
const ( // KeyLocationStandard is every key not listed below. KeyLocationStandard = core.KeyLocationStandard // KeyLocationLeft is the left Shift, Control, Alt or Meta key. KeyLocationLeft = core.KeyLocationLeft // KeyLocationRight is the right Shift, Control, Alt (AltGr) or Meta key. KeyLocationRight = core.KeyLocationRight // KeyLocationNumpad is a key of the numeric keypad. KeyLocationNumpad = core.KeyLocationNumpad )
The key locations (the DOM's KeyboardEvent.location).
const ( // AlignAuto scrolls as little as possible: not at all when the item // is fully visible, else to align its bottom (when it reaches past // the bottom of the viewport) or its top. AlignAuto = core.AlignAuto // AlignStart puts the item's top at the top of the viewport. AlignStart = core.AlignStart // AlignCenter centers the item in the viewport. AlignCenter = core.AlignCenter // AlignEnd puts the item's bottom at the bottom of the viewport. AlignEnd = core.AlignEnd )
The alignments of ElementRef.ScrollToIndex. The first and last items include the list's padding: AlignStart on item 0 scrolls to the very top, AlignEnd on the last item to the very bottom.
Variables ¶
var ( // Transparent is fully transparent black. Transparent = gfx.Transparent // Black is opaque black. Black = gfx.Black // White is opaque white. White = gfx.White )
Common colors.
Functions ¶
func Post ¶
func Post(fn func())
Post runs fn on the UI thread of the running application, in order with the state updates queued before and after it. It is safe from any goroutine; use it to touch UI-thread state (a Ref, a Callback's forwarder) from background work.
go func() {
data := fetch()
tela.Post(func() { cache.Current = data })
}()
Functions posted before Run starts run once its window is up. Headless trees (tests, snapshots) do not run posted functions unless they receive them (telatest.Mount does); until then they wait, in order.
func Run ¶
Run opens a window, shows app.Root and runs the event loop until the window is closed. It must be called from the main goroutine, normally from main. The window is drawn by Tela's own renderer on the native backend: Wayland or X11 on Linux, Win32 and Cocoa elsewhere.
Run returns an error when the platform cannot open a window (for example, no display is available). It panics when app.Root is nil.
func UseContext ¶
UseContext returns the value of the nearest Provider of ctx above the component, or the context's default when there is none.
theme := tela.UseContext(Theme)
func UseEffect ¶
func UseEffect(effect func() func(), deps ...any)
UseEffect runs effect after the frame is presented, when its dependencies changed since the previous build. effect may return a cleanup function (or nil), which runs before the effect runs again and when the component unmounts.
The dependencies follow React:
tela.UseEffect(f) // after every build tela.UseEffect(f, nil) // only after the first build (React's []) tela.UseEffect(f, id, n) // when id or n changed (compared with ==)
A single nil is a constant dependency, so the effect runs only on mount and its cleanup only on unmount: the idiom for subscriptions and timers.
tela.UseEffect(func() func() {
tick := time.NewTicker(time.Second)
done := make(chan struct{})
go func() {
for {
select {
case now := <-tick.C:
setNow.Set(now) // safe from any goroutine
case <-done:
return
}
}
}()
return func() { tick.Stop(); close(done) }
}, nil)
Values that cannot be compared (functions, slices, maps) count as changed on every build; dev mode (TELA_DEV=1) reports them, and a dependency count that changes between builds. Pass an ID, a comparable value or a Callback instead.
Within a frame, the effects of children run before their parents', and every cleanup runs before the effects that follow it. Effects of a component whose build was skipped do not run. State set by an effect is applied in a later frame.
func UseId ¶
func UseId() string
UseId returns a string that identifies this hook slot, for accessibility relations between elements (a label and its input). It is the same on every build, unique within the process, and the same for the same mount order (snapshots are deterministic).
id := tela.UseId()
func UseLayoutEffect ¶
func UseLayoutEffect(effect func() func(), deps ...any)
UseLayoutEffect is UseEffect, but the effect runs in the frame after layout and before paint, with the same dependencies and cleanups. Its state updates are applied in the next frame.
tela.UseLayoutEffect(func() func() {
log.Println("laid out")
return nil
}, nil)
func UseMemo ¶
UseMemo returns compute(), computed on the first build and again only when the dependencies changed (compared like UseEffect's: none recomputes on every build, a single nil never).
visible := tela.UseMemo(func() []Todo { return filter(todos, query) }, len(todos), query)
func UseStateSet ¶
func UseStateSet[T any](initial T) (T, func(T))
UseStateSet is UseState for those who only set whole values: it returns the Setter's Set method as a plain function, made once per slot.
name, setName := tela.UseStateSet("")
setName("Ada")
func UseStateUpdate ¶
func UseStateUpdate[T any](initial T) (T, func(func(T) T))
UseStateUpdate is UseState for those who only update from the previous state: it returns the Setter's Update method as a plain function, made once per slot.
count, update := tela.UseStateUpdate(0)
update(func(n int) int { return n + 1 })
Types ¶
type App ¶
type App struct {
// Title is the window title.
Title string
// AppID identifies the application to the desktop environment (the
// Wayland app_id, the X11 WM_CLASS). Use a reverse-DNS name such as
// "com.example.notes". Empty lets the platform pick one.
AppID string
// Width and Height are the requested size of the window in logical
// pixels. Zero means the platform's default size.
Width, Height float32
// RootClass are Tailwind classes for the root, which fills the
// window. Use it to give the window a background ("bg-slate-900") or
// a text style; without it the window is white and the text black.
// The root is the viewport: content taller or wider than the window
// scrolls, unless RootClass sets an overflow ("overflow-hidden" keeps
// the window from scrolling; CSS's overflow propagation).
RootClass string
// Root is the root component. It is built as a component of its own,
// so it may use hooks.
Root func() Node
// ClassicScrollbars draws always-visible classic scrollbars (a
// Windows look) that take space next to the content, instead of the
// default thin overlay bars that appear while scrolling. The classes
// scrollbar-classic and scrollbar-overlay choose per element.
ClassicScrollbars bool
// InstantScroll makes wheel, keyboard and scrollbar scrolling jump
// instead of animating, and touchpad and touch scrolling stop when
// the fingers lift instead of flinging. Elements with scroll-smooth
// still animate; the class scroll-instant does the same for one
// element.
InstantScroll bool
}
App describes an application window for Run.
tela.Run(tela.App{
Title: "Files",
Root: Root,
ClassicScrollbars: true, // always-visible classic bars (a Windows look)
InstantScroll: true, // no scroll animation, no inertia
})
type Callback ¶
Callback is a stable handle on the latest function given to UseCallback: cb.Fn() returns one forwarding function, the same on every build, that calls the latest function. It is comparable, so a props struct holding one stays comparable and CP can skip the component that receives it.
func UseCallback ¶
UseCallback stores fn as the latest function of this hook slot and returns the slot's Callback. cb.Fn() is one function of type F, the same on every build, that calls the latest fn when it is called (React's useEffectEvent): handlers keep a stable identity and still see the latest build's values. There are no dependencies. F must be a function type; UseCallback panics otherwise, and for a nil fn.
onSave := tela.UseCallback(func(e tela.MouseEvent) { save(draft) })
return tela.CP(Toolbar, ToolbarProps{OnSave: onSave}) // skips while draft changes
// in Toolbar:
Button(OnClick(p.OnSave.Fn()), Text("Save"))
Call the forwarder on the UI thread (handlers and effects run there); from other goroutines, use Post.
type ChangeEvent ¶
type ChangeEvent = core.ChangeEvent
ChangeEvent is passed to h.OnChange after every edit of a text field's value, like React's onChange (the DOM's input event, not its change on blur): Value is the new value (with the text an IME is composing), InputType, Data and IsComposing describe the edit as in InputEvent. It bubbles, so a Form's OnChange sees the edits of all its fields.
name, setName := tela.UseState("")
Input(Value(name), OnChange(func(e tela.ChangeEvent) { setName.Set(e.Value) }))
// the same: Input(Bind(name, setName))
type Child ¶
Child is what an element takes: a Node (a child) or a Prop (h.Class, h.Style, h.Key, h.OnClick...). Props and children may be mixed in any order. The interface is sealed: passing a string or any other value is a compile error; wrap text in h.Text.
type Code ¶
Code is a physical key: its position on the keyboard whatever the layout (a W3C KeyboardEvent code value), one of the Code constants. Use it for shortcuts that depend on the position (WASD), and Key for those that depend on the meaning (Ctrl+Z):
if e.Code == tela.CodeKeyW || e.Code == "ArrowUp" { moveUp() }
const ( // CodeUnidentified is a key without a W3C code value. CodeUnidentified Code = "Unidentified" // Writing system keys (alphanumeric section). The comments give the // US layout label. CodeBackquote Code = "Backquote" // `~ (半角/全角 on Japanese keyboards) CodeBackslash Code = "Backslash" // \| (# on UK layouts, row C) CodeBracketLeft Code = "BracketLeft" // [{ CodeBracketRight Code = "BracketRight" // ]} CodeComma Code = "Comma" // ,< CodeDigit0 Code = "Digit0" // 0) CodeDigit1 Code = "Digit1" // 1! CodeDigit2 Code = "Digit2" // 2@ CodeDigit3 Code = "Digit3" // 3# CodeDigit4 Code = "Digit4" // 4$ CodeDigit5 Code = "Digit5" // 5% CodeDigit6 Code = "Digit6" // 6^ CodeDigit7 Code = "Digit7" // 7& CodeDigit8 Code = "Digit8" // 8* CodeDigit9 Code = "Digit9" // 9( CodeEqual Code = "Equal" // =+ CodeIntlBackslash Code = "IntlBackslash" // between left Shift and Z on ISO keyboards CodeIntlRo Code = "IntlRo" // ろ, between / and right Shift (Japanese) CodeIntlYen Code = "IntlYen" // ¥, between = and Backspace (Japanese) CodeKeyA Code = "KeyA" CodeKeyB Code = "KeyB" CodeKeyC Code = "KeyC" CodeKeyD Code = "KeyD" CodeKeyE Code = "KeyE" CodeKeyF Code = "KeyF" CodeKeyG Code = "KeyG" CodeKeyH Code = "KeyH" CodeKeyI Code = "KeyI" CodeKeyJ Code = "KeyJ" CodeKeyK Code = "KeyK" CodeKeyL Code = "KeyL" CodeKeyM Code = "KeyM" CodeKeyN Code = "KeyN" CodeKeyO Code = "KeyO" CodeKeyP Code = "KeyP" CodeKeyQ Code = "KeyQ" CodeKeyR Code = "KeyR" CodeKeyS Code = "KeyS" CodeKeyT Code = "KeyT" CodeKeyU Code = "KeyU" CodeKeyV Code = "KeyV" CodeKeyW Code = "KeyW" CodeKeyX Code = "KeyX" CodeKeyY Code = "KeyY" CodeKeyZ Code = "KeyZ" CodeMinus Code = "Minus" // -_ CodePeriod Code = "Period" // .> CodeQuote Code = "Quote" // '" CodeSemicolon Code = "Semicolon" // ;: CodeSlash Code = "Slash" // /? // Functional keys of the alphanumeric section. CodeAltLeft Code = "AltLeft" CodeAltRight Code = "AltRight" // AltGr on many layouts CodeBackspace Code = "Backspace" CodeCapsLock Code = "CapsLock" CodeContextMenu Code = "ContextMenu" CodeControlLeft Code = "ControlLeft" CodeControlRight Code = "ControlRight" CodeEnter Code = "Enter" CodeMetaLeft Code = "MetaLeft" // Windows, Command (⌘) or Super key CodeMetaRight Code = "MetaRight" // Windows, Command (⌘) or Super key CodeShiftLeft Code = "ShiftLeft" CodeShiftRight Code = "ShiftRight" CodeSpace Code = "Space" CodeTab Code = "Tab" CodeConvert Code = "Convert" // 変換 (Japanese) CodeKanaMode Code = "KanaMode" // カタカナ/ひらがな/ローマ字 (Japanese) CodeLang1 Code = "Lang1" // 한/영 (Korean), かな (Apple Japanese) CodeLang2 Code = "Lang2" // 한자 (Korean), 英数 (Apple Japanese) CodeLang3 Code = "Lang3" // カタカナ (Japanese) CodeLang4 Code = "Lang4" // ひらがな (Japanese) CodeLang5 Code = "Lang5" // 半角/全角 (Japanese) CodeNonConvert Code = "NonConvert" // 無変換 (Japanese) // Control pad. CodeDelete Code = "Delete" CodeEnd Code = "End" CodeHelp Code = "Help" CodeHome Code = "Home" CodeInsert Code = "Insert" CodePageDown Code = "PageDown" CodePageUp Code = "PageUp" // Arrow pad. CodeArrowDown Code = "ArrowDown" CodeArrowLeft Code = "ArrowLeft" CodeArrowRight Code = "ArrowRight" CodeArrowUp Code = "ArrowUp" // Numpad section. CodeNumLock Code = "NumLock" CodeNumpad0 Code = "Numpad0" CodeNumpad1 Code = "Numpad1" CodeNumpad2 Code = "Numpad2" CodeNumpad3 Code = "Numpad3" CodeNumpad4 Code = "Numpad4" CodeNumpad5 Code = "Numpad5" CodeNumpad6 Code = "Numpad6" CodeNumpad7 Code = "Numpad7" CodeNumpad8 Code = "Numpad8" CodeNumpad9 Code = "Numpad9" CodeNumpadAdd Code = "NumpadAdd" CodeNumpadBackspace Code = "NumpadBackspace" CodeNumpadClear Code = "NumpadClear" CodeNumpadClearEntry Code = "NumpadClearEntry" CodeNumpadComma Code = "NumpadComma" CodeNumpadDecimal Code = "NumpadDecimal" CodeNumpadDivide Code = "NumpadDivide" CodeNumpadEnter Code = "NumpadEnter" CodeNumpadEqual Code = "NumpadEqual" CodeNumpadHash Code = "NumpadHash" CodeNumpadMemoryAdd Code = "NumpadMemoryAdd" CodeNumpadMemoryClear Code = "NumpadMemoryClear" CodeNumpadMemoryRecall Code = "NumpadMemoryRecall" CodeNumpadMemoryStore Code = "NumpadMemoryStore" CodeNumpadMemorySubtract Code = "NumpadMemorySubtract" CodeNumpadMultiply Code = "NumpadMultiply" CodeNumpadParenLeft Code = "NumpadParenLeft" CodeNumpadParenRight Code = "NumpadParenRight" CodeNumpadStar Code = "NumpadStar" CodeNumpadSubtract Code = "NumpadSubtract" // Function section. CodeEscape Code = "Escape" CodeF1 Code = "F1" CodeF2 Code = "F2" CodeF3 Code = "F3" CodeF4 Code = "F4" CodeF5 Code = "F5" CodeF6 Code = "F6" CodeF7 Code = "F7" CodeF8 Code = "F8" CodeF9 Code = "F9" CodeF10 Code = "F10" CodeF11 Code = "F11" CodeF12 Code = "F12" CodeF13 Code = "F13" CodeF14 Code = "F14" CodeF15 Code = "F15" CodeF16 Code = "F16" CodeF17 Code = "F17" CodeF18 Code = "F18" CodeF19 Code = "F19" CodeF20 Code = "F20" CodeF21 Code = "F21" CodeF22 Code = "F22" CodeF23 Code = "F23" CodeF24 Code = "F24" CodeFn Code = "Fn" CodeFnLock Code = "FnLock" CodePrintScreen Code = "PrintScreen" CodeScrollLock Code = "ScrollLock" CodePause Code = "Pause" // Media keys. CodeBrowserBack Code = "BrowserBack" CodeBrowserFavorites Code = "BrowserFavorites" CodeBrowserForward Code = "BrowserForward" CodeBrowserHome Code = "BrowserHome" CodeBrowserRefresh Code = "BrowserRefresh" CodeBrowserSearch Code = "BrowserSearch" CodeBrowserStop Code = "BrowserStop" CodeEject Code = "Eject" CodeLaunchApp1 Code = "LaunchApp1" // often "My Computer" CodeLaunchApp2 Code = "LaunchApp2" // often "Calculator" CodeLaunchMail Code = "LaunchMail" CodeMediaPlayPause Code = "MediaPlayPause" CodeMediaSelect Code = "MediaSelect" CodeMediaStop Code = "MediaStop" CodeMediaTrackNext Code = "MediaTrackNext" CodeMediaTrackPrevious Code = "MediaTrackPrevious" CodePower Code = "Power" CodeSleep Code = "Sleep" CodeAudioVolumeDown Code = "AudioVolumeDown" CodeAudioVolumeMute Code = "AudioVolumeMute" CodeAudioVolumeUp Code = "AudioVolumeUp" CodeWakeUp Code = "WakeUp" // Legacy, non-standard and special keys. CodeHyper Code = "Hyper" CodeSuper Code = "Super" CodeTurbo Code = "Turbo" CodeAbort Code = "Abort" CodeResume Code = "Resume" CodeSuspend Code = "Suspend" CodeAgain Code = "Again" CodeCopy Code = "Copy" CodeCut Code = "Cut" CodeFind Code = "Find" CodeOpen Code = "Open" CodePaste Code = "Paste" CodeProps Code = "Props" CodeSelect Code = "Select" CodeUndo Code = "Undo" CodeHiragana Code = "Hiragana" CodeKatakana Code = "Katakana" )
Physical key codes: the values of KeyboardEvent.Code (W3C UI Events KeyboardEvent code values, https://www.w3.org/TR/uievents-code/), the position of the key whatever the layout: CodeKeyA is the key labeled A on a US keyboard (it types "q" on AZERTY). Code is a string type: e.Code == "KeyS" and e.Code == tela.CodeKeyS mean the same.
type Color ¶
Color is a non-premultiplied color with float32 components R, G, B and A. A (opacity) is in [0, 1]. R, G and B are extended sRGB, like CSS Color 4: they are in [0, 1] for colors inside the sRGB gamut, while a color outside it, such as a third of the Tailwind palette once converted from OKLCH, keeps components below 0 or above 1 and is clipped when painted, as browsers do. Clamp before converting to 8-bit values. The zero value is transparent black.
Color is an alias of an internal type: use its fields and methods (WithAlpha, MulAlpha, String...) and the constructors Hex, RGB, RGBA and OKLCH.
func Hex ¶
Hex parses a CSS hex color: "#rgb", "#rgba", "#rrggbb" or "#rrggbbaa". It panics with the reason when s is not valid, since a literal that does not parse is a programmer error.
tela.Hex("#1e1e2e")
Example ¶
package main
import (
"fmt"
"github.com/kuronosu/tela"
)
func main() {
c := tela.Hex("#1e1e2e")
fmt.Println(c.R == tela.RGB(30, 30, 46).R, c)
}
Output: true #1e1e2e
func OKLCH ¶
OKLCH returns the opaque color with OKLCH lightness l (0 to 1), chroma c (about 0 to 0.4) and hue h in degrees, the notation of the Tailwind v4 palette: tela.OKLCH(0.637, 0.237, 25.331) is red-500. Use WithAlpha for transparency. The result is not gamut-mapped (see Color).
type CompositionEvent ¶
type CompositionEvent = core.CompositionEvent
CompositionEvent is passed to h.OnCompositionStart, h.OnCompositionUpdate and h.OnCompositionEnd while an input method (IME: Japanese, Chinese, Korean...) composes text in a text field: Data is the text being composed. It bubbles.
type Context ¶
Context passes a value down the tree without props: see CreateContext.
func CreateContext ¶
CreateContext returns a new context whose UseContext outside any Provider returns def. Create contexts once, at package level.
var Theme = tela.CreateContext("light")
func App() tela.Node {
theme, _ := tela.UseState("dark")
return Theme.Provider(theme, tela.C(Page))
}
ctx.Provider(value, children...) returns a node without a box that gives value to its children's subtrees (only h.Key is accepted as a prop). When the Provider gets a value that is not == to the previous one (values that cannot be compared always count as changed), only the components that read it with UseContext build again, even when the components between them skip their build.
type Dispatcher ¶
type Dispatcher[A any] = core.Dispatcher[A]
Dispatcher sends actions to one UseReducer slot: d.Dispatch(action). Like Setter, it is comparable, stable for its slot and safe from any goroutine.
func UseReducer ¶
func UseReducer[S, A any](reducer func(S, A) S, initial S) (S, Dispatcher[A])
UseReducer returns the state of this hook slot and a Dispatcher whose actions update it with reducer (React's useReducer). The first build stores initial. Queued actions apply in order on the UI thread with the reducer of the latest build, each receiving the state left by the previous one; reducer must be pure. For comparable states, a result == to the current state does not rebuild.
type action struct{ kind string }
func reduce(n int, a action) int {
switch a.kind {
case "inc":
return n + 1
case "reset":
return 0
}
return n
}
count, d := tela.UseReducer(reduce, 0)
d.Dispatch(action{"inc"})
type DragEvent ¶
DragEvent is passed to h.OnDragStart, h.OnDrag and h.OnDragEnd: the position, the movement since the previous drag event (DeltaX, DeltaY) and since the press (OffsetX, OffsetY), and the velocity in pixels per second (VelocityX, VelocityY; meaningful at the end).
type ElementRef ¶
type ElementRef = core.ElementRef
ElementRef is a handle on the element it is attached to with h.Ref, returned by UseElementRef: the same comparable value on every build. Its methods act on the element while it is mounted and do nothing (or report false) otherwise; call them from event handlers and effects.
- Focus focuses the element, if it is focusable (a Button, an h.Input or h.Textarea, or an element with h.TabIndex, not disabled or hidden).
- Blur removes the focus from the element if it has it.
- Focused reports whether the element has the focus of its window.
- ScrollTo(x, y) and ScrollBy(dx, dy) scroll a scrolling element (overflow auto, scroll or hidden) to or by offsets, clamped to its content; ScrollOffset returns its offsets (the DOM's scrollLeft and scrollTop). They jump, or animate when the element has scroll-smooth, and do nothing (or return 0) for other elements.
- ScrollIntoView scrolls every scrolling ancestor of the element so that it is visible, each as little as possible (block and inline "nearest").
- Bounds returns the element's border box in window coordinates as a Rect, moved by scrolling and transforms (getBoundingClientRect); the zero Rect before it is laid out. UseBounds follows it in a Value.
- ScrollToIndex(i, align) scrolls an h.VirtualList to item i, placed as ScrollAlign says, and keeps it there while the items around it are measured; it does nothing for other elements.
- Text fields (h.Input, h.Textarea): Value returns the value (the DOM's input.value: mainly for uncontrolled fields), Select selects all of it, SetSelectionRange(start, end) selects part of it and SelectionRange returns the selection, in UTF-8 byte offsets of the value (HTML's select(), setSelectionRange, selectionStart and selectionEnd); on other elements they do nothing and return "" or 0, 0.
For example, "/" focuses a search box:
search := tela.UseElementRef()
return Div(OnKeyDown(func(e tela.KeyboardEvent) {
if e.Key == "/" && !search.Focused() {
e.PreventDefault()
search.Focus()
search.Select()
}
}),
Input(Ref(search), Type("search"), Placeholder("Search"), Class("rounded-md border px-2 py-1")),
)
A "back to top" button for a list:
list := tela.UseElementRef()
return Div(Class("relative"),
Div(Ref(list), Class("h-96 overflow-y-auto scroll-smooth"), rows),
Button(OnClick(func(tela.MouseEvent) { list.ScrollTo(0, 0) }), Text("Top")))
func UseElementRef ¶
func UseElementRef() ElementRef
UseElementRef returns the ElementRef of this hook slot, the same on every build. Attach it to an element with h.Ref, then act on the element from handlers and effects (React's useRef with a DOM element):
func Search() tela.Node {
input := tela.UseElementRef()
tela.UseEffect(func() func() {
input.Focus()
return nil
}, nil)
return Div(Ref(input), TabIndex(0), Class("rounded-md p-2 focus:ring-2"), Text("Search"))
}
type FocusEvent ¶
type FocusEvent = core.FocusEvent
FocusEvent is passed to h.OnFocus and h.OnBlur. Like React's onFocus and onBlur, focus events bubble: a container's OnFocus also runs when one of its descendants gains the focus.
Div(OnFocus(func(tela.FocusEvent) { setEditing.Set(true) }),
OnBlur(func(tela.FocusEvent) { setEditing.Set(false) }),
...)
type Gradient ¶
Gradient is a background gradient; build one with LinearGradient.
func LinearGradient ¶
func LinearGradient(angleDeg float32, stops ...GradientStop) Gradient
LinearGradient returns a linear gradient in the direction of angleDeg: 0 points up, 90 to the right and 180 (CSS's default) down. Colors interpolate in Oklab, like Tailwind v4.
type GradientStop ¶
GradientStop is a color stop of a gradient; build one with Stop or StopAt.
func Stop ¶
func Stop(c Color) GradientStop
Stop returns a gradient stop whose position is spread automatically between its neighbors, as in CSS.
func StopAt ¶
func StopAt(c Color, percent float32) GradientStop
StopAt returns a gradient stop at a percentage (0 to 100) of the gradient line.
type InputEvent ¶
type InputEvent = core.InputEvent
InputEvent is passed to h.OnBeforeInput before a text field's value changes by an edit of the user (the DOM's beforeinput): InputType names the edit with the W3C Input Events names ("insertText", "insertFromPaste", "insertLineBreak", "deleteContentBackward", "deleteWordBackward", "deleteByCut", "historyUndo", "insertCompositionText"...), Data is the inserted text (empty for deletions), IsComposing reports an IME composition. PreventDefault cancels the edit, except during a composition and for the text an IME deletes around the caret. It bubbles.
// Digits only.
Input(OnBeforeInput(func(e tela.InputEvent) {
if strings.Trim(e.Data, "0123456789") != "" {
e.PreventDefault()
}
}))
type Key ¶
Key is a logical key: what the key means with the active layout and modifiers (a W3C KeyboardEvent key value). It is a named key, one of the Key constants ("Enter", "Escape", "ArrowLeft", "Shift"), or the character the key types ("a", "A", "é", " " for the space bar). Ctrl does not change it: Ctrl+S has Key "s". Compare it with a constant or a string literal:
if e.Key == tela.KeyEnter || e.Key == "+" { ... }
const ( // KeyUnidentified is a key with neither a name nor a character. KeyUnidentified Key = "Unidentified" // Modifier keys. AltGraph is AltGr (ISO_Level3_Shift); Meta is the // Windows, Command (⌘) or Super key, as browsers report it on every // OS. KeyAlt Key = "Alt" KeyAltGraph Key = "AltGraph" KeyCapsLock Key = "CapsLock" KeyControl Key = "Control" KeyFn Key = "Fn" KeyFnLock Key = "FnLock" KeyMeta Key = "Meta" KeyNumLock Key = "NumLock" KeyScrollLock Key = "ScrollLock" KeyShift Key = "Shift" KeySymbol Key = "Symbol" KeySymbolLock Key = "SymbolLock" KeyHyper Key = "Hyper" KeySuper Key = "Super" // Whitespace keys (the space bar is the character " "). KeyEnter Key = "Enter" KeyTab Key = "Tab" // Navigation keys. KeyArrowDown Key = "ArrowDown" KeyArrowLeft Key = "ArrowLeft" KeyArrowRight Key = "ArrowRight" KeyArrowUp Key = "ArrowUp" KeyEnd Key = "End" KeyHome Key = "Home" KeyPageDown Key = "PageDown" KeyPageUp Key = "PageUp" // Editing keys. KeyBackspace Key = "Backspace" KeyClear Key = "Clear" KeyCopy Key = "Copy" KeyCrSel Key = "CrSel" KeyCut Key = "Cut" KeyDelete Key = "Delete" KeyEraseEof Key = "EraseEof" KeyExSel Key = "ExSel" KeyInsert Key = "Insert" KeyPaste Key = "Paste" KeyRedo Key = "Redo" KeyUndo Key = "Undo" // UI keys. KeyAccept Key = "Accept" KeyAgain Key = "Again" KeyAttn Key = "Attn" KeyCancel Key = "Cancel" KeyContextMenu Key = "ContextMenu" KeyEscape Key = "Escape" KeyExecute Key = "Execute" KeyFind Key = "Find" KeyHelp Key = "Help" KeyPause Key = "Pause" KeyPlay Key = "Play" KeyProps Key = "Props" KeySelect Key = "Select" KeyZoomIn Key = "ZoomIn" KeyZoomOut Key = "ZoomOut" // Device keys. KeyBrightnessDown Key = "BrightnessDown" KeyBrightnessUp Key = "BrightnessUp" KeyEject Key = "Eject" KeyLogOff Key = "LogOff" KeyPower Key = "Power" KeyPowerOff Key = "PowerOff" KeyPrintScreen Key = "PrintScreen" KeyHibernate Key = "Hibernate" KeyStandby Key = "Standby" KeyWakeUp Key = "WakeUp" // IME and composition keys. Dead is a dead key (a pending accent or // other combining mark; the next key types the result). KeyAllCandidates Key = "AllCandidates" KeyAlphanumeric Key = "Alphanumeric" KeyCodeInput Key = "CodeInput" KeyCompose Key = "Compose" KeyConvert Key = "Convert" KeyDead Key = "Dead" KeyFinalMode Key = "FinalMode" KeyGroupFirst Key = "GroupFirst" KeyGroupLast Key = "GroupLast" KeyGroupNext Key = "GroupNext" KeyGroupPrevious Key = "GroupPrevious" KeyModeChange Key = "ModeChange" KeyNextCandidate Key = "NextCandidate" KeyNonConvert Key = "NonConvert" KeyPreviousCandidate Key = "PreviousCandidate" KeyProcess Key = "Process" KeySingleCandidate Key = "SingleCandidate" KeyHangulMode Key = "HangulMode" KeyHanjaMode Key = "HanjaMode" KeyJunjaMode Key = "JunjaMode" KeyEisu Key = "Eisu" KeyHankaku Key = "Hankaku" KeyHiragana Key = "Hiragana" KeyHiraganaKatakana Key = "HiraganaKatakana" KeyKanaMode Key = "KanaMode" KeyKanjiMode Key = "KanjiMode" KeyKatakana Key = "Katakana" KeyRomaji Key = "Romaji" KeyZenkaku Key = "Zenkaku" KeyZenkakuHankaku Key = "ZenkakuHankaku" // General-purpose function keys. KeyF1 Key = "F1" KeyF2 Key = "F2" KeyF3 Key = "F3" KeyF4 Key = "F4" KeyF5 Key = "F5" KeyF6 Key = "F6" KeyF7 Key = "F7" KeyF8 Key = "F8" KeyF9 Key = "F9" KeyF10 Key = "F10" KeyF11 Key = "F11" KeyF12 Key = "F12" KeyF13 Key = "F13" KeyF14 Key = "F14" KeyF15 Key = "F15" KeyF16 Key = "F16" KeyF17 Key = "F17" KeyF18 Key = "F18" KeyF19 Key = "F19" KeyF20 Key = "F20" KeyF21 Key = "F21" KeyF22 Key = "F22" KeyF23 Key = "F23" KeyF24 Key = "F24" // Multimedia keys. KeyClose Key = "Close" KeyMailForward Key = "MailForward" KeyMailReply Key = "MailReply" KeyMailSend Key = "MailSend" KeyMediaClose Key = "MediaClose" KeyMediaFastForward Key = "MediaFastForward" KeyMediaPause Key = "MediaPause" KeyMediaPlay Key = "MediaPlay" KeyMediaPlayPause Key = "MediaPlayPause" KeyMediaRecord Key = "MediaRecord" KeyMediaRewind Key = "MediaRewind" KeyMediaStop Key = "MediaStop" KeyMediaTrackNext Key = "MediaTrackNext" KeyMediaTrackPrevious Key = "MediaTrackPrevious" KeyNew Key = "New" KeyOpen Key = "Open" KeyPrint Key = "Print" KeySave Key = "Save" KeySpellCheck Key = "SpellCheck" // Audio keys. KeyAudioVolumeDown Key = "AudioVolumeDown" KeyAudioVolumeUp Key = "AudioVolumeUp" KeyAudioVolumeMute Key = "AudioVolumeMute" KeyMicrophoneVolumeMute Key = "MicrophoneVolumeMute" // Application launch keys. KeyLaunchApplication1 Key = "LaunchApplication1" KeyLaunchApplication2 Key = "LaunchApplication2" KeyLaunchCalendar Key = "LaunchCalendar" KeyLaunchMail Key = "LaunchMail" KeyLaunchMediaPlayer Key = "LaunchMediaPlayer" KeyLaunchMusicPlayer Key = "LaunchMusicPlayer" KeyLaunchPhone Key = "LaunchPhone" KeyLaunchScreenSaver Key = "LaunchScreenSaver" KeyLaunchSpreadsheet Key = "LaunchSpreadsheet" KeyLaunchWebBrowser Key = "LaunchWebBrowser" KeyLaunchWebCam Key = "LaunchWebCam" KeyLaunchWordProcessor Key = "LaunchWordProcessor" // Browser keys. KeyBrowserBack Key = "BrowserBack" KeyBrowserFavorites Key = "BrowserFavorites" KeyBrowserForward Key = "BrowserForward" KeyBrowserHome Key = "BrowserHome" KeyBrowserRefresh Key = "BrowserRefresh" KeyBrowserSearch Key = "BrowserSearch" KeyBrowserStop Key = "BrowserStop" )
Named keys: the values of KeyboardEvent.Key that are not characters (W3C UI Events KeyboardEvent key values, https://www.w3.org/TR/uievents-key/). A key that produces a character has that character as its Key ("a", "A", "1", "é", and " " for the space bar), so compare with a string literal: e.Key == "z". Key is a string type: e.Key == "Enter" and e.Key == tela.KeyEnter mean the same.
type KeyLocation ¶
type KeyLocation = core.KeyLocation
KeyLocation tells apart keys with the same meaning: the left and right Shift, Control, Alt and Meta keys, and the numeric keypad.
type KeyboardEvent ¶
type KeyboardEvent = core.KeyboardEvent
KeyboardEvent is passed to h.OnKeyDown and h.OnKeyUp: the logical Key, the physical Code, its Location, Repeat for a keydown generated while the key is held, and the modifiers held after the event (Shift, Ctrl, Alt, Meta; Meta is the Windows, Command or Super key). StopPropagation stops the bubbling; PreventDefault cancels the key's default action (Tab's focus move, the click of Enter or Space on a button, the text a key types into a text field). Typed text reaches text fields (h.Input, h.Textarea), which report it with h.OnChange.
OnKeyDown(func(e tela.KeyboardEvent) {
switch {
case e.Key == tela.KeyArrowDown:
e.PreventDefault()
setIndex.Update(func(i int) int { return min(i+1, n-1) })
case e.Ctrl && e.Key == "a":
selectAll()
}
})
type MouseButton ¶
type MouseButton = core.MouseButton
MouseButton is a mouse button, or the touch contact or pen tip that stands for the primary one.
type MouseButtons ¶
type MouseButtons = core.MouseButtons
MouseButtons is a set of held buttons: e.Buttons.Has(tela.MouseButtonLeft).
type MouseEvent ¶
type MouseEvent = core.MouseEvent
MouseEvent is passed to the mouse handlers (h.OnClick, h.OnMouseDown, h.OnContextMenu...). X and Y are window coordinates in logical pixels; LocalX and LocalY are relative to the border box of the element whose handler runs. ClickCount is 1 for a click, 2 for the second click of a double click. StopPropagation stops the bubbling; PreventDefault marks the event (on h.OnPointerDown it suppresses the press's mouse events, not the click).
type Node ¶
Node is an immutable, cheap description of a piece of UI: an element (h.Div), a text (h.Text), a component or a fragment. Function components return one, and every build creates new ones; the persistent state lives elsewhere, keyed by position and key (ARCHITECTURE section 6).
The zero Node renders nothing. As a child of an element it keeps the positions of its siblings, so h.If(cond, node) does not remount the siblings when cond changes.
func C ¶
C returns a node that builds fn as a component with its own state: a component boundary (ARCHITECTURE section 7). Calling fn() directly instead runs it inside the caller's build, as a custom hook whose hooks belong to the caller.
Div(tela.C(Header), tela.C(Body))
A C component builds whenever its parent builds, since fn may capture values that changed; use CP for automatic skipping. Only the very same node (one kept in a variable or a UseMemo) is not built again.
func CP ¶
func CP[P comparable](fn func(P) Node, props P) Node
CP returns a node that builds fn(props) as a component with its own state. When its parent builds again with props == to the previous ones, and neither its state nor a context it reads changed, the build is skipped (Jetpack Compose's automatic skipping): free memoization.
type CardProps struct {
Title string
OnOpen tela.Callback[func(tela.MouseEvent)]
}
func Card(p CardProps) tela.Node { ... }
tela.CP(Card, CardProps{Title: "Inbox", OnOpen: onOpen})
Give CP a named function and pass everything it uses in its props: variables captured by a closure are not compared, so dev mode (TELA_DEV=1) reports anonymous functions once per call site. Keep props comparable by passing Callback values instead of funcs, and IDs instead of slices or maps; a props value whose == panics counts as changed.
type PointerEvent ¶
type PointerEvent = core.PointerEvent
PointerEvent is passed to the pointer handlers (h.OnPointerDown..., h.OnLongPress): the mouse fields plus the pointer's Kind, PointerID, Pressure and IsPrimary.
type Previews ¶
Previews are the named screens of a package, for `tela snapshot` and `tela inspect` (ARCHITECTURE section 17). Define one as a package-level variable called Previews:
var Previews = tela.Previews{
"Login": LoginScreen,
"Settings": SettingsScreen,
}
Each function builds a screen as a component, so it may use hooks. The CLI renders the preview selected with --preview NAME; without the flag it renders the first name in byte order (see Previews.Names), so a name such as "1 Login" can pick the default. A package with a single screen can define `func Preview() tela.Node` instead.
Example ¶
package main
import (
"fmt"
"github.com/kuronosu/tela"
"github.com/kuronosu/tela/internal/core"
)
func main() {
login := func() tela.Node { return core.Text("Login") }
settings := func() tela.Node { return core.Text("Settings") }
previews := tela.Previews{"Settings": settings, "Login": login}
// `tela snapshot` renders the first name unless --preview says otherwise.
fmt.Println(previews.Names())
}
Output: [Login Settings]
type Prop ¶
Prop is a property of an element: classes, typed style, key, event handlers, an image source. Built by the functions of package h.
type Rect ¶
Rect is a rectangle in logical pixels: the position of its top-left corner (X, Y) and its size (Width, Height). Right, Bottom and Contains derive from them. It is what ElementRef.Bounds and UseBounds return, in window coordinates (the window's top-left corner is 0, 0), like the DOM's DOMRect.
type Ref ¶
Ref is a mutable box returned by UseRef: the same pointer on every build. Changing Current does not rebuild.
func UseRef ¶
UseRef returns the Ref of this hook slot, a box whose Current field persists across builds; the first build sets it to initial. Changing Current does not rebuild: use it for values that rendering does not show, such as a timer or the previous value of a prop.
renders := tela.UseRef(0) renders.Current++
type ScrollAlign ¶
type ScrollAlign = core.ScrollAlign
ScrollAlign is where ElementRef.ScrollToIndex puts an item of an h.VirtualList in its viewport (TanStack Virtual's align).
type ScrollEvent ¶
type ScrollEvent = core.ScrollEvent
ScrollEvent is passed to h.OnScroll after an element's scroll offsets changed, by the user or by code (the DOM's scroll event: at most once per frame, on the scrolling element only; it does not bubble). The fields are the DOM's scroll properties in logical pixels: ScrollLeft and ScrollTop (the offsets), ScrollWidth and ScrollHeight (the content size), ClientWidth and ClientHeight (the visible size).
Div(Class("h-96 overflow-y-auto"), OnScroll(func(e tela.ScrollEvent) {
if e.ScrollTop+e.ClientHeight >= e.ScrollHeight-200 {
loadMore()
}
}), rows)
type SelectEvent ¶
type SelectEvent = core.SelectEvent
SelectEvent is passed to h.OnSelect when the user selects text in a text field or code changes its selection: Start and End are the selection in UTF-8 byte offsets of the value. It bubbles.
type Setter ¶
Setter sets the state of one UseState slot. It is a small comparable value, the same on every build of the slot, so it can be stored, passed to goroutines and put in props compared by CP. Its methods are safe from any goroutine: they queue the update and wake the UI thread, which applies the queued updates in order before the next build. Updates to a component that has been unmounted are dropped. The zero Setter is not tied to any state; its methods panic.
setCount.Set(0)
setCount.Update(func(n int) int { return n + 1 })
Set(v) replaces the state; Update(f) computes it from the state left by the previous queued update, so two queued Update(inc) add 2. f runs on the UI thread and must be pure. For comparable types, a new state == to the current one does not rebuild.
func UseState ¶
UseState returns the state of this hook slot and its Setter (ARCHITECTURE section 7). The first build stores initial; later builds return the latest value set. Setting a different value rebuilds the component in the next frame.
func Counter() tela.Node {
count, setCount := tela.UseState(0)
return Button(OnClick(func(tela.MouseEvent) { setCount.Set(count + 1) }),
Textf("Clicked %d times", count))
}
Use setCount.Update(func(n int) int { return n + 1 }) when the new state depends on the old one and several updates may be queued before the next build (from goroutines, or several events in one frame).
Hooks follow React's rules: call them at the top level of a component function, in the same order on every build, never inside conditions or loops. UseState panics when called outside a component build or when the call order changed since the previous build.
type Shadow ¶
Shadow is one box shadow layer: OffsetX, OffsetY, Blur and Spread in logical pixels, its Color, and Inset for an inner shadow.
type StyleProp ¶
StyleProp is one typed style declaration, built by the functions below and passed to h.Style. Typed props apply after the element's classes, so they win over a class that sets the same property; use them for values that come from code (a measured width, a color from state). Lengths are logical pixels.
func BackgroundGradient ¶
BackgroundGradient sets a gradient as the background image, over the background color.
func BorderColor ¶
BorderColor sets the color of the four borders.
func BorderWidth ¶
BorderWidth sets the width of the four borders in pixels.
func FontWeight ¶
FontWeight sets the font weight, from 100 to 900.
func HeightFn ¶
HeightFn defers Height, in pixels, to the layout phase: a change of the Values fn reads lays the element out again, without building it.
func OpacityFn ¶
OpacityFn defers Opacity to the paint phase: a change of the Values fn reads repaints the element, without layout.
func TranslateFn ¶
TranslateFn defers Translate to the paint phase: a change of the Values fn reads repaints the element, without layout.
Style(tela.TranslateFn(func() (x, y float32) { return float32(pos.Get()), 0 }))
type SubmitEvent ¶
type SubmitEvent = core.SubmitEvent
SubmitEvent is passed to h.OnSubmit when a h.Form is submitted: Enter in a single-line field of the form, or a click on a submit button inside it. Tela does not navigate; PreventDefault exists for familiarity.
Form(OnSubmit(func(e tela.SubmitEvent) {
e.PreventDefault()
login(email, password)
}), ...)
type Value ¶
Value is an observable value held by one UseValue hook slot: v.Get() reads it, v.Set(x) and v.Update(f) change it. The *Value is the same pointer on every build, so it is comparable and can be passed in props compared by CP or through a context. Set and Update are safe from any goroutine: they queue the change, in order with the state updates, and the UI thread applies it before the next frame. Get is safe from any goroutine too and returns the value the UI thread applied last (a change still in the queue is not visible yet); Latest returns the value as it will be once the queued changes apply, which is what an event handler that has just called Set wants to read. Latest never subscribes anything.
What a change redoes depends on where Get was called; see UseValue. For comparable types, a value == to the current one changes nothing.
func UseBounds ¶
func UseBounds(ref ElementRef) *Value[Rect]
UseBounds returns a Value that follows the bounds of the element ref is attached to with h.Ref: its border box in window coordinates, as ElementRef.Bounds returns it (moved by scrolling and transforms, the zero Rect while the element is not mounted or displayed). It is a ResizeObserver that also sees the position: use it to size or place one element after another, or to anchor a floating element.
The Value is updated at the end of every frame, when the bounds changed since the last one, so a component that reads it rebuilds, and a deferred style closure that reads it lays out or repaints, in the frame after the change. Frames where nothing moved cost one comparison and request no further frame. The same Value is returned on every build.
func Meter() tela.Node {
track := tela.UseElementRef()
b := tela.UseBounds(track)
return Div(Ref(track), Class("h-2 rounded bg-slate-200"),
Div(Class("h-2 rounded bg-indigo-500"),
Style(tela.WidthFn(func() float32 { return b.Get().Width * 0.4 }))))
}
In an event handler, ElementRef.Bounds gives the same rectangle without a Value: a seek bar turns the pointer's x into a fraction with e.X-ref.Bounds().X over ref.Bounds().Width.
func UseScroll ¶
func UseScroll(ref ElementRef) (x, y *Value[float32])
UseScroll returns two Value objects that follow the scroll offsets of the element ref is attached to (zero while it is not a mounted scrolling element): x is its horizontal offset (scrollLeft) and y its vertical one (scrollTop), in logical pixels. They change on the UI thread as the element scrolls, so a deferred style closure that reads them repaints or lays out only its element, without rebuilding (Motion's useScroll). The same Values are returned on every build.
func Page() tela.Node {
list := tela.UseElementRef()
_, y := tela.UseScroll(list)
header := Div(Class("h-16 bg-white shadow"), Style(tela.OpacityFn(func() float32 {
return 1 - min(y.Get()/200, 1) // fades out over the first 200 px
})))
return Div(Class("flex h-screen flex-col"), header,
Div(Ref(list), Class("grow overflow-y-auto"), rows()))
}
Reading y.Get() in the component's body instead rebuilds the component on every scrolled frame; use h.OnScroll for logic that runs once per frame of scrolling.
func UseValue ¶
UseValue returns the Value of this hook slot: an observable value for something that changes often, such as a drag offset, a scroll position or download progress (ARCHITECTURE section 7, phase-deferred reads, as in Jetpack Compose). The first build stores initial; every build returns the same *Value.
Where v.Get() is called decides what a change redoes:
- in the component's body while it builds: the component builds again, like with UseState (also when the components above it skip their build);
- inside a layout-phase style closure (WidthFn, HeightFn): that element is laid out again, with no build and no style computation;
- inside a paint-phase style closure (TranslateFn, OpacityFn): that element is repainted, with no build, style or layout;
- anywhere else (event handlers, effects, goroutines): nothing; Get just returns the value.
The reads are taken again on every evaluation, so a closure that stops reading a Value stops being redone by it. A progress bar fed by a goroutine, which lays out the bar again without building Download:
func Download() tela.Node {
progress := tela.UseValue(0.0) // from 0 to 1
tela.UseEffect(func() func() {
ctx, cancel := context.WithCancel(context.Background())
go fetch(ctx, func(done float64) { progress.Set(done) }) // any goroutine
return cancel
}, nil)
return Div(Class("h-2 w-64 overflow-hidden rounded-full bg-slate-200"),
Div(Class("h-full bg-indigo-500"),
Style(tela.WidthFn(func() float32 { return float32(256 * progress.Get()) }))))
}
A dragged card that moves by repainting only (the offset is set from pointer handlers):
type Offset struct{ X, Y float32 }
offset := tela.UseValue(Offset{})
card := Div(Class("rounded-xl bg-white p-6 shadow-lg"),
Style(tela.TranslateFn(func() (x, y float32) {
p := offset.Get()
return p.X, p.Y
})))
Reading the same Value in the body instead (x := offset.Get()) would build the component on every change. Values read by the closures of another window's tree are not tracked.
type WheelEvent ¶
type WheelEvent = core.WheelEvent
WheelEvent is passed to h.OnWheel: a mouse wheel notch or a touchpad scroll over the element (the DOM's wheel event). DeltaX and DeltaY are the distance in logical pixels with the DOM's signs (positive DeltaY scrolls down; a notch is 53 pixels, and Shift turns a vertical notch horizontal); X, Y, LocalX and LocalY and the modifiers are as in MouseEvent. It bubbles, and its default action scrolls the nearest scrolling element that can move that way: PreventDefault cancels it.
// Ctrl+wheel zooms a canvas instead of scrolling the page.
Div(OnWheel(func(e tela.WheelEvent) {
if e.Ctrl {
e.PreventDefault()
zoom.Update(func(z float32) float32 { return z * (1 - e.DeltaY/500) })
}
}))
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
tela
command
Command tela is the Tela command line tool.
|
Command tela is the Tela command line tool. |
|
examples
|
|
|
board
command
Command board is a small kanban board: three columns of cards that can be dragged from one column to another, or reordered inside a column.
|
Command board is a small kanban board: three columns of cards that can be dragged from one column to another, or reordered inside a column. |
|
counter
command
Command counter is the Counter example of ARCHITECTURE section 1: a card with a heading and a button, styled only with Tailwind classes.
|
Command counter is the Counter example of ARCHITECTURE section 1: a card with a heading and a button, styled only with Tailwind classes. |
|
dashboard
command
Command dashboard is an admin dashboard in the style of shadcn/ui's dashboard example: a header with navigation, four stat cards, a bar chart built from divs and a list of recent sales with avatar initials.
|
Command dashboard is an admin dashboard in the style of shadcn/ui's dashboard example: a header with navigation, four stat cards, a bar chart built from divs and a list of recent sales with avatar initials. |
|
hello
command
Command hello is the smallest Tela program that looks like something: a static screen with a headline, a tagline and a rule, styled only with Tailwind classes.
|
Command hello is the smallest Tela program that looks like something: a static screen with a headline, a tagline and a rule, styled only with Tailwind classes. |
|
native
command
Command native is a file explorer that looks native on Windows 11 (Fluent), GNOME (Adwaita) and KDE (Breeze), each in light and dark.
|
Command native is a file explorer that looks native on Windows 11 (Fluent), GNOME (Adwaita) and KDE (Breeze), each in light and dark. |
|
player
command
Command player is a music player: a card with cover art, title, artist, a progress bar, previous/play/next buttons, and a queue of tracks.
|
Command player is a music player: a card with cover art, title, artist, a progress bar, previous/play/next buttons, and a queue of tracks. |
|
pricing
command
Command pricing is a pricing page: three plans with a highlighted one, feature lists with check marks and a monthly/yearly switch that changes the prices.
|
Command pricing is a pricing page: three plans with a highlighted one, feature lists with check marks and a monthly/yearly switch that changes the prices. |
|
settings
command
Command settings is a preferences page: a notifications card with switches, an appearance card with radio-like option cards, and a footer with Reset and Save buttons that stay disabled until something changed.
|
Command settings is a preferences page: a notifications card with switches, an appearance card with radio-like option cards, and a footer with Reset and Save buttons that stay disabled until something changed. |
|
shapes
command
Command shapes is a showcase of what Tela's styling can paint: a page of twelve cards with shadows, gradients (linear, radial and conic), rings, blur and backdrop blur, opacity, rounded and rotated shapes, all written as elements and Tailwind classes.
|
Command shapes is a showcase of what Tela's styling can paint: a page of twelve cards with shadows, gradients (linear, radial and conic), rings, blur and backdrop blur, opacity, rounded and rotated shapes, all written as elements and Tailwind classes. |
|
signup
command
Command signup is a "Create your account" screen: a brand panel on the left and, on the right, a form with every kind of text field Tela has.
|
Command signup is a "Create your account" screen: a brand panel on the left and, on the right, a form with every kind of text field Tela has. |
|
text
command
Command text is the phase-2 showcase: mixed-script paragraphs (Latin, Arabic, CJK, emoji), a code block, truncation and decorations, laid out on a page of cards in a native window.
|
Command text is the phase-2 showcase: mixed-script paragraphs (Latin, Arabic, CJK, emoji), a code block, truncation and decorations, laid out on a page of cards in a native window. |
|
virtuallist
command
Command virtuallist is a directory of 100,000 people in an h.VirtualList: only the rows in view (plus a few) are built, laid out and painted, rows with a note are taller and measured when they appear, and the header's buttons jump to a row with ElementRef.ScrollToIndex.
|
Command virtuallist is a directory of 100,000 people in an h.VirtualList: only the rows in view (plus a few) are built, laid out and painted, rows with a note are taller and measured when they appear, and the header's buttons jump to a row with ElementRef.ScrollToIndex. |
|
Package h holds the elements, props and helpers of a Tela UI.
|
Package h holds the elements, props and helpers of a Tela UI. |
|
internal
|
|
|
assets/fonts
Package fonts embeds the fonts Tela ships with, as raw font file bytes.
|
Package fonts embeds the fonts Tela ships with, as raw font file bytes. |
|
core
Package core is Tela's runtime (ARCHITECTURE sections 6 to 8): the three trees, reconciliation, hooks, the frame pipeline and its scheduler, and the hosts that run a tree in a window or headless.
|
Package core is Tela's runtime (ARCHITECTURE sections 6 to 8): the three trees, reconciliation, hooks, the frame pipeline and its scheduler, and the hosts that run a tree in a window or headless. |
|
fmath
Package fmath holds the elementary functions that every layer feeding the rasterizer needs with the same result, bit for bit, on every architecture (ADR-027, ADR-041).
|
Package fmath holds the elementary functions that every layer feeding the rasterizer needs with the same result, bit for bit, on every architecture (ADR-027, ADR-041). |
|
gen/keys
command
Command keys generates keys_gen.go in package tela: a tela.Key constant for every named key of internal/platform (its Key constants) and a tela.Code constant for every physical key code (its KeyCode constants and their W3C names), so the public lists never drift from the platform's.
|
Command keys generates keys_gen.go in package tela: a tela.Key constant for every named key of internal/platform (its Key constants) and a tela.Code constant for every physical key code (its KeyCode constants and their W3C names), so the public lists never drift from the platform's. |
|
gen/wikidoc
command
Command wikidoc generates the API reference pages of the GitHub wiki (docs/wiki/API-Reference-<pkg>.md) from the doc comments of Tela's public packages, so the wiki never drifts from pkg.go.dev.
|
Command wikidoc generates the API reference pages of the GitHub wiki (docs/wiki/API-Reference-<pkg>.md) from the doc comments of Tela's public packages, so the wiki never drifts from pkg.go.dev. |
|
gesture
Package gesture is Tela's gesture arena (ARCHITECTURE section 14), after Flutter's: the recognizers interested in a pointer sequence (a press and what follows until the release) compete in that pointer's arena, and one wins.
|
Package gesture is Tela's gesture arena (ARCHITECTURE section 14), after Flutter's: the recognizers interested in a pointer sequence (a press and what follows until the release) compete in that pointer's arena, and one wins. |
|
gfx
Package gfx holds Tela's basic graphics types and the DisplayList, the contract between the paint phase, the CPU rasterizer and any future GPU backend.
|
Package gfx holds Tela's basic graphics types and the DisplayList, the contract between the paint phase, the CPU rasterizer and any future GPU backend. |
|
gfx/raster
Package raster is Tela's CPU rasterizer: it renders a gfx.DisplayList into an *image.RGBA.
|
Package raster is Tela's CPU rasterizer: it renders a gfx.DisplayList into an *image.RGBA. |
|
layout
Package layout is Tela's layout engine (ARCHITECTURE section 9): block layout without margin collapsing, flexbox, relative and absolute positioning, intrinsic sizes, leaf measurement by callback, caching and relayout boundaries.
|
Package layout is Tela's layout engine (ARCHITECTURE section 9): block layout without margin collapsing, flexbox, relative and absolute positioning, intrinsic sizes, leaf measurement by callback, caching and relayout boundaries. |
|
platform
Package platform is the lowest layer of Tela: the contract between the framework and one OS windowing system (a backend).
|
Package platform is the lowest layer of Tela: the contract between the framework and one OS windowing system (a backend). |
|
platform/auto
Package auto selects the platform backend for the current environment.
|
Package auto selects the platform backend for the current environment. |
|
platform/headless
Package headless is an in-memory platform backend for tests, snapshots, telatest and the CLI.
|
Package headless is an in-memory platform backend for tests, snapshots, telatest and the CLI. |
|
platform/internal/evdev
Package evdev maps Linux evdev key codes (linux/input-event-codes.h) to physical keys (platform.KeyCode, W3C "code" values).
|
Package evdev maps Linux evdev key codes (linux/input-event-codes.h) to physical keys (platform.KeyCode, W3C "code" values). |
|
platform/internal/keyboard
Package keyboard holds the OS-independent part of the platform keyboard contract (see package platform, "Keyboard"), shared by the backends: the set of keys a window has delivered presses for, which turns into synthetic releases when the window loses focus, and the reporting of keyboard setup errors.
|
Package keyboard holds the OS-independent part of the platform keyboard contract (see package platform, "Keyboard"), shared by the backends: the set of keys a window has delivered presses for, which turns into synthetic releases when the window loses focus, and the reporting of keyboard setup errors. |
|
platform/internal/loop
Package loop is the OS-independent half of a platform backend's event loop: the part of the platform contract (see package platform, "Loop contract" and "Frames and pacing") that does not depend on how the OS reports events.
|
Package loop is the OS-independent half of a platform backend's event loop: the part of the platform contract (see package platform, "Loop contract" and "Frames and pacing") that does not depend on how the OS reports events. |
|
platform/internal/utf16x
Package utf16x converts text offsets between UTF-16 code units and UTF-8 bytes for the backends whose OS text APIs count UTF-16 (Win32 IMM32 and macOS NSTextInputClient).
|
Package utf16x converts text offsets between UTF-16 code units and UTF-8 bytes for the backends whose OS text APIs count UTF-16 (Win32 IMM32 and macOS NSTextInputClient). |
|
platform/internal/xkb
Package xkb is a minimal pure-Go implementation of the parts of XKB a Wayland client needs (no libxkbcommon, no cgo):
|
Package xkb is a minimal pure-Go implementation of the parts of XKB a Wayland client needs (no libxkbcommon, no cgo): |
|
platform/platformtest
Package platformtest is a backend-agnostic conformance suite for the platform contract.
|
Package platformtest is a backend-agnostic conformance suite for the platform contract. |
|
platform/wayland
Package wayland is Tela's Wayland platform backend: a pure-Go client of the compositor that implements the platform contract (see package platform) over the wire runtime and the generated protocol bindings.
|
Package wayland is Tela's Wayland platform backend: a pure-Go client of the compositor that implements the platform contract (see package platform) over the wire runtime and the generated protocol bindings. |
|
platform/wayland/protocols
Package protocols holds the Go bindings of the Wayland protocols Tela uses, generated from the XML files in xml/ by internal/platform/wayland/wlgen.
|
Package protocols holds the Go bindings of the Wayland protocols Tela uses, generated from the XML files in xml/ by internal/platform/wayland/wlgen. |
|
platform/wayland/wire
Package wire is the client side of the Wayland wire protocol: connection setup, the object ID map, message encoding and decoding, file-descriptor passing and buffered, non-blocking I/O.
|
Package wire is the client side of the Wayland wire protocol: connection setup, the object ID map, message encoding and decoding, file-descriptor passing and buffered, non-blocking I/O. |
|
platform/wayland/wlgen
command
Command wlgen generates Go bindings from Wayland protocol XML files.
|
Command wlgen generates Go bindings from Wayland protocol XML files. |
|
platform/win32
Package win32 is the Windows platform backend.
|
Package win32 is the Windows platform backend. |
|
platform/x11
Package x11 is the X11 platform backend, the Linux fallback when no Wayland compositor is available.
|
Package x11 is the X11 platform backend, the Linux fallback when no Wayland compositor is available. |
|
platform/x11/internal/xgb
Package XGB provides the X Go Binding, which is a low-level API to communicate with the core X protocol and many of the X extensions.
|
Package XGB provides the X Go Binding, which is a low-level API to communicate with the core X protocol and many of the X extensions. |
|
platform/x11/internal/xgb/bigreq
Package bigreq is the X client API for the BIG-REQUESTS extension.
|
Package bigreq is the X client API for the BIG-REQUESTS extension. |
|
platform/x11/internal/xgb/xproto
Package xproto is the X client API for the extension.
|
Package xproto is the X client API for the extension. |
|
semantics
Package semantics holds Tela's semantics tree (ARCHITECTURE sections 4, 8 and 16): a parallel tree of roles, names, states and actions derived from the Element and Box trees, used by accessibility backends, tests (telatest's ByRole and ByText) and agents (the aria snapshot of `tela inspect --aria` and `tela mcp`).
|
Package semantics holds Tela's semantics tree (ARCHITECTURE sections 4, 8 and 16): a parallel tree of roles, names, states and actions derived from the Element and Box trees, used by accessibility backends, tests (telatest's ByRole and ByText) and agents (the aria snapshot of `tela inspect --aria` and `tela mcp`). |
|
style
Package style is Tela's style layer (ARCHITECTURE section 10): the theme and design tokens, the Tailwind v4 class parser, and the resolution of classes and typed props into a ComputedStyle.
|
Package style is Tela's style layer (ARCHITECTURE section 10): the theme and design tokens, the Tailwind v4 class parser, and the resolution of classes and typed props into a ComputedStyle. |
|
style/gen
command
Command gen generates internal/style/theme_gen.go, the default theme, from the Tailwind v4 theme source vendored in tailwind/theme.css (ADR-018, ADR-037).
|
Command gen generates internal/style/theme_gen.go, the default theme, from the Tailwind v4 theme source vendored in tailwind/theme.css (ADR-018, ADR-037). |
|
testutil/golden
Package golden compares images produced by tests against golden PNG files.
|
Package golden compares images produced by tests against golden PNG files. |
|
text
Package text is Tela's text layer (ARCHITECTURE section 11): the font layer (typefaces, face instances, metrics, font matching and fallback, color glyphs) and, on top of it, paragraph layout (shaping, bidi, line breaking, alignment, truncation, painting); see "Paragraph layout".
|
Package text is Tela's text layer (ARCHITECTURE section 11): the font layer (typefaces, face instances, metrics, font matching and fallback, color glyphs) and, on top of it, paragraph layout (shaping, bidi, line breaking, alignment, truncation, painting); see "Paragraph layout". |
|
text/edit
Package edit is the editable plain-text model under core's Input and Textarea (ARCHITECTURE section 11, ADR-065): a UTF-8 value, a selection, an IME composition, an undo history and the commands that key and pointer handling produce.
|
Package edit is the editable plain-text model under core's Input and Textarea (ARCHITECTURE section 11, ADR-065): a UTF-8 value, a selection, an IME composition, an undo history and the commands that key and pointer handling produce. |
|
Package telatest tests Tela UIs without a window: it mounts a tree in a headless window, simulates a user with real pointer and key events, finds elements by role and text like Playwright, and compares the pixels with goldens (ARCHITECTURE section 17).
|
Package telatest tests Tela UIs without a window: it mounts a tree in a headless window, simulates a user with real pointer and key events, finds elements by role and text like Playwright, and compares the pixels with goldens (ARCHITECTURE section 17). |





