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 Animate[T Animatable](v *Value[T], target T, opts ...AnimationOption)
- func Assets(fsys fs.FS)
- func Post(fn func())
- func Run(app App) error
- func SetColorScheme(s ColorScheme)
- 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 UsePresence() (present bool, done func())
- func UseReducedMotion() bool
- func UseStateSet[T any](initial T) (T, func(T))
- func UseStateUpdate[T any](initial T) (T, func(func(T) T))
- type Animatable
- type AnimationOption
- func Bounce(b float64) AnimationOption
- func Damping(c float64) AnimationOption
- func Duration(d time.Duration) AnimationOption
- func Ease(css string) AnimationOption
- func OnDone(fn func()) AnimationOption
- func Spring(opts ...AnimationOption) AnimationOption
- func Stiffness(k float64) AnimationOption
- func Velocity[T Animatable](v T) AnimationOption
- type App
- type Callback
- type ChangeEvent
- type Child
- type Code
- type Color
- type ColorScheme
- 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 Point
- type PointerEvent
- type PointerKind
- type Previews
- type Prop
- type Rect
- type Ref
- type ScrollAlign
- type ScrollEvent
- type SelectEvent
- type Setter
- type Shadow
- type Size
- type StyleProp
- func BackgroundGradient(g Gradient) StyleProp
- func Bg(c Color) StyleProp
- func BgFn(fn func() Color) StyleProp
- func BgImage(img image.Image) 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 TextColorFn(fn func() 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
- func UseBounds(ref ElementRef) *Value[Rect]
- func UseScroll(ref ElementRef) (x, y *Value[float32])
- func UseSpring[T Animatable](initial, target T, opts ...AnimationOption) *Value[T]
- func UseTween[T Animatable](initial, target T, opts ...AnimationOption) *Value[T]
- func UseValue[T any](initial T) *Value[T]
- 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 Animate ¶ added in v0.6.0
func Animate[T Animatable](v *Value[T], target T, opts ...AnimationOption)
Animate moves v to target: with a spring by default (Motion's default, or as Stiffness, Damping, Bounce or Spring say), or with a tween when the options are Duration and Ease only. v is any Value of an Animatable type, from UseValue, UseSpring or UseTween.
y := tela.UseValue[float32](0)
Button(OnClick(func(tela.MouseEvent) { tela.Animate(y, 200, tela.Spring(tela.Bounce(0.25))) }),
Text("Drop"))
tela.Animate(y, 0, tela.Duration(300*time.Millisecond), tela.Ease("ease-in-out"))
A new Animate on the same Value interrupts the running animation: the new one starts where the value is and, for a spring, at the speed it has, so a fling continues smoothly; a Set or Update of the Value stops the animation. Like Set, Animate is safe from any goroutine: the animation starts when the UI thread applies it, in order with the Value's Set and Update calls (an animation applied between two frames starts at the next one). Value.Latest does not anticipate animations: it returns the value they have reached.
With reduced motion, or in a tree that no frame clock drives, the value jumps to target. An animation of a Value whose component unmounts ends.
Example ¶
t := &exampleT{}
defer t.close()
var y *tela.Value[float32]
ball := func() tela.Node {
y = tela.UseValue[float32](0)
return Div(
Button(OnClick(func(tela.MouseEvent) { tela.Animate(y, 200, tela.Spring(tela.Bounce(0.25))) }), Text("Drop")),
Button(OnClick(func(tela.MouseEvent) {
tela.Animate(y, 0, tela.Duration(300*time.Millisecond), tela.Ease("ease-in-out"))
}), Text("Lift")),
Div(Class("size-8 rounded-full bg-indigo-500"), Style(tela.TranslateFn(func() (float32, float32) { return 0, y.Get() }))))
}
app := telatest.Mount(t, tela.C(ball))
app.Click(telatest.ByRole("button", "Drop")) // settles: the spring ends
fmt.Println(y.Get())
app.Click(telatest.ByRole("button", "Lift"))
fmt.Println(y.Get())
Output: 200 0
func Assets ¶ added in v0.6.0
Assets registers a file system that background images are loaded from: the path in a class such as bg-[url(img/hero.jpg)] names a file of fsys, relative to its root (a leading "/" or "./" is ignored, so url(/img/hero.jpg) is the same file; ".." is not allowed). Call it from an init function, so that the files are found the same way by Run, tela snapshot, tela inspect and telatest:
//go:embed img
var images embed.FS
func init() { tela.Assets(images) }
Assets may be called several times: the file systems are searched in the order they were registered and the first one that has the file wins. Files are decoded once, when first painted, and kept for the life of the process: PNG, JPEG, GIF (the first frame) and WebP, recognized by their content. A missing or undecodable file paints nothing and is reported once in the dev log, with the path and the registered file systems, like an unknown class. Remote and data: URLs are not supported.
Example ¶
package main
import (
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"testing/fstest"
"github.com/kuronosu/tela"
"github.com/kuronosu/tela/internal/core"
"github.com/kuronosu/tela/internal/style"
)
// solidPNG returns a 2x2 PNG of one color.
func solidPNG(c color.Color) []byte {
img := image.NewRGBA(image.Rect(0, 0, 2, 2))
for i := range 4 {
img.Set(i%2, i/2, c)
}
var buf bytes.Buffer
png.Encode(&buf, img)
return buf.Bytes()
}
func main() {
// An app embeds its images and registers them from an init function:
//
// //go:embed img
// var images embed.FS
//
// func init() { tela.Assets(images) }
//
// Here an in-memory file system holds one red PNG.
tela.Assets(fstest.MapFS{"img/red.png": {Data: solidPNG(color.RGBA{R: 255, A: 255})}})
// With package h: Div(Class("size-8 bg-[url(img/red.png)] bg-cover")).
card := core.Primitive(style.KindDiv, core.Class("size-8 bg-[url(img/red.png)] bg-cover"))
_, img := core.Render(card, core.RenderOptions{Width: 32, Height: 32})
fmt.Println(img.RGBAAt(16, 16))
}
Output: {255 0 0 255}
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 SetColorScheme ¶ added in v0.6.0
func SetColorScheme(s ColorScheme)
SetColorScheme sets the color scheme of the running app: Light or Dark override the system's preference, System follows it again. Every element is styled again (dark: classes) and the components that call UseColorScheme build again. It is safe from any goroutine, like a state setter: the change applies on the UI thread before the next frame. Called before Run, it applies when the window opens (it wins over App.ColorScheme). Like Post, it goes to the app that receives posted functions (telatest.Mount's app in tests).
Button(OnClick(func(tela.MouseEvent) { tela.SetColorScheme(tela.Dark) }), Text("Dark"))
It panics for a value other than System, Light and Dark.
Example ¶
toggle := func() tela.Node {
dark := tela.UseColorScheme() == tela.Dark
next, label := tela.Dark, "Dark mode"
if dark {
next, label = tela.Light, "Light mode"
}
return Button(Class("bg-white text-black dark:bg-black dark:text-white"),
OnClick(func(tela.MouseEvent) { tela.SetColorScheme(next) }), Text(label))
}
app := telatest.Mount(t, tela.C(toggle))
defer app.Close()
app.Click(telatest.ByRole("button", "Dark mode"))
fmt.Println(app.AriaSnapshot())
Output: - button "Light mode"
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 UsePresence ¶ added in v0.6.0
func UsePresence() (present bool, done func())
UsePresence reports whether the component is present, for a component inside a child of an h.Presence: present is false while that child exits, after it was removed. The component then runs its own exit (an Animate, saving a draft...) and calls done when it is finished; the child unmounts once every component that used UsePresence called done and the transitions of its h.Exit classes ended. done is the same function on every build, safe from any goroutine, and does nothing while the component is present (a call from a previous exit that was cancelled is ignored). Outside any Presence, present is always true.
A toast that slides down and fades out before it unmounts: the tween to its exit position calls done when it arrives (OnDone; done does nothing while the toast is present, so the entrance may pass it too).
func Toast(msg string) tela.Node {
present, done := tela.UsePresence()
target := float32(0)
if !present {
target = 1
}
out := tela.UseTween(float32(0), target, tela.Duration(200*time.Millisecond), tela.OnDone(done))
return Div(Class("rounded-lg bg-slate-900 px-4 py-2 text-white"),
Style(tela.TranslateFn(func() (float32, float32) { return 0, 40 * out.Get() }),
tela.OpacityFn(func() float32 { return 1 - out.Get() })),
Text(msg))
}
Presence(If(saved, tela.C(func() tela.Node { return Toast("Saved") })))
Example ¶
t := &exampleT{}
defer t.close()
toast := func() tela.Node {
present, done := tela.UsePresence()
target := float32(0)
if !present {
target = 1
}
// The tween out ends the exit: done does nothing while present.
out := tela.UseTween(float32(0), target, tela.Duration(200*time.Millisecond), tela.OnDone(done))
return Div(Role("status"), Class("rounded-lg bg-slate-900 px-4 py-2 text-white"),
Style(tela.OpacityFn(func() float32 { return 1 - out.Get() })), Text("Saved"))
}
page := func() tela.Node {
shown, setShown := tela.UseState(true)
return Div(
Button(OnClick(func(tela.MouseEvent) { setShown.Set(false) }), Text("Dismiss")),
Presence(If(shown, tela.C(toast))))
}
app := telatest.Mount(t, tela.C(page))
start := app.Now()
app.Click(telatest.ByRole("button", "Dismiss")) // waits for the exit
fmt.Println(app.Count(telatest.ByRole("status", "")), app.Now()-start >= 200*time.Millisecond)
Output: 0 true
func UseReducedMotion ¶ added in v0.6.0
func UseReducedMotion() bool
UseReducedMotion reports whether the system asks for reduced motion (the motion-reduce: classes apply, transitions take no time and scrolling jumps). The component builds again when it changes. It is a hook: call it at the top level of a component.
Example ¶
spinner := func() tela.Node {
if tela.UseReducedMotion() {
return P(Text("Loading…"))
}
return Div(Class("size-6 animate-spin rounded-full border-2 border-t-transparent"), AriaLabel("Loading"), Role("status"))
}
yaml, _ := telatest.AriaSnapshot(tela.C(spinner), telatest.ReducedMotion())
fmt.Println(yaml)
Output: - paragraph: Loading…
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 Animatable ¶ added in v0.6.0
type Animatable = core.Animatable
Animatable lists the types that UseSpring, UseTween and Animate animate:
- float32: a number (an offset, a scale, an opacity, a progress);
- Color: interpolated as CSS transitions interpolate colors, premultiplied by alpha, in sRGB between two colors written as hex, rgb() or named colors, and in Oklab otherwise (the Oklch palette of Tailwind, OKLCH); read it in a BgFn or TextColorFn to animate a color by repainting only;
- Point, Size and Rect: component by component, each with its own velocity.
Example ¶
t := &exampleT{}
defer t.close()
var bg *tela.Value[tela.Color]
app := telatest.Mount(t, tela.C(func() tela.Node {
bg = tela.UseValue(tela.Hex("#ffffff"))
// Read in the paint phase: the animation only repaints the box.
return Div(Class("size-10"), Style(tela.BgFn(bg.Get)))
}), telatest.NoSettle())
// Hex colors interpolate in sRGB, like a CSS transition between them.
tela.Animate(bg, tela.Hex("#000000"), tela.Duration(100*time.Millisecond), tela.Ease("linear"))
app.Advance(50 * time.Millisecond) // 33 ms of the tween: see ExamplePoint
fmt.Println(bg.Get())
Output: #aaaaaa
type AnimationOption ¶ added in v0.6.0
type AnimationOption = core.AnimOption
AnimationOption configures an animation of UseSpring, UseTween or Animate: Stiffness, Damping, Bounce, Duration, Ease, Velocity and Spring, which groups spring options, and OnDone. The zero AnimationOption changes nothing.
Springs (Motion's model) are defined either by their physics or by how long they take:
tela.Stiffness(300), tela.Damping(30) // physics; mass 1 tela.Spring(tela.Duration(500*time.Millisecond), tela.Bounce(0.25))
Without options a spring has stiffness 100 and damping 10 (Motion's default, slightly bouncy); Bounce without a Duration takes 800 ms, and a Duration without Bounce a bounce of 0.3. Tweens take a Duration (default 300 ms) and an Ease (default CSS's "ease").
Options that contradict each other or the hook (Ease on a spring, Stiffness and Duration together, spring options on UseTween), invalid numbers and invalid easing strings panic in dev mode (TELA_DEV=1) with what to write instead; otherwise they are ignored.
Example ¶
// Options for the three kinds of motion.
physics := []tela.AnimationOption{tela.Stiffness(300), tela.Damping(30)}
timed := []tela.AnimationOption{tela.Spring(tela.Duration(400*time.Millisecond), tela.Bounce(0.2))}
tween := []tela.AnimationOption{tela.Duration(250 * time.Millisecond), tela.Ease("cubic-bezier(0, 0, 0.2, 1)")}
fmt.Println(len(physics), len(timed), len(tween))
Output: 2 1 2
func Bounce ¶ added in v0.6.0
func Bounce(b float64) AnimationOption
Bounce sets how bouncy a spring defined by its duration is (Motion's bounce): 0 does not overshoot, 0.25 bounces a little, values toward 1 oscillate longer. Without a Duration the spring takes 800 ms. It makes Animate use a spring.
Example ¶
// A spring defined by its duration (800 ms without Duration) ends no
// earlier than it, at rest.
fmt.Println("bounce 0:", springEnd(tela.Spring(tela.Bounce(0))))
fmt.Println("bounce 0.5:", springEnd(tela.Spring(tela.Bounce(0.5))))
Output: bounce 0: 830ms bounce 0.5: 870ms
func Damping ¶ added in v0.6.0
func Damping(c float64) AnimationOption
Damping sets the damping of a spring: how strongly it resists motion (default 10). Low values bounce around the target; damping = 2*sqrt(stiffness) is critically damped (no overshoot), so Stiffness(300) with Damping(35) settles without bouncing. It makes Animate use a spring.
Example ¶
// Damping 2*sqrt(stiffness) is critically damped: no overshoot. Less
// bounces around the target and takes longer to come to rest.
fmt.Println("damping 40:", springEnd(tela.Stiffness(400), tela.Damping(40)))
fmt.Println("damping 10:", springEnd(tela.Stiffness(400), tela.Damping(10)))
Output: damping 40: 480ms damping 10: 1.15s
func Duration ¶ added in v0.6.0
func Duration(d time.Duration) AnimationOption
Duration sets how long a tween takes, or, for a spring (UseSpring, or inside Spring), the time a spring found from it settles in (Motion's duration/bounce springs; it ends no earlier). A Duration of 0 jumps to the target: the value is stored at once, a tween's or a spring's, and an OnDone is queued right after, as for any target that applies at once.
Example ¶
fmt.Println("tween:", springEnd(tela.Duration(250*time.Millisecond)))
fmt.Println("spring:", springEnd(tela.Spring(tela.Duration(250*time.Millisecond))))
Output: tween: 270ms spring: 320ms
func Ease ¶ added in v0.6.0
func Ease(css string) AnimationOption
Ease sets the easing curve of a tween, a CSS <easing-function>: one of the keywords linear, ease, ease-in, ease-out, ease-in-out, step-start and step-end, cubic-bezier(x1, y1, x2, y2) or steps(n[, position]). The keywords are CSS's curves: Tailwind's ease-out utility is Ease("cubic-bezier(0, 0, 0.2, 1)"). An invalid string panics in dev mode with a suggestion (Ease("ease-out-cubic"): did you mean "ease-out"?); otherwise the tween uses "ease".
Example ¶
fmt.Println("ease-out:", springEnd(tela.Ease("ease-out"))) // 300 ms by default
fmt.Println("steps:", springEnd(tela.Duration(time.Second), tela.Ease("steps(4, jump-end)")))
Output: ease-out: 320ms steps: 1.03s
func OnDone ¶ added in v0.6.0
func OnDone(fn func()) AnimationOption
OnDone calls fn when the animation comes to rest at its target: once, on the UI thread, queued with the state updates like Post, so it runs before the next build and state it sets is built in the frame that shows the target. It is not called when the animation is interrupted: by another Animate, Set or Update of the Value, by a new target of UseSpring or UseTween, or by the unmount of the Value's component. When the target applies at once (reduced motion, a tree that no frame clock drives, Duration(0), a value already at its target and at rest, a NaN target) fn is queued right after it applies. For UseSpring and UseTween it is the OnDone of the build that set the target that runs.
tela.Animate(x, 0, tela.Spring(), tela.OnDone(func() { setOpen.Set(false) }))
It is how a component inside an h.Presence child ends its own exit: see UsePresence.
Example ¶
t := &exampleT{}
defer t.close()
var x *tela.Value[float32]
var log []string
app := telatest.Mount(t, tela.C(func() tela.Node {
x = tela.UseValue[float32](0)
return Div(Class("size-8 bg-black"), Style(tela.TranslateFn(func() (float32, float32) { return x.Get(), 0 })))
}), telatest.NoSettle())
tela.Animate(x, 100, tela.OnDone(func() { log = append(log, "first") }))
app.Advance(50 * time.Millisecond)
// Interrupted: the first OnDone never runs.
tela.Animate(x, 0, tela.Duration(100*time.Millisecond), tela.OnDone(func() { log = append(log, "second") }))
app.Advance(time.Second)
fmt.Println(log, x.Get())
Output: [second] 0
func Spring ¶ added in v0.6.0
func Spring(opts ...AnimationOption) AnimationOption
Spring makes the animation a spring with the options given: Stiffness and Damping, or Duration and Bounce. Spring() alone is Motion's default spring. It is how Animate gets a spring defined by its duration:
tela.Animate(y, 200, tela.Spring(tela.Duration(400*time.Millisecond), tela.Bounce(0.3)))
Example ¶
fmt.Println("Motion's default:", springEnd(tela.Spring()))
fmt.Println("500 ms, bounce 0.3:", springEnd(tela.Spring(tela.Duration(500*time.Millisecond), tela.Bounce(0.3))))
Output: Motion's default: 1.07s 500 ms, bounce 0.3: 530ms
func Stiffness ¶ added in v0.6.0
func Stiffness(k float64) AnimationOption
Stiffness sets the stiffness of a spring: how strongly it pulls the value to its target (default 100). Higher is faster. With Damping it defines the spring by its physics, so it cannot be combined with Duration or Bounce. It makes Animate use a spring.
Example ¶
// Four times the stiffness (with the damping that keeps both critically
// damped) arrives in about half the time.
fmt.Println("stiffness 100:", springEnd(tela.Stiffness(100), tela.Damping(20)))
fmt.Println("stiffness 400:", springEnd(tela.Stiffness(400), tela.Damping(40)))
Output: stiffness 100: 870ms stiffness 400: 480ms
func Velocity ¶ added in v0.6.0
func Velocity[T Animatable](v T) AnimationOption
Velocity sets the initial velocity of a spring, in the Value's units per second, component by component (a Point's X and Y, a Rect's X, Y, Width and Height). It replaces the velocity an interrupted animation would pass on: a released drag hands its pointer velocity to the spring that brings the element back:
OnDragEnd(func(e tela.DragEvent) {
tela.Animate(pos, tela.Point{}, tela.Spring(), tela.Velocity(tela.Point{X: e.VelocityX, Y: e.VelocityY}))
})
It makes Animate use a spring. Its type must be the Value's, and it does not apply to tweens (UseTween, or Duration and Ease alone) nor to Color values, whose components move in a space (sRGB or Oklab, premultiplied) that depends on the two colors: those panic in dev mode and are ignored otherwise.
Example ¶
t := &exampleT{}
defer t.close()
var x *tela.Value[float32]
app := telatest.Mount(t, tela.C(func() tela.Node {
x = tela.UseValue[float32](0)
return Div(Class("size-8 bg-black"), Style(tela.TranslateFn(func() (float32, float32) { return x.Get(), 0 })))
}), telatest.NoSettle())
// A flick at rest: the value is already at its target, the velocity
// carries it away and the spring brings it back.
tela.Animate(x, 0, tela.Stiffness(200), tela.Damping(20), tela.Velocity(float32(600)))
var peak float32
for range 30 {
app.Advance(time.Second / 60)
peak = max(peak, x.Get())
}
app.Advance(2 * time.Second)
fmt.Printf("peak %.0f, rest %v\n", peak, x.Get())
Output: peak 19, rest 0
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
// ColorScheme is the app's color scheme: Light or Dark override the
// system's preference (the dark: classes, UseColorScheme); the zero
// value, System, follows it. SetColorScheme changes it while the app
// runs.
ColorScheme ColorScheme
}
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 ColorScheme ¶ added in v0.6.0
type ColorScheme = core.ColorScheme
ColorScheme is a color scheme: what the app asks for (System, Light or Dark) in App.ColorScheme and SetColorScheme, and the effective scheme UseColorScheme returns (Light or Dark). The dark: classes apply in the effective Dark scheme.
const ( // System follows the system's light or dark preference. It is the // zero value, so an [App] follows the system unless it sets // ColorScheme. System ColorScheme = core.ColorSchemeSystem // Light is the light scheme: dark: classes do not apply. Light ColorScheme = core.ColorSchemeLight // Dark is the dark scheme: dark: classes apply. Dark ColorScheme = core.ColorSchemeDark )
func UseColorScheme ¶ added in v0.6.0
func UseColorScheme() ColorScheme
UseColorScheme returns the effective color scheme, Light or Dark: the one the app set (App.ColorScheme, SetColorScheme) or, with System, the system's preference. The component builds again when it changes, like with a state hook. It is a hook: call it at the top level of a component.
func Logo() tela.Node {
src := lightLogo
if tela.UseColorScheme() == tela.Dark {
src = darkLogo
}
return Img(SrcImage(src), Alt("Tela"))
}
Classes do not need it: dark:bg-slate-900 already follows the scheme.
Example ¶
theme := func() tela.Node {
name := "light"
if tela.UseColorScheme() == tela.Dark {
name = "dark"
}
return P(Textf("Theme: %s", name))
}
// The system prefers dark (telatest.Dark), the app follows it.
yaml, _ := telatest.AriaSnapshot(tela.C(theme), telatest.Dark())
fmt.Println(yaml)
// The app overrides the system (tela.App.ColorScheme).
yaml, _ = telatest.AriaSnapshot(tela.C(theme), telatest.Dark(), telatest.ColorScheme(tela.Light))
fmt.Println(yaml)
Output: - paragraph: "Theme: dark" - paragraph: "Theme: light"
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 Point ¶ added in v0.6.0
Point is a point in logical pixels: X to the right, Y down. It is one of the Animatable types, for a position that moves as a whole, such as a dragged card's offset.
Example ¶
t := &exampleT{}
defer t.close()
var pos *tela.Value[tela.Point]
card := func() tela.Node {
pos = tela.UseValue(tela.Point{})
return Div(Class("size-24 rounded-xl bg-white shadow"),
Style(tela.TranslateFn(func() (float32, float32) { p := pos.Get(); return p.X, p.Y })),
// Released: back to the origin on a bouncy spring.
OnDragEnd(func(tela.DragEvent) { tela.Animate(pos, tela.Point{}, tela.Spring(tela.Bounce(0.4))) }),
OnDrag(func(e tela.DragEvent) { pos.Set(tela.Point{X: e.OffsetX, Y: e.OffsetY}) }))
}
app := telatest.Mount(t, tela.C(card), telatest.NoSettle())
// Called between frames (like from a goroutine), Animate starts at the
// next frame: 50 ms later the tween has run for 33 ms of its 100.
tela.Animate(pos, tela.Point{X: 40, Y: 30}, tela.Duration(100*time.Millisecond), tela.Ease("linear"))
app.Advance(50 * time.Millisecond)
fmt.Printf("%.0f %.0f\n", pos.Get().X, pos.Get().Y)
Output: 13 10
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 Size ¶ added in v0.6.0
Size is a width and a height in logical pixels. It is one of the Animatable types.
Example ¶
t := &exampleT{}
defer t.close()
var size *tela.Value[tela.Size]
app := telatest.Mount(t, tela.C(func() tela.Node {
size = tela.UseSpring(tela.Size{Width: 100, Height: 40}, tela.Size{Width: 200, Height: 120})
return Div(Class("rounded-lg bg-sky-500"), Style(
tela.WidthFn(func() float32 { return size.Get().Width }),
tela.HeightFn(func() float32 { return size.Get().Height })))
}))
// Mount settles: the spring from the initial size to the target ended.
_, _, w, h := app.Bounds(telatest.ByClass("bg-sky-500"))
fmt.Println(w, h)
Output: 200 120
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 BgFn ¶ added in v0.6.0
BgFn defers Bg to the paint phase: the element's background color is fn's, and a change of the Values fn reads repaints the element without building, computing styles or laying out. Like the other Fn props it wins over the background classes, and over their transitions. With a UseSpring or Animate of a Color it animates a background without rebuilding:
bg := tela.UseSpring(tela.Hex("#ffffff"), target)
Div(Class("rounded-lg p-4"), Style(tela.BgFn(bg.Get)))
Example ¶
t := &exampleT{}
defer t.close()
var bg *tela.Value[tela.Color]
app := telatest.Mount(t, tela.C(func() tela.Node {
bg = tela.UseValue(tela.Hex("#ffffff"))
return Div(Class("size-10"), Style(tela.BgFn(bg.Get)))
}), telatest.Size(40, 40))
before := app.Stats()
bg.Set(tela.Hex("#4f46e5"))
after := app.Stats() // settles: the frame that shows the change
// The change repainted the box: no build, style or layout.
fmt.Println(app.Screenshot().RGBAAt(5, 5), after.Builds-before.Builds, after.Styles-before.Styles, after.Layouts-before.Layouts)
Output: {79 70 229 255} 0 0 0
func BgImage ¶ added in v0.6.0
BgImage sets a decoded image as the background image, over the background color. The background classes apply to it as to a bg-[url(...)] image: bg-cover, bg-contain, bg-center, bg-no-repeat, bg-origin-*, bg-clip-* and the others. Each image.Image value is converted once and cached (the cache keeps the most recently used images, up to 256 MiB of pixels), so pass the same value on every build and do not modify its pixels afterwards: give a new image instead. For images stored as files, register them with Assets and use a class.
Example ¶
package main
import (
"fmt"
"image"
"image/color"
"github.com/kuronosu/tela"
"github.com/kuronosu/tela/internal/core"
"github.com/kuronosu/tela/internal/style"
)
func main() {
// A decoded image, such as one from image.Decode or generated in
// code. Pass the same value on every build: it is converted once.
photo := image.NewRGBA(image.Rect(0, 0, 4, 4))
for i := range 16 {
photo.Set(i%4, i/4, color.RGBA{B: 255, A: 255})
}
// With package h: Div(Class("size-8 bg-cover bg-center"), Style(tela.BgImage(photo))).
card := core.Primitive(style.KindDiv, core.Class("size-8 bg-cover bg-center"), core.Style(tela.BgImage(photo)))
_, img := core.Render(card, core.RenderOptions{Width: 32, Height: 32})
fmt.Println(img.RGBAAt(16, 16))
}
Output: {0 0 255 255}
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 TextColorFn ¶ added in v0.6.0
TextColorFn defers TextColor to the paint phase: a change of the Values fn reads repaints the text without building, computing styles or laying out. The element's text takes fn's color, and so does the text of its descendants that inherits the element's color: those whose classes (and the classes of the elements between) set no text color of their own, as CSS inheritance would give it. Underlines and other decorations without a color of their own follow the text. Colors that the classes derive from the text color when the style is computed (currentColor borders, outlines, ring colors) and the text of h.Input and h.Textarea keep the color their classes give. It wins over the text color classes and their transitions.
color := tela.UseTween(tela.Hex("#0f172a"), target, tela.Duration(200*time.Millisecond))
P(Style(tela.TextColorFn(color.Get)), Text("Status: "), Strong(Text("online")))
Example ¶
t := &exampleT{}
defer t.close()
var c *tela.Value[tela.Color]
app := telatest.Mount(t, tela.C(func() tela.Node {
c = tela.UseValue(tela.Hex("#0f172a"))
// The Strong inherits the paragraph's color, so it follows the Fn
// too; the Span has a color of its own.
return P(Style(tela.TextColorFn(c.Get)),
Text("Status: "), Strong(Text("online")), Span(Class("text-slate-400"), Text(" (auto)")))
}))
before := app.Stats()
c.Set(tela.Hex("#16a34a"))
after := app.Stats()
// Builds, styles, layouts and paints of the change.
fmt.Println(after.Builds-before.Builds, after.Styles-before.Styles, after.Layouts-before.Layouts, after.Paints-before.Paints)
Output: 0 0 0 1
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 UseSpring ¶ added in v0.6.0
func UseSpring[T Animatable](initial, target T, opts ...AnimationOption) *Value[T]
UseSpring returns a Value that follows target with a spring: on the first build it holds initial and, if target differs, starts moving to it; whenever a build passes a target that differs from the previous one, the spring retargets from where the value is, keeping its velocity, so an interrupted motion turns smoothly instead of stopping (Compose's animate*AsState, Motion's useSpring). It is a Value like UseValue's: read it in a deferred style closure (TranslateFn, OpacityFn, WidthFn...) and the element animates by repainting or laying out only, without rebuilding the component; the same *Value is returned on every build.
The options are [AnimationOption]s: Stiffness and Damping, or Duration and Bounce; without them, Motion's default spring (stiffness 100, damping 10). The spring ends when it comes to rest (its speed and its distance to the target are below Motion's thresholds), at exactly the target, and from then on the app requests no frames.
A panel that slides in when open changes:
target := float32(-320)
if open {
target = 0
}
x := tela.UseSpring(float32(-320), target, tela.Stiffness(300), tela.Damping(30))
return Div(Class("fixed inset-y-0 left-0 w-80 bg-white shadow-xl"),
Style(tela.TranslateFn(func() (float32, float32) { return x.Get(), 0 })))
With reduced motion (App, or the system setting), or in a tree that no frame clock drives (telatest.Render, tela snapshot), the value jumps to each new target. Calling Set on the Value stops the spring; the next target change starts from the value set.
Example ¶
t := &exampleT{}
defer t.close()
var x *tela.Value[float32]
panel := func() tela.Node {
open, setOpen := tela.UseState(false)
target := float32(-320)
if open {
target = 0
}
x = tela.UseSpring(float32(-320), target, tela.Stiffness(300), tela.Damping(30))
return Div(Class("relative h-full"),
Button(OnClick(func(tela.MouseEvent) { setOpen.Set(true) }), Text("Open")),
Div(Class("absolute inset-y-0 left-0 w-80 bg-white shadow-xl"),
Style(tela.TranslateFn(func() (float32, float32) { return x.Get(), 0 }))))
}
// NoSettle: the click does not run the spring to its end.
app := telatest.Mount(t, tela.C(panel), telatest.NoSettle())
app.Click(telatest.ByRole("button", "Open"))
for range 3 {
app.Advance(100 * time.Millisecond)
fmt.Printf("%.1f\n", x.Get())
}
app.ExpectText("Open") // expectations settle: the spring comes to rest
fmt.Println(x.Get())
Output: -140.5 -24.7 -0.1 0
func UseTween ¶ added in v0.6.0
func UseTween[T Animatable](initial, target T, opts ...AnimationOption) *Value[T]
UseTween is UseSpring with a tween: the value moves to each new target in a fixed Duration along an Ease curve (default 300 ms, CSS's "ease"), like a CSS transition of the value. A new target starts a new tween from where the value is (tweens have no velocity to keep).
op := tela.UseTween(float32(0), target, tela.Duration(200*time.Millisecond), tela.Ease("ease-out"))
Div(Class("rounded-lg bg-slate-900 p-3 text-white"),
Style(tela.OpacityFn(op.Get)), Text("Saved"))
It takes only Duration and Ease; spring options panic in dev mode.
Example ¶
t := &exampleT{}
defer t.close()
var op *tela.Value[float32]
toast := func() tela.Node {
shown, setShown := tela.UseState(false)
target := float32(0)
if shown {
target = 1
}
op = tela.UseTween(float32(0), target, tela.Duration(200*time.Millisecond), tela.Ease("linear"))
return Div(
Button(OnClick(func(tela.MouseEvent) { setShown.Set(true) }), Text("Save")),
Div(Class("rounded-lg bg-slate-900 p-3 text-white"), Style(tela.OpacityFn(op.Get)), Text("Saved")))
}
app := telatest.Mount(t, tela.C(toast), telatest.NoSettle())
app.Click(telatest.ByRole("button", "Save"))
app.Advance(50 * time.Millisecond)
fmt.Printf("%.2f\n", op.Get())
app.Advance(150 * time.Millisecond)
fmt.Printf("%.2f\n", op.Get())
Output: 0.25 1.00
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, BgFn, TextColorFn): 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 analytics dashboard in the glassmorphism style: frosted panels over a colorful backdrop, in light and dark.
|
Command dashboard is an analytics dashboard in the glassmorphism style: frosted panels over a colorful backdrop, in light and dark. |
|
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. |
|
layout
command
Command layout shows Tela's layout animations (ARCHITECTURE section 12, ADR-081): a task list whose rows slide to their new places when it is reordered (h.AnimateLayout), a tab bar whose underline slides from the tab that loses it to the one that gets it (h.LayoutID), and a card that grows when it is expanded while its header keeps its size and the note below it moves down.
|
Command layout shows Tela's layout animations (ARCHITECTURE section 12, ADR-081): a task list whose rows slide to their new places when it is reordered (h.AnimateLayout), a tab bar whose underline slides from the tab that loses it to the one that gets it (h.LayoutID), and a card that grows when it is expanded while its header keeps its size and the note below it moves down. |
|
motion
command
Command motion shows Tela's explicit animations (ARCHITECTURE section 12): a dialog that enters with the starting: variant and leaves with h.Exit inside h.Presence, a toast that runs its own exit with tela.UsePresence and a tween that calls done (tela.OnDone), and a card that follows a drag through a spring (tela.Animate retargeted on every move, keeping its velocity) and springs back when it is released, with the pointer's release velocity (tela.Velocity).
|
Command motion shows Tela's explicit animations (ARCHITECTURE section 12): a dialog that enters with the starting: variant and leaves with h.Exit inside h.Presence, a toast that runs its own exit with tela.UsePresence and a tween that calls done (tela.OnDone), and a card that follows a drag through a spring (tela.Animate retargeted on every move, keeping its velocity) and springs back when it is released, with the pointer's release velocity (tela.Velocity). |
|
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. |
|
icons
|
|
|
lucide
module
|
|
|
internal
|
|
|
anim
Package anim is Tela's animation layer (ARCHITECTURE sections 8 and 12): the pieces that turn time into values, without knowing the element tree.
|
Package anim is Tela's animation layer (ARCHITECTURE sections 8 and 12): the pieces that turn time into values, without knowing the element tree. |
|
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, CSS grid, relative, absolute and fixed 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, CSS grid, relative, absolute and fixed 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). |





