claude/skills/tui-design/SKILL.md
Create distinctive, production-grade terminal user interfaces with high usability and professional polish. Use this skill when the user asks to build TUI applications, terminal dashboards, interactive CLI tools, or any full-screen terminal interface (system monitors, git clients, database explorers, log viewers, file managers, REPL wrappers). Supports Rust (ratatui/crossterm) and Python (Textual/Rich). Use when the user says "build a dashboard", "terminal UI", "interactive CLI", "TUI app", "system monitor", "log viewer", or invokes /tui-design.
npx skillsauth add paulnsorensen/dotfiles tui-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Design and build professional TUIs. Character grids, not canvases.
Delegate to the right skill for each phase of TUI development:
| Phase | Skill | Why |
|-------|-------|-----|
| Search codebase for patterns | scout | rg/fd for fast file and content search |
| Understand existing code structure | LSP | Symbol lookup, cross-references, type info |
| Look up ratatui/Textual/crossterm docs | fetch | Context7 for version-specific library docs |
| Find structural code patterns | trace | ast-grep for "what implements X?" questions |
| Edit existing files precisely | chisel | sd for multi-file replacements, Edit for precision |
| Pre-commit smoke test | diff | Catch secrets, debug statements, silent failures |
| Stage and commit | commit | Conventional commits, no force-push |
| GitHub operations (PR, push) | gh | gh CLI for all GitHub ops |
Use fetch with Context7 FIRST when working with ratatui, crossterm, Textual, Rich, or
cursive APIs. These libraries evolve fast — don't rely on training data for API specifics.
Before coding, commit to a clear interaction model:
? help.CRITICAL: The terminal is not a web browser. You have a fixed character grid, no fonts, no subpixel rendering, limited color in some environments, and keybinding conflicts with terminal emulators and multiplexers. Design within these constraints — don't fight them.
Then implement working code that is:
+-+||+-+) for standard panels, double-line sparingly for emphasis/focus. Round corners for softer feel. Consistent style across the app. 1 char internal padding minimum.NO_COLOR.[MODE] | context | metadata | position | keybinding hints. Temporary messages overwrite 3-5 seconds, then restore. Use the CVD-safe palette above — always with text prefix ([OK], [ERR], [WARN]).NEVER hard-code colors assuming dark background. NEVER rely on color alone. NEVER mix border styles. NEVER skip the status bar.
Universal conventions — users expect them:
| Key | Action | Notes |
|-----|--------|-------|
| q / Ctrl-C | Quit | q normal exit, Ctrl-C interrupt |
| ? / F1 | Help overlay | All keybindings for current context |
| / | Search/filter | Open search input |
| Enter | Confirm/select/open | Primary action key |
| Esc | Cancel/back/close | Return to previous state |
| j/k AND up/down | Navigate up/down | Always support BOTH |
| h/l AND left/right | Navigate left/right | Collapse/expand in trees |
| Tab / Shift-Tab | Next/previous panel | |
| Space | Toggle/multi-select | |
| gg / G | Jump to top/bottom | |
| Ctrl-D / Ctrl-U | Half-page down/up | |
| n/N | Next/prev search result | After / search |
Ctrl-S (freezes terminal), Ctrl-Q (XON), Ctrl-Z (SIGTSTP), Ctrl-\ (SIGQUIT)Ctrl-Shift-* (reserved by terminal emulators)Ctrl-I = Tab, Ctrl-M = Enter, Ctrl-H = Backspace, Ctrl-[ = Escape (physical collisions)? help overlay: Full keybinding reference for the current view: or Ctrl-P for searchable actions in complex appsUse prefix keys (gg, dd) instead of modifier chords for maximum terminal compatibility. Make all keybindings user-configurable. Test inside tmux.
| Risk | Pattern | Example |
|------|---------|---------|
| Reversible | Undo instead of confirm | Done. Press u to undo (10s) |
| Low | Force-flag | :q vs :q! |
| Moderate | Inline y/N | Delete config.yaml? [y/N]: (capitalize safe default) |
| High | Modal dialog | Centered overlay with OK/Cancel |
| Catastrophic | Type-to-confirm | Type "production-db" to confirm: |
Overusing confirmations causes habituation — users auto-click "yes", defeating the purpose.
Rust (ratatui + crossterm):
Note: ratatui::init() handles startup/cleanup and panic hooks automatically. The
manual crossterm calls below are only needed for PTY handoff (shelling out mid-session):
fn shell_out<B: Backend>(terminal: &mut Terminal<B>, cmd: &str, args: &[&str]) -> io::Result<ExitStatus> {
crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
crossterm::terminal::disable_raw_mode()?;
crossterm::execute!(io::stdout(), crossterm::cursor::Show)?;
let result = std::process::Command::new(cmd).args(args).status();
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?;
terminal.clear()?;
result
}
Python (Textual):
with self.app.suspend():
os.system("vim file.txt") # Terminal fully restored during this block
Always restore terminal state on every exit path: normal exit, panic, signals.
ratatui::init() handles panic hooks automaticallyApp.run() lifecycle handles cleanup| Tier | Detection | Use |
|------|-----------|-----|
| Truecolor (24-bit) | COLORTERM=truecolor | Default for modern terminals |
| 256-color | TERM contains -256color | Older emulators |
| 16-color ANSI | Everything else | Maximum compatibility |
Disable all color when NO_COLOR is set.
Every color choice must answer "what does this color mean?" If the answer is "it looked nice," it's wrong. Centralize color decisions:
Theme struct with semantic slots (primary, surface, error, muted). Pass &theme to render functions. Use Color::Reset for terminal-adaptive fg/bg. See references/ratatui.md..tcss semantic variables ($surface, $primary, $accent). One .tcss file per theme for runtime switching. See references/textual.md.Handle SIGWINCH by recalculating layouts and full redraw. Collapse sidebars below 80 cols, hide metadata below 60. Design for 80x24 minimum, optimize for 120x40+.
CSI ? 2026 h / l): like VSync for terminalsAI assistants produce these predictable mistakes in TUI code. Check every one before
presenting output. These are the TUI-specific equivalents of the de-slop patterns.
A 100+ line ui() / render() with nested layout math and inline styling.
Fix: Delegate to per-panel render functions. Each panel is one function, < 40 lines.
Color::White on Color::Black, or fg="white" in Textual — invisible on light terminals.
Fix: Use Color::Reset (Rust) or $surface/$text (Textual). Define a Theme, never scatter RGB at use sites.
Renders sidebar at any width, truncates to garbage below 60 cols.
Fix: Check area.width and collapse panels responsively. Test at 40, 80, and 200 cols.
Network fetch or file I/O inline in the render loop — drops frames, freezes UI.
Fix: Background task via tokio::spawn + mpsc (Rust) or @work (Textual). Main loop only does recv + draw.
Business logic inside the render closure. Mutations during draw.
Fix: TEA split — App struct owns state, handle_event mutates, render is pure read-only.
Manual enable_raw_mode() without panic hook — crash leaves terminal trashed.
Fix: Use ratatui::init() (installs panic hook automatically). Textual's App.run() handles this.
Actions wired to keys but never shown in status bar or ? help.
Fix: Central keybinding table that feeds BOTH the action handler AND the help display.
// Create the layout, // Handle quit key, // Render the list — narrating every line.
Fix: Delete comments that restate code. TUI code is visual — the structure speaks for itself.
WidgetFactory, RenderManager, LayoutBuilder for a 3-panel app.
Fix: Functions, not abstractions. Extract a trait only when 3+ components genuinely share behavior.
Study these production TUIs for patterns worth stealing:
.tcss theme files, runtime theme switchingThe bar is: would your code look at home in these codebases?
ratatui::init() / ratatui::restore().references/ratatui.md for concrete skeletons.StatefulWidget for scroll/selection state, Constraint::Fill(1) for flexible layouts, Layout::vertical/horizontal builder style.EventStream + mpsc channels. Never block the render loop.Theme struct with semantic color slots — pass &theme to every render function.fetch skill with Context7 for ratatui API lookups — the API surface is large.compose() + yield widget trees, Screen push/pop for navigation, reactive attributes with watch_* callbacks, Worker with @work(exclusive=True) for async I/O. See references/textual.md for patterns..tcss files with semantic variables ($surface, $primary, $accent). Multiple .tcss files for runtime theme switching.CommandPalette for apps with many actions (Ctrl-P searchable commands).fetch skill with Context7 for Textual API lookups.Generate tests alongside the implementation — a TUI without tests is incomplete.
#[test]
fn renders_main_view() {
let backend = TestBackend::new(80, 24);
let mut terminal = Terminal::new(backend).unwrap();
let app = App::with_test_data();
terminal.draw(|f| app.render(f)).unwrap();
insta::assert_snapshot!(terminal.backend().to_string());
}
#[test]
fn narrow_terminal_hides_sidebar() {
let backend = TestBackend::new(40, 24);
// ... render and assert sidebar content absent
}
#[test]
fn quit_key_exits() {
let mut app = App::new();
app.handle_event(key_event('q')).unwrap();
assert!(!app.running);
}
async def test_search_filters(snap_compare):
app = MyApp()
async with app.run_test() as pilot:
await pilot.press("slash")
await pilot.type("query")
await pilot.press("enter")
await pilot.pause()
results = app.query(ResultItem)
assert all("query" in r.label.plain for r in results)
async def test_main_view(snap_compare):
assert await snap_compare("myapp/app.py", terminal_size=(80, 24))
Every TUI must be verified against these scenarios:
See references/ratatui.md and references/textual.md for full test examples.
Build a TUI when: exploring unknown state, multiple data views needed simultaneously, user would chain CLI commands, real-time monitoring adds value.
Build a CLI when: output needs piping, runs in CI/automation, single command-to-result.
Best practice: Build the CLI core first, add TUI as interactive layer. Show what CLI commands the TUI executes — lazygit's command log is beloved for this.
NO_COLOR / TERM=dumb handling — always test with color disabledFrame lifetime constraints trip up async code — keep render logic synchronoustools
Reconstruct what a past coding-agent session was doing so you can resume it — goal, files touched, last verified state, and the next step — by querying the session logs. Use when the user says "what was I working on", "recover that session", "reconstruct where I left off", "resume my last session", "what did that session change", "rebuild context from logs", or invokes /work-recovery. Report-only — it never scores or judges. Do NOT use for usage scoring (that is /skill-improver, /tool-efficiency, /prompt-analytics) or one-off interactive log queries (that is /session-analytics).
development
Curate this repo's hallouminate wiki (.hallouminate/wiki/, the repo:dotfiles:wiki corpus) — add or update architecture pages, per-harness docs, and gotchas. Use when the user says "update the wiki", "document this in the wiki", "refresh the harness docs", "add a wiki page", "curate the wiki", "the wiki is stale", or invokes /wiki-curator. Also use at session end to write back a non-obvious decision or gotcha worth preserving. Grounds the existing wiki first, follows one-topic-per-file conventions, verifies every external doc URL before writing, and reindexes. Do NOT use for general code search (that is cheez-search) or for editing AGENTS.md command reference.
tools
Audit how a tool, command, or MCP server is actually used across coding-agent sessions and produce calibrated recommendations — tool-vs-task fit, error forensics, fix recommendations, permission friction, MCP health, and token economics. Use when the user says "tool efficiency", "am I using X efficiently", "audit tool usage", "why does X keep failing", "how do I fix this error", "what should I change", "permission friction", "is this MCP worth it", "tool error rate", "fix recommendations", or invokes /tool-efficiency. Do NOT use for auditing a skill or agent definition (that is /skill-improver) or for one-off interactive log queries (that is /session-analytics).
tools
Analyze how prompts and skill routing behave across coding-agent sessions and produce calibrated recommendations — prompt-pattern analysis, routing accuracy, and knowledge gaps. Use when the user says "analyze my prompts", "prompt patterns", "is routing working", "which skill should have fired", "knowledge gaps", "what do I keep asking", or invokes /prompt-analytics. Do NOT use for auditing a single skill/agent definition (that is /skill-improver), tool/MCP efficiency (that is /tool-efficiency), or one-off interactive log queries (that is /session-analytics).