zgx

Reading the reference

One page per element and API group of zgx-script. Each gives the signature, every option with its type and default, a short example and the gotchas. The examples are complete scripts: cargo test -p zgx-script --test reference_docs runs every one headless and type checks it in strict mode.

Page
Containerscolumn, row, div, overlay, grid
Texttext, rich, span, markdown
Controlsbutton, input, textarea, checkbox, slider, progress
Images and canvasimage, svg, canvas, path, linear, bitmap, shader
Scrollingscroll, scroll_x, list
Dynamic contentshow, switch, each, presence
Overlays and menusmodal, popover, menu, menu_item, submenu, context_menu
Web views, terminals, commandswebview, web, terminal, term, command
State and componentsstate, shared, persist, use_state, computed, effect, batch, ref, component, on_mount, slot, contribute, actions
Animationthe anime global (anime.js v4), tween, spring, loop
Propertiesevery style property, grouped
Design systemsvariants, raw, theme.strict: component variants and the design lint
Eventspointer, keyboard, focus, lifecycle, drag and drop
App structuresplitting an app across modules with shared state

Reading the signatures

Signatures are Luau types from zgx.d.luau, the file editors and zgx check use. Its export types are global type names a script can use in its own annotations:

Type
Nodewhat element functions return; a child
Childa node, a string (text, {name} shows state), a function returning text, a list of them, false or nil
State<T>Rust-owned state holding a T (see State)
Reactive<T>what a property takes: a T, a state of a T, a function returning one, or a binding
Propsevery element’s properties: styles, identity, events (no children)
ContainerPropsProps and children
Stylesplain style values, for hover, active, focus, disabled
Color, Length"#rrggbb", "rgb(…)", 0xrrggbb; points or "50%", "auto"
Element, Refa mounted node, and what follows one (see Events)
Merge<A, B>A with B’s properties: Merge<ContainerProps, { title: string }>
VariantProps<R>a ui.variants recipe’s axes as props (see Design systems)

Each element function also has its own props type (InputProps, ListProps, EachOptions…), named on its page. Every props and options table is closed: an unknown key is an error, which luau-lsp reports as the key’s type not being number (Expected this to be 'number', but got '"bgg"', for bgg). A missing required option (an input’s value) is reported as a missing field.

Checking

zgx check app.luau (or a directory) runs luau-lsp in strict mode with these types; scripting has the editor setup. In a debug build a hot-reloading host runs the same check after every save and shows the errors in the window’s banner, when luau-lsp is installed.

Luau infers a callback’s parameters from how its body uses them when the callback is passed to a generic function, which misfires, so the few callbacks whose argument is the script’s own data take it as any: each’s row function, presence’s content function, drag payloads, a web page’s messages. Annotate those parameters to have them checked:

type Task = { id: string, title: string, done: boolean }
local tasks = ui.state("tasks", {} :: { Task })
return ui.each(tasks, function(task: Task)
	return ui.text { task.title }
end)

State takes the type of its initial value. Give nil and empty tables a type, as above: ui.state("selected", nil :: string?).

Edit on GitHub