Claude-skill-registry castella-core
Build desktop, web, or terminal UIs with Castella. Create widgets, components, layouts, manage reactive state, handle events, and use the theme system.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/castella-core" ~/.claude/skills/majiayu000-claude-skill-registry-castella-core && rm -rf "$T"
skills/data/castella-core/SKILL.mdCastella Core UI Development
Castella is a pure Python cross-platform UI framework for desktop (GLFW/SDL2), web (PyScript/Pyodide), and terminal (prompt-toolkit) applications. Write once, run everywhere with GPU-accelerated rendering via Skia.
When to use: "create a Castella app", "build a Castella UI", "Castella component", "add a button/input/text", "use reactive state", "layout with Row/Column", "change the theme", "handle click events", "preserve scroll position", "animate a widget"
Quick Start
Create a minimal Castella app:
from castella import App, Text from castella.frame import Frame App(Frame("Hello", 800, 600), Text("Hello, Castella!")).run()
Install and run:
uv sync --extra glfw # Desktop with GLFW uv run python app.py
Core Concepts
App and Frame
- Window/container for the UIFrame(title, width, height)
- Application entry point withApp(frame, widget).run()- Frame auto-selects platform: GLFW (desktop), Web, or Terminal
from castella import App from castella.frame import Frame frame = Frame("My App", 800, 600) app = App(frame, my_widget) app.run()
Widgets
Base building blocks for UI elements:
| Widget | Description | Key Methods |
|---|---|---|
| Display text | |
| Clickable button | |
| Single-line input | |
| Multi-line editor | |
| Toggle checkbox | |
| Range slider | |
| Local image | - |
| Remote image | - |
| Rich markdown | |
Layout Containers
Arrange widgets hierarchically:
from castella import Column, Row, Box # Vertical stack Column( Text("Header"), Button("Click me"), Text("Footer"), ) # Horizontal stack Row( Button("Left"), Button("Right"), ) # Overlapping (z-index support) Box( main_content, modal_overlay.z_index(10), )
Component Pattern
Build reactive UIs with the
Component class:
from castella import Component, State, Column, Text, Button class Counter(Component): def __init__(self): super().__init__() self._count = State(0) self._count.attach(self) # Trigger view() on change def view(self): return Column( Text(f"Count: {self._count()}"), Button("+1").on_click(lambda _: self._count.set(self._count() + 1)), )
State Management
State[T] is an observable value that triggers UI rebuilds:
from castella import State count = State(0) # Create with initial value value = count() # Read current value count.set(42) # Set new value count += 1 # Operator support: +=, -=, *=, /=
ListState for Collections
ListState is an observable list:
from castella import ListState items = ListState(["a", "b", "c"]) items.append("d") # Triggers rebuild items.set(["x", "y"]) # Atomic replace (single rebuild)
Multiple States Pattern
When using multiple states, attach each to the component:
class MultiStateComponent(Component): def __init__(self): super().__init__() self._tab = State("home") self._counter = State(0) # Attach each state self._tab.attach(self) self._counter.attach(self) def view(self): return Column( Text(f"Tab: {self._tab()}"), Text(f"Count: {self._counter()}"), )
Size Policies
Control how widgets size themselves:
| Policy | Behavior |
|---|---|
| Exact size specified |
| Fill available space |
| Size to fit content |
Fluent API Shortcuts
from castella import SizePolicy # Fixed sizing widget.fixed_width(100) widget.fixed_height(40) widget.fixed_size(200, 100) # Content sizing widget.fit_content() # Both dimensions widget.fit_content_width() # Width only widget.fit_content_height() # Height only # Fill parent widget.fit_parent()
Important Constraint
A Layout with
CONTENT height_policy cannot have EXPANDING height children:
# This will raise RuntimeError: Column( Text("Hello"), # Text defaults to EXPANDING height ).height_policy(SizePolicy.CONTENT) # Fix by setting children to FIXED or CONTENT: Column( Text("Hello").fixed_height(24), ).height_policy(SizePolicy.CONTENT)
Styling
Widget Styling Methods
Chain style methods on widgets:
Text("Hello") .bg_color("#1a1b26") .text_color("#c0caf5") .fixed_height(40) .padding(10)
Border Styling
# Show border with theme's default color (or custom color) widget.show_border() # Use theme's border color widget.show_border("#ff0000") # Use custom color # Hide border (make it match background) widget.erase_border()
Theme System
Access and toggle themes:
from castella.theme import ThemeManager manager = ThemeManager() theme = manager.current # Get current theme manager.toggle_dark_mode() # Toggle dark/light manager.prefer_dark(True) # Force dark mode
Built-in themes: Tokyo Night (default), Cupertino, Material Design 3
See
references/theme.md for custom themes.
Event Handling
Click Events
Button("Click me").on_click(lambda event: print("Clicked!"))
Input Changes
Input("initial").on_change(lambda text: print(f"New value: {text}"))
Important: Input Widget Pattern
Do NOT attach states that Input/MultilineInput manages:
class FormComponent(Component): def __init__(self): super().__init__() self._text = State("initial") # DON'T attach - causes focus loss on every keystroke # self._text.attach(self) def view(self): return Input(self._text()).on_change(lambda t: self._text.set(t))
Animation
AnimatedState
Values that animate smoothly on change:
from castella import AnimatedState class AnimatedCounter(Component): def __init__(self): super().__init__() self._value = AnimatedState(0, duration_ms=300) self._value.attach(self) def view(self): return Column( Text(f"Value: {self._value():.1f}"), Button("+10").on_click(lambda _: self._value.set(self._value() + 10)), )
Widget Animation Methods
# Animate to position/size widget.animate_to(x=200, y=100, duration_ms=400) # Slide animations widget.slide_in("left", distance=100, duration_ms=300) widget.slide_out("right", distance=100, duration_ms=300)
See
references/animation.md for more animation patterns.
Scrollable Containers
Make layouts scrollable:
from castella import Column, ScrollState, SizePolicy class ScrollableList(Component): def __init__(self, items): super().__init__() self._items = ListState(items) self._items.attach(self) self._scroll = ScrollState() # Preserves scroll position def view(self): return Column( *[Text(item).fixed_height(30) for item in self._items], scrollable=True, scroll_state=self._scroll, ).fixed_height(300)
Z-Index Stacking
Layer widgets with z-index:
from castella import Box Box( main_content.z_index(1), modal_dialog.z_index(10), # Appears on top )
Semantic IDs for MCP
Assign semantic IDs for MCP accessibility:
Button("Submit").semantic_id("submit-btn") Input("").semantic_id("email-input")
Best Practices
- Attach states: Use
for each observable statestate.attach(self) - Fixed heights in scrollable containers: Use
for list items.fixed_height() - Preserve scroll: Use
to maintain scroll positionScrollState - Atomic list updates: Use
for single rebuildListState.set(items) - Don't attach Input states: Avoid attaching states managed by Input widgets
- Semantic IDs: Add
for MCP integration.semantic_id()
Running Scripts
# Counter example uv run python scripts/counter.py # Hot reload during development uv run python tools/hot_restarter.py scripts/counter.py
Packaging
Package your Castella app for distribution:
# Install ux bundler uv tool install ux-py # Create executable ux bundle --project . --output ./dist/
See
castella-packaging skill for detailed options (macOS app bundles, code signing, cross-compilation).
Reference
- Complete widget APIreferences/widgets.md
- Theme system detailsreferences/theme.md
- Animation patternsreferences/animation.md
- State management patternsreferences/state.md
- Executable examples (counter.py, form.py, scrollable_list.py)scripts/