| name | ui-elements |
| description | Trigger when building or modifying Talon UI, HUD, overlay, notification, cheatsheet, or using ui_elements. |
talon-ui-elements
Python canvas UI library for Talon voice control runtime.
- NOT web/browser - renders on a Skia canvas. No HTML, CSS, DOM, or browser APIs.
- Everything is flexbox. No
display: block/inline/grid. Every container is a flex container.
flex_direction defaults to "column" - children stack vertically. Use flex_direction="row" for horizontal.
align_items defaults to "stretch" on containers (divs), "flex_start" on screen.
- Runs inside Talon's Python runtime (no pip, no virtualenv, no build step). Talon hot-reloads
.py files on save.
- React-inspired declarative API with CSS-like properties (flexbox layout, styling kwargs).
- Import:
from talon import actions → call actions.user.ui_elements(["div", "text", "screen", ...]) to get element constructors.
- All UI code lives in plain
.py files inside the Talon user directory.
Minimal Example
from talon import actions
def hello_world_ui():
div, text, screen = actions.user.ui_elements(["div", "text", "screen"])
return screen(justify_content="center", align_items="center")[
div(background_color="#333333", padding=16, border_radius=8, border_width=1)[
text("Hello world", color="#FFFFFF", font_size=24)
]
]
def show_hello_world():
actions.user.ui_elements_show(hello_world_ui)
def hide_hello_world():
actions.user.ui_elements_hide(hello_world_ui)
def toggle_hello_world():
actions.user.ui_elements_toggle(hello_world_ui)
Getting Elements
Destructure element constructors from actions.user.ui_elements(...):
div, text, screen = actions.user.ui_elements(["div", "text", "screen"])
button, input_text, state = actions.user.ui_elements(["button", "input_text", "state"])
ref, effect, icon = actions.user.ui_elements(["ref", "effect", "icon"])
style, component = actions.user.ui_elements(["style", "component"])
window = actions.user.ui_elements("window")
All elements: div, text, code, screen, button, input_text, textarea, select, data_table, form, state, ref, effect, icon, style, component, link, checkbox, switch, table, tr, td, th, window, active_window
SVG elements (must be used inside svg()): svg, path, rect, circle, line, polyline, polygon
Element Hierarchy
- Root must be
screen() or active_window()
- Children via bracket syntax:
parent()[child1, child2]
- Containers (can have children):
div, form, window, table, tr, td, th, screen, active_window, svg
- Leaves (no children):
text, code, icon, checkbox, switch, input_text, textarea, select, data_table, link
button: leaf with label button("Click"), container without: button(on_click=fn)[icon("check")]
- Dynamic lists:
div()[*[text(item) for item in items]]
See patterns.md for common layout patterns, tab navigation, and complete examples.
Properties Quick Reference
All properties are kwargs: div(background_color="333333", padding=16).
- Layout:
flex_direction ("column"/"row"), justify_content, align_items, align_self, flex, gap, flex_wrap
- Sizing:
width, height, min_width, max_width, min_height, max_height - pixels or "100%"
- Spacing:
padding, margin - plus _top, _right, _bottom, _left variants
- Position:
position ("static"/"relative"/"absolute"/"fixed"), top, left, right, bottom
- Colors:
background_color, color, border_color - hex strings ("FF0000", "#FF0000", "FF000080") or named colors
- Gradients:
background="linear_gradient(to_right, FF0000, 0000FF)" - directions: to_right, to_left, to_bottom, to_top, or angle like 45deg
- Font:
font_size (default 16), font_weight ("normal"/"bold"), font_family, text_align ("left"/"center"/"right"), white_space ("normal"/"nowrap")
- Border:
border_width, border_radius, border_color - plus individual sides (border_top, etc.)
- Interactivity:
on_click, on_change, highlight_style, disabled, draggable, drag_handle, autofocus
- Animation:
transition, mount_style, unmount_style
- Identity:
id, key, class_name, z_index
- Scrolling:
overflow / overflow_y / overflow_x = "scroll" | "hidden"
- Other:
opacity (0.0-1.0, cascades), drop_shadow
See properties.md for full tables with types and defaults.
State Management
All state is global - any UI or voice command can read/write any key. State is shared across UIs, so one UI can react to state set by another UI or by a voice command.
state = actions.user.ui_elements("state")
value = state.get("key", default)
value, set_value = state.use("key", default)
state.set("key", new_value)
set_value(lambda prev: prev + 1)
set_items(lambda prev: prev + [new_item])
External access (from voice commands / outside UI):
actions.user.ui_elements_set_state("key", value)
actions.user.ui_elements_set_state("count", lambda prev: prev + 1)
actions.user.ui_elements_set_state({"key1": "val1", "key2": "val2"})
actions.user.ui_elements_get_state("key")
actions.user.ui_elements_get_state("key", "default_value")
Initial state (pre-set before first render):
actions.user.ui_elements_show(my_ui, initial_state={"tab": "home", "items": []})
See: docs/concepts/state.md
Refs
Direct element access for input fields. Requires matching id prop. Only use in callbacks or effects, not during initial render.
ref = actions.user.ui_elements("ref")
input_ref = ref("my_input")
input_ref.value
input_ref.clear()
input_ref.focus()
See: docs/concepts/ref.md
Effects (Lifecycle)
effect = actions.user.ui_elements("effect")
effect(on_mount, [])
effect(on_mount, on_unmount, [])
effect(on_tab_change, ["active_tab"])
def on_mount():
return lambda: print("Cleanup!")
effect(on_mount, [])
See: docs/concepts/effect.md
Style Blocks
CSS-like selectors for shared styles:
style = actions.user.ui_elements("style")
style({
"*": {"color": "CCCCCC"},
"text": {"font_size": 14},
"#my_id": {"background_color": "333333"},
".my_class": {"padding": 12, "border_radius": 6},
})
button("Click", class_name="my_class")
See: docs/concepts/style.md
Elements Quick Reference
screen() / active_window() - Root containers. screen(1) for second monitor. active_window() follows focused OS window.
div() - Generic container.
text("content") - Display text.
code("def hello():") - Syntax-highlighted code. Props: language ("python"/"talon"), theme, diff=True, copyable=True. Monospace font by default.
form(on_submit=fn) - Form container. Enter in child input_text or clicking a child button(type="submit") triggers on_submit. Callback receives SubmitEvent(data={"input_id": "value", ...}) with all child input values. Ctrl+Enter submits from textarea.
button("label", on_click=fn) - Interactive button. Container when no label: button(on_click=fn)[icon("check")]. Use type="submit" inside a form to trigger the form's on_submit.
input_text(id="x") - Text input. Requires id. Supports placeholder, autofocus, on_change.
textarea(id="x", rows=5) - Multi-line input. Requires id.
select(id="x", options=[...]) - Dropdown. Requires id. Options: strings or {"label": "...", "value": "..."} dicts.
checkbox(checked=True, on_change=fn) - Toggle. Uses on_change not on_click.
switch(checked=True, on_change=fn, animated=True) - Toggle switch. Uses on_change not on_click. animated enables smooth transitions. size scales dimensions (default 14).
link("text", url="...") - Clickable URL. close_on_click=True to hide UI after click.
icon("name", size=24) - Built-in SVG icon (Lucide-style, 24x24 viewbox). ~48 available names: arrow_down, arrow_left, arrow_right, arrow_up, check, chevron_down, chevron_left, chevron_right, chevron_up, close, clock, copy, delta, diamond, download, edit, external_link, file, file_text, folder, home, maximize, menu, mic, minimize, minus, more_horizontal, more_vertical, multiply, pause, play, plus, rotate_left, settings, shrink, star, stop, trash, upload.
window(title="...") - Draggable panel with title bar, minimize, close buttons.
table() / tr() / th() / td() - Table structure.
component(fn, props) - Reusable UI with local state (state.use_local). Only needed for local state or scoped styles.
svg() / path() / rect() / circle() / line() - Custom SVG. Use size and view_box, not width/height/viewBox. Icons use a 24x24 viewbox. To create a custom icon:
svg, path, circle = actions.user.ui_elements(["svg", "path", "circle"])
svg()[
circle(cx=12, cy=12, r=10),
path(d="M12 6v6l4 2"),
]
See: docs/elements.md
Show / Hide API
actions.user.ui_elements_show(my_ui)
actions.user.ui_elements_show(my_ui, initial_state={...})
actions.user.ui_elements_show(my_ui, duration="2s")
actions.user.ui_elements_show(my_ui, on_mount=fn, on_unmount=fn)
actions.user.ui_elements_show(my_ui, show_hints=False)
actions.user.ui_elements_hide(my_ui)
actions.user.ui_elements_hide("screen_id")
actions.user.ui_elements_toggle(my_ui)
actions.user.ui_elements_hide_all()
actions.user.ui_elements_is_active(my_ui)
Every interactive element (buttons, inputs, links, checkboxes, switches) automatically gets a voice-activated 2-letter hint label. Users say the letters to click the element. This is on by default - pass show_hints=False to disable for UIs that don't need voice interaction (e.g. display-only HUDs).
See: docs/actions.md
Imperative Actions (from outside the UI)
actions.user.ui_elements_set_text("element_id", "new text")
actions.user.ui_elements_set_text("element_id", lambda current: current + "!")
actions.user.ui_elements_highlight("element_id")
actions.user.ui_elements_unhighlight("element_id")
actions.user.ui_elements_highlight_briefly("element_id")
actions.user.ui_elements_get_input_value("input_id")
Reactive State vs Decoration Layer
Choose your update strategy based on whether layout changes:
- Reactive (
state.use) - for forms, wizards, dashboards. Layout adds/removes/resizes elements. Re-renders on change. Use when interactions are human-speed (clicks, typing).
- Decoration layer (
highlight, set_text) - for game overlays, real-time HUDs. Layout renders once; only appearance changes (color, text content). No re-render, no layout recalc. Use when updates are rapid or performance-critical.
The decoration layer paints on top of existing elements without touching layout. highlight changes an element's visual state (background, border, color). set_text swaps displayed text. Neither can add, remove, or reposition elements - use state for that.
div(id="jump", highlight_style={"background_color": "87ceeb88"}, **key_style)[text("jump")]
actions.user.ui_elements_highlight_briefly("jump")
actions.user.ui_elements_highlight("jump")
actions.user.ui_elements_unhighlight("jump")
actions.user.ui_elements_set_text("apm", str(apm_count))
See paradigms.md for complete examples of both approaches.
Gotchas
flex_direction defaults to "column", not "row". Children stack vertically. Use flex_direction="row" for horizontal.
- Must CALL elements before brackets:
div()[...] not div[...]. Forgetting () raises TypeError.
- Root must be
screen() or active_window(). No bare div() as root.
- Children via
[], not function args. div()[text("hi")] not div(text("hi")).
input_text, textarea, select require id prop. input_text(id="my_input") not input_text().
position required for top/left/right/bottom. div(top=10, position="relative") not div(top=10).
- Loop variable capture:
lambda e, item=item: delete(item) not lambda e: delete(item).
- Ref values not available during initial render. Use refs in callbacks or effects only.
- Effect deps are string state key names:
effect(fn, ["count"]) not effect(fn, [count]).
effect() must be called during render (inside UI function body), not outside or in a callback.
show() twice is a no-op. To restart, hide first then show.
checkbox and switch use on_change, not on_click. on_click will raise an error.
svg() is not HTML <svg>. Use size and view_box (underscore), not width/height/viewBox/xmlns.
Defaults
| Property | Default |
|---|
flex_direction | "column" |
align_items | "stretch" (containers), "flex_start" (screen) |
justify_content | "flex_start" |
font_size | 16 |
color | "FFFFFF" |
background_color | None (transparent) |
border_color | "555555" |
position | "static" |
font_weight | "normal" |
text_align | "left" |
z_index | 0 |
Cascaded (inherited by children): color, font_family, font_size, highlight_style, opacity, stroke, stroke_width, z_index
Further Reading
references/properties.md - Full property tables with types and values
references/patterns.md - Layout patterns, tab navigation, splitting files, complete examples
references/paradigms.md - Reactive state vs decoration layer: full examples of both approaches
docs/elements.md - All elements with detailed examples
docs/actions.md - All Talon actions
docs/concepts/state.md - State management deep dive
docs/concepts/effect.md - Lifecycle effects
docs/concepts/ref.md - Imperative ref system
docs/concepts/components.md - Reusable components
docs/concepts/style.md - CSS-like styling
docs/concepts/transitions.md - Animations
docs/concepts/window.md - Window element
docs/concepts/svgs.md - Custom SVG graphics
docs/tutorials/ - Step-by-step tutorials (hello_world, cheatsheet, game_keys)
examples/ - Runnable example UIs (dashboard, cheatsheet, todo_list, game_keys, inputs, etc.)
storybook/ - Per-element storybook for visual/behavioral reference