Teaches coding agents how to build TUIs with TamboUI correctly: API-level selection, render-thread discipline, display-width safety, CSS-aware element authoring, and JFR conventions.
76
95%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Toolkit elements that carry per-instance state (scroll position, selection, cursor, expand/collapse, text-input buffer) must be held as fields on the ToolkitApp and re-used across renders. Building a fresh list(...), table(...), tree(...), or textInput(...) inside render() discards the state every frame. Why: the render cycle replaces the element instance, but events targeting the previous instance have already mutated its now-orphaned state — from the outside it looks identical to "scroll/selection doesn't work."
val / final field on the ToolkitApp subclass: private final ListElement<String> chatList = list().stickyScroll().scrollbar()...;render(), refresh the content of that field — chatList.elements(*chatItems) or .items(...) — rather than calling the factory againTableElement, TreeElement, anything ending in *Element that exposes a *State or a setter for its own collectionprivate final *State field (e.g., ListState, TreeState, TextInputState)State — if it has one, it is statefultextInput() called inlineoverride fun render() = panel("CHAT",
list(*chatItems).stickyScroll().scrollbar()
).rounded()private val chatList = list<String>().stickyScroll().scrollbar()
.id("chat").focusable()
override fun render() = panel("CHAT",
chatList.also { it.elements(*chatItems) }
).rounded()rules
skills