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 | |
|---|---|
| Containers | column, row, div, overlay, grid |
| Text | text, rich, span, markdown |
| Controls | button, input, textarea, checkbox, slider, progress |
| Images and canvas | image, svg, canvas, path, linear, bitmap, shader |
| Scrolling | scroll, scroll_x, list |
| Dynamic content | show, switch, each, presence |
| Overlays and menus | modal, popover, menu, menu_item, submenu, context_menu |
| Web views, terminals, commands | webview, web, terminal, term, command |
| State and components | state, shared, persist, use_state, computed, effect, batch, ref, component, on_mount, slot, contribute, actions |
| Animation | the anime global (anime.js v4), tween, spring, loop |
| Properties | every style property, grouped |
| Design systems | variants, raw, theme.strict: component variants and the design lint |
| Events | pointer, keyboard, focus, lifecycle, drag and drop |
| App structure | splitting 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 | |
|---|---|
Node | what element functions return; a child |
Child | a 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 |
Props | every element’s properties: styles, identity, events (no children) |
ContainerProps | Props and children |
Styles | plain style values, for hover, active, focus, disabled |
Color, Length | "#rrggbb", "rgb(…)", 0xrrggbb; points or "50%", "auto" |
Element, Ref | a 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?).
Design systems
A design system in zgx is three layers: theme tokens hold the values ("$accent", "$space.3"; see theming), ui.variants turns them into each component's styles, and the design lint keeps code from going around them.
API index
Every function and value of ui and anime, with the type luau-lsp checks it against, read from zgx.d.luau when the site builds.