ui-elements
Declarative, flexbox-based UI library for building overlays and notifications in the Talon Python runtime.
Install
mkdir -p .claude/skills/ui-elements && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/19438" && unzip -o skill.zip -d .claude/skills/ui-elements && rm skill.zipInstalls to .claude/skills/ui-elements
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Trigger when building or modifying Talon UI, HUD, overlay, notification, cheatsheet, or using ui_elements.Key capabilities
- →Construct custom user interfaces using a declarative React-inspired API
- →Implement layout structures using flexbox properties
- →Manage global application state across multiple UI components
- →Render visual elements on a Skia canvas
- →Handle user interactions like clicks and state changes
How it works
The library provides a declarative API that maps to a Skia canvas, allowing developers to define UI components and layouts using flexbox within Talon's Python runtime.
Inputs & outputs
When to use ui-elements
- →Create a custom voice control HUD
- →Build a persistent UI overlay for Talon
- →Design interactive notifications
- →Develop a cheatsheet UI component
About this skill
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_directiondefaults to"column"- children stack vertically. Useflex_direction="row"for horizontal.align_itemsdefaults 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
.pyfiles on save. - React-inspired declarative API with CSS-like properties (flexbox layout, styling kwargs).
- Import:
from talon import actions→ callactions.user.ui_elements(["div", "text", "screen", ...])to get element constructors. - All UI code lives in plain
.pyfiles 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") # single element returns directly
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()oractive_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 labelbutton("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,_leftvariants - 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 like45deg - 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) # Read-only snapshot
value, set_value = state.use("key", default) # Reactive (triggers re-render)
state.set("key", new_value) # Set directly
set_value(lambda prev: prev + 1) # Lambda setter
set_items(lambda prev: prev + [new_item]) # Append to list
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"}) # batch
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 # Read current value
input_ref.clear() # Clear the input
input_ref.focus() # Focus the input
See: docs/concepts/ref.md
Effects (Lifecycle)
effect = actions.user.ui_elements("effect")
# Mount only:
effect(on_mount, [])
# Mount + unmount:
effect(on_mount, on_unmount, [])
# State change:
effect(on_tab_change, ["active_tab"]) # deps are string state key names
# Mount with cleanup:
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"}, # Universal
"text": {"font_size": 14}, # Tag selector
"#my_id": {"background_color": "333333"}, # ID selector
".my_class": {"padding": 12, "border_radius": 6}, # Class selector
})
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 childinput_textor clicking a childbutton(type="submit")triggerson_submit. Callback receivesSubmitEvent(data={"input_id": "value", ...})with all child input values. Ctrl+Enter submits fromtextarea.button("label", on_click=fn)- Interactive button. Container when no label:button(on_click=fn)[icon("check")]. Usetype="submit"inside aformto trigger the form'son_submit.input_text(id="x")- Text input. Requiresid. Supportsplaceholder,autofocus,on_change.textarea(id="x", rows=5)- Multi-line input. Requiresid.select(id="x", options=[...])- Dropdown. Requiresid. Options: strings or{"label": "...", "value": "..."}dicts.checkbox(checked=True, on_change=fn)- Toggle. Useson_changenoton_click.switch(checked=True, on_change=fn, animated=True)- Toggle switch. Useson_changenoton_click.animatedenables smooth transitions.sizescales dimensions (default 14).link("text", url="...")- Clickable URL.close_on_click=Trueto 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. Usesizeandview_box, notwidth/height/viewBox. Icons use a 24x24 viewbox. To create a custom icon:
svg, path, circle = actions.user.ui_elements(["svg", "path", "circle"])
# defaults: size=24, view_box="0 0 24 24", stroke_width=2, stroke_linecap/linejoin="round"
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) # Show
actions.user.ui_elements_show(my_ui, initial_state={...}) # With initial state
actions.user.ui_elements_show(my_ui, duration="2s") # Auto-hide
actions.user.ui_elements_show(my_ui, on_mount=fn, on_unmount=fn)
---
*Content truncated.*
When not to use it
- →Building web or browser-based interfaces
- →Using HTML, CSS, or DOM APIs
- →Projects requiring non-Talon runtime environments
Prerequisites
Limitations
- →No support for standard web technologies like HTML or CSS
- →Ref values are unavailable during initial render
- →Checkbox and switch elements require on_change instead of on_click
How it compares
Unlike standard web development, this library renders directly to a Skia canvas within the Talon environment without using HTML, CSS, or browser APIs.
Compared to similar skills
ui-elements side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| ui-elements (this skill) | 0 | 3mo | No flags | Intermediate |
| gemini-logo-remover | 9 | 7mo | Review | Beginner |
| slack-gif-creator | 6 | 3mo | Review | Intermediate |
| nano-image-generator | 1 | 5mo | Review | Beginner |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
gemini-logo-remover
bear2u
Remove Gemini logos, watermarks, or AI-generated image markers using OpenCV inpainting. Use this skill when the user asks to remove Gemini logo, AI watermark, or any logo/watermark from images.
slack-gif-creator
anthropics
Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."
nano-image-generator
solidSpoon
Generate images using Nano Banana Pro (Gemini 3 Pro Preview). Use when creating app icons, logos, UI graphics, marketing banners, social media images, illustrations, diagrams, or any visual assets. Triggers include phrases like 'generate an image', 'create a graphic', 'make an icon', 'design a logo', 'create a banner', or any request needing visual content.
image-crop-rotate
instavm
Image processing skill for cropping images to 50% from center and rotating them 90 degrees clockwise. This skill should be used when users request image cropping to center, image rotation, or both operations combined on image files.
sora
davila7
Use when the user asks to generate, remix, poll, list, download, or delete Sora videos via OpenAI’s video API using the bundled CLI (`scripts/sora.py`), including requests like “generate AI video,” “Sora,” “video remix,” “download video/thumbnail/spritesheet,” and batch video generation; requires `OPENAI_API_KEY` and Sora API access.
post-process-logo
tradingstrategy-ai
Post-process original logos into standardised 256x256 PNG format