| name | textual-tui |
| description | Build, refactor, debug, test, and package Python terminal user interfaces with Textual. Use when the user wants a TUI, terminal dashboard, admin console, multi-screen workflow, keyboard-first tool, data explorer, file browser, markdown or log viewer, editor, command palette, browser-served console app, or a migration from curses/Rich-only UI to Textual—even if they never say “Textual”. Covers TCSS and themes, built-in widgets, screens and modes, reactive state, workers, browser delivery APIs, and pytest Pilot or snapshot testing. |
| license | Proprietary |
| compatibility | Requires Python 3.9+ and the Textual package. `textual-dev`, `pytest`, and `pytest-asyncio` are strongly recommended. `pytest-textual-snapshot` and `textual[syntax]` are optional extras. |
| metadata | {"version":"2.0","scope":"Textual apps, TUIs, browser-served Textual apps, testing, refactors"} |
Use this skill when the task is fundamentally about building or changing a Textual app, not merely printing Rich output or writing a non-interactive CLI.
Start by classifying the app
Pick the closest shape before writing code:
-
Single-screen shell
One main view with panels, tables, forms, or logs. Prefer containers plus built-in widgets.
-
Multi-screen workflow
Large context changes, separate flows, or drill-down views. Prefer Screen / ModalScreen.
-
Multi-mode admin app
Persistent top-level areas such as “dashboard / jobs / settings / logs”. Prefer named MODES, screen stacks, and command palette support.
-
Data explorer
Records plus details, filters, or side panes. Prefer DataTable, details panel, responsive breakpoints, and keyboard navigation.
-
Document or filesystem tool
Prefer DirectoryTree, MarkdownViewer, TextArea, Tree, and delivery APIs for export/download.
-
Chat / streaming / long-running task UI
Prefer a scrollable transcript or log plus @work / workers for background operations.
If the user has not chosen an architecture, choose one and proceed.
Default engineering stance
- Prefer built-in widgets first. Only hand-roll behaviour when a built-in widget clearly does not fit.
- Keep the
App thin. Move screen-specific logic into Screen classes and reusable composite widgets.
- Prefer
.tcss files over inline CSS once styling grows beyond a toy example.
- Use IDs and semantic classes deliberately so styling and Pilot tests stay stable.
- Design for narrow terminals first, then add split panes and breakpoint-driven layouts.
- Leave behind tests whenever behaviour changes.
Choose the right Textual primitive
- Use
Screen when navigation changes the user’s working context.
- Use
ModalScreen for short interruptions: confirmations, pickers, destructive actions.
- Use
ContentSwitcher for wizard steps or one-screen subflows.
- Use named
MODES when the app has durable top-level areas with separate navigation stacks.
- Use command palette providers when there are many actions, bindings, or discoverability matters.
- Use workers for network, subprocess, parsing, search, sleeps, or anything that may block input.
See:
Widget-first selection rules
Before inventing custom widgets, check the widget atlas.
Common defaults:
DataTable for record-heavy views
DirectoryTree for filesystem navigation
MarkdownViewer for rich document views
TextArea for editing
TabbedContent for grouped settings or alternate panes
Log / RichLog for live output
SelectionList, OptionList, ListView, Tree, Select, Switch, Input, Button for most interaction needs
Reactivity and workers
Use the playbook in reactivity and workers.
Core rules:
- Put fast derived state in
compute_*, but keep it cheap and side-effect free.
- Use
watch_* for UI reactions, not blocking work.
- Use
var when you want state without automatic refresh machinery.
- Use
set_reactive before mount when initial state changes should not trip watchers early.
- Move blocking work into
@work or run_worker(...).
- Use
exclusive=True for stale-search cancellation and similar “latest request wins” flows.
- For thread workers, update the UI via messages or
call_from_thread.
Browser, dev loop, and delivery
Textual may run in a terminal or be served to a browser. Build with both in mind when relevant.
- Use
textual run --dev while iterating.
- Use
textual console and devtools when behaviour is unclear.
- Use
textual serve when browser parity matters.
- Prefer
deliver_text, deliver_binary, or deliver_screenshot for browser-friendly exports and downloads.
- Use
open_url when handing off to the user’s browser is appropriate.
See:
Testing is part of the feature
Default output after any non-trivial change:
- one smoke test with
run_test()
- one behaviour test for the changed flow
- one narrow-terminal or alternate-size test when layout matters
- one snapshot test when the view structure matters visually
See testing matrix.
When working on an existing project
Start with the scripts, then refine by hand:
python scripts/inspect_textual_project.py <project>
python scripts/audit_textual_project.py <project>
- Generate scaffolds or tests only after you understand the existing structure.
Use the audit to catch:
- oversized
App classes
- blocking handlers
- missing breakpoints
- missed built-in widget opportunities
- missing command palette or delivery APIs
- missing Pilot tests
Bundled scripts
-
scripts/scaffold_textual_app.py
Generate starter apps, TCSS, tests, optional pyproject.toml, and CI workflow.
-
scripts/inspect_textual_project.py
Inventory app classes, screens, widgets, bindings, IDs, workers, and styling.
-
scripts/audit_textual_project.py
Heuristic architecture/performance/test audit for an existing Textual project.
-
scripts/generate_pilot_tests.py
Emit starter smoke and behaviour tests for an existing app.
-
scripts/dump_dom_and_bindings.py
If Textual is installed, launch an app under run_test() and dump DOM and active bindings.
-
scripts/emit_textual_pyproject.py
Generate a packageable Hatch-based pyproject.toml.
-
scripts/emit_github_actions_ci.py
Generate a GitHub Actions workflow for Textual tests.
-
scripts/build_upstream_pattern_atlas.py
Summarise a local Textual repo snapshot into references/repo-map.md and references/upstream-pattern-atlas.md.
-
scripts/self_check.py
Compile scripts and scaffold all bundled templates as a package validation step.
Bundled starter templates
Available scaffolds:
dashboard
form
chat
data-explorer
file-browser
settings
wizard
log-monitor
editor
admin-modes
download-demo
List them with:
python scripts/scaffold_textual_app.py --list-templates
Generate one with:
python scripts/scaffold_textual_app.py \
--template data-explorer \
--module my_app \
--class-name MyApp \
--app-title "My App" \
--output-dir .
Output checklist
Before you finish, aim to leave behind:
- a clear app structure
- stable IDs/classes for styling and tests
- TCSS separated from Python unless the app is tiny
- background work off the main event path
- keyboard-discoverable actions
- responsive layout decisions
- at least a smoke test and one behaviour test
- notes on how to run the app in dev mode
Read next as needed