Back to skills
SKILL.md
Tui Screen
ASecurityCreate TUI screens, components, and views for the agents-in-a-box terminal UI. Use when building new ratatui components, adding panels, creating list views, or styling any terminal interface element. Provides color palette, component patterns, and quality checklist.
- 23 stars
- 0 votes
- 0 copies
- 1 view
- Added September 3, 2026
Works with
Security analysis
100/100npx -y skills add stevengonsalvez/claudecode-bootstrap --skill tui-screen --agent claude-codeAre you the author of Tui Screen?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/stevengonsalvez-tui-screen)---
name: tui-screen
description: Create TUI screens, components, and views for the agents-in-a-box terminal UI. Use when building new ratatui components, adding panels, creating list views, or styling any terminal interface element. Provides color palette, component patterns, and quality checklist.
---
# TUI Screen Creation Skill
This skill guides creation of **premium, consistent TUI components** for agents-in-a-box.
## Core Principles
All TUI components must follow these styling standards for visual consistency:
### 1. Color Palette (MANDATORY)
```rust
// Primary colors
const CORNFLOWER_BLUE: Color = Color::Rgb(100, 149, 237); // Borders, section titles
const GOLD: Color = Color::Rgb(255, 215, 0); // CTAs, emphasis, titles
const SELECTION_GREEN: Color = Color::Rgb(100, 200, 100); // Active selections
const WARNING_ORANGE: Color = Color::Rgb(255, 165, 0); // Warnings
// Backgrounds
const DARK_BG: Color = Color::Rgb(25, 25, 35); // Main background
const PANEL_BG: Color = Color::Rgb(30, 30, 40); // Panel backgrounds
const LIST_HIGHLIGHT_BG: Color = Color::Rgb(40, 40, 60); // Selection background
// Text
const SOFT_WHITE: Color = Color::Rgb(220, 220, 230); // Primary text
const MUTED_GRAY: Color = Color::Rgb(120, 120, 140); // Secondary text
const SUBDUED_BORDER: Color = Color::Rgb(60, 60, 80); // Secondary borders
// Status
const PROGRESS_CYAN: Color = Color::Rgb(100, 200, 230); // Loading/progress
```
### 2. Required Imports
```rust
use ratatui::{
Frame,
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, BorderType, List, ListItem, ListState, Paragraph, Tabs, Wrap},
};
```
## Component Patterns
### Standard Panel Block
```rust
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded) // ALWAYS rounded
.border_style(Style::default().fg(CORNFLOWER_BLUE))
.style(Style::default().bg(DARK_BG))
.title(Line::from(vec![
Span::styled(" π ", Style::default().fg(GOLD)),
Span::styled("Title", Style::default().fg(GOLD).add_modifier(Modifier::BOLD)),
]))
```
### Active/Focused Panel
```rust
.border_style(Style::default().fg(SELECTION_GREEN))
```
### Title with Count Badge
```rust
.title(Line::from(vec![
Span::styled(" π ", Style::default().fg(GOLD)),
Span::styled("Items ", Style::default().fg(GOLD).add_modifier(Modifier::BOLD)),
Span::styled(format!("({})", count), Style::default().fg(CORNFLOWER_BLUE).add_modifier(Modifier::BOLD)),
]))
```
### Bottom Help Bar (Keyboard Hints)
```rust
.title_bottom(Line::from(vec![
Span::styled(" Enter", Style::default().fg(GOLD).add_modifier(Modifier::BOLD)),
Span::styled(" select ", Style::default().fg(MUTED_GRAY)),
Span::styled("β", Style::default().fg(SUBDUED_BORDER)),
Span::styled(" Esc", Style::default().fg(GOLD).add_modifier(Modifier::BOLD)),
Span::styled(" back ", Style::default().fg(MUTED_GRAY)),
]))
```
### Selected List Item
```rust
let mut spans = vec![];
if is_selected {
spans.push(Span::styled("βΆ ", Style::default().fg(SELECTION_GREEN)));
} else {
spans.push(Span::raw(" "));
}
spans.push(Span::styled(&item_text,
if is_selected {
Style::default().fg(SELECTION_GREEN).add_modifier(Modifier::BOLD)
} else {
Style::default().fg(SOFT_WHITE)
}
));
let base_style = if is_selected {
Style::default().bg(LIST_HIGHLIGHT_BG)
} else {
Style::default()
};
ListItem::new(Line::from(spans)).style(base_style)
```
### Empty State
```rust
Paragraph::new(vec![
Line::from(Span::styled("β¨ No items found", Style::default().fg(MUTED_GRAY))),
Line::from(""),
Line::from(Span::styled("Helpful hint here", Style::default().fg(MUTED_GRAY).add_modifier(Modifier::ITALIC))),
])
```
### Input Field (Active)
```rust
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(SELECTION_GREEN))
.style(Style::default().bg(Color::Rgb(35, 35, 45)))
// Block cursor
Span::styled("β", Style::default().fg(SELECTION_GREEN))
```
### Status Bar
```rust
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(SUBDUED_BORDER))
.style(Style::default().bg(PANEL_BG))
```
### Status Indicator Pattern
Use for displaying configuration/connection status inline with settings:
```rust
// Status value format: "Type (details)" or "Type"
// Examples:
// - "API Key (sk-ant-xxxxβ’β’β’β’β’β’β’β’)" - configured with masked details
// - "System Auth (Pro/Max Plan)" - configured, no secrets
// - "Not configured" - unconfigured state
// Status colors based on state
let (status_text, status_style) = if is_configured {
(
format!("{} ({})", config_type, masked_value),
Style::default().fg(SELECTION_GREEN)
)
} else {
(
"Not configured".to_string(),
Style::default().fg(MUTED_GRAY)
)
};
// Render as setting row
Line::from(vec![
Span::styled("βΆ ", Style::default().fg(SELECTION_GREEN)),
Span::styled("Setting Name", Style::default().fg(SOFT_WHITE)),
Span::styled(": ", Style::default().fg(MUTED_GRAY)),
Span::styled(status_text, status_style),
])
```
### Status Badge Pattern
For showing status badges next to items (e.g., "Coming Soon", "Beta", "Current"):
```rust
// Status badge colors
const COMING_SOON_GRAY: Color = Color::Rgb(80, 80, 100);
const BETA_CYAN: Color = Color::Rgb(100, 200, 230);
const CURRENT_GREEN: Color = Color::Rgb(100, 200, 100);
// Badge styling
fn status_badge(text: &str, badge_type: BadgeType) -> Span {
let (color, modifier) = match badge_type {
BadgeType::ComingSoon => (COMING_SOON_GRAY, Modifier::ITALIC),
BadgeType::Beta => (BETA_CYAN, Modifier::empty()),
BadgeType::Current => (CURRENT_GREEN, Modifier::empty()),
BadgeType::Disabled => (MUTED_GRAY, Modifier::ITALIC),
};
Span::styled(
format!("[{}]", text),
Style::default().fg(color).add_modifier(modifier)
)
}
// Usage in list item
let mut spans = vec![
Span::styled("Item Name", Style::default().fg(SOFT_WHITE)),
Span::styled(" ", Style::default()),
];
if is_coming_soon {
spans.push(status_badge("Coming Soon", BadgeType::ComingSoon));
}
if is_current {
spans.push(Span::styled(" ", Style::default()));
spans.push(status_badge("Current", BadgeType::Current));
}
```
### Configuration Status Panel
For showing multiple status items in a summary view:
```rust
// Layout: status indicator | label | value
fn render_status_row(label: &str, value: &str, is_ok: bool) -> Line<'static> {
let indicator = if is_ok {
Span::styled("β ", Style::default().fg(SELECTION_GREEN))
} else {
Span::styled("β ", Style::default().fg(MUTED_GRAY))
};
Line::from(vec![
indicator,
Span::styled(label, Style::default().fg(SOFT_WHITE)),
Span::styled(": ", Style::default().fg(MUTED_GRAY)),
Span::styled(
value.to_string(),
if is_ok {
Style::default().fg(SOFT_WHITE)
} else {
Style::default().fg(MUTED_GRAY).add_modifier(Modifier::ITALIC)
}
),
])
}
// Example usage
let rows = vec![
render_status_row("Claude Auth", "API Key (sk-ant-β’β’β’β’)", true),
render_status_row("GitHub", "System Default", true),
render_status_row("AWS Bedrock", "Not configured", false),
];
```
## Icon Reference
| Element | Icon | Color |
|---------|------|-------|
| Folder closed | π | CORNFLOWER_BLUE |
| Folder open | π | CORNFLOWER_BLUE |
| File generic | π | SOFT_WHITE |
| File Rust | π¦ | - |
| File Python | π | - |
| File Markdown | π | - |
| Config | βοΈ | - |
| Success/Check | β | SELECTION_GREEN |
| Running | π’ | Green |
| Stopped | π΄ | Red |
| Warning | β οΈ | WARNING_ORANGE |
| Loading | π | WARNING_ORANGE |
| Push ready | π | SELECTION_GREEN |
| Edit | βοΈ | GOLD |
| Stats | π | GOLD |
| Sparkle | β¨ | GOLD |
## Tree Characters
```rust
const EXPANDED: &str = "βΌ";
const COLLAPSED: &str = "βΆ";
const TREE_BRANCH: &str = "ββ";
const TREE_LAST: &str = "ββ";
const TREE_VERTICAL: &str = "β";
```
## Layout Guidelines
| Element | Constraint |
|---------|------------|
| Tab bar | `Length(3)` |
| Status bar | `Length(3)` |
| Content area | `Min(0)` |
| Modal width | 80% of terminal |
| Modal height | 70% of terminal |
| Split panes | 40/60 or 50/50 |
## Component Structure Template
```rust
pub struct MyComponentState {
pub selected_index: usize,
pub items: Vec<MyItem>,
pub scroll_offset: usize,
pub is_active: bool,
}
impl MyComponentState {
pub fn new() -> Self { /* ... */ }
pub fn next_item(&mut self) { /* ... */ }
pub fn previous_item(&mut self) { /* ... */ }
}
pub struct MyComponent;
impl MyComponent {
pub fn render(frame: &mut Frame, area: Rect, state: &MyComponentState) {
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3), // Header
Constraint::Min(0), // Content
Constraint::Length(3), // Status
])
.split(area);
Self::render_header(frame, chunks[0], state);
Self::render_content(frame, chunks[1], state);
Self::render_status_bar(frame, chunks[2], state);
}
}
```
## Modal Popup Patterns (MANDATORY for Config/Settings)
All configuration and settings screens MUST use popup-based interactions rather than inline editing:
### Popup Types
| Setting Type | Popup Behavior |
|--------------|----------------|
| Choice/Enum | Selection list with βΆ indicator, Up/Down navigation |
| Text/String | Text input field with cursor |
| Boolean | Enabled/Disabled toggle selection |
| Number | Numeric input field |
### Popup Structure
```rust
// Popup overlay pattern
use ratatui::widgets::Clear;
// 1. Calculate centered position
let popup_width = 50u16.min(area.width - 4);
let popup_height = 12u16.min(area.height - 4);
let popup_x = (area.width - popup_width) / 2;
let popup_y = (area.height - popup_height) / 2;
let popup_area = Rect::new(popup_x, popup_y, popup_width, popup_height);
// 2. Clear background
frame.render_widget(Clear, popup_area);
// 3. Draw popup block
let block = Block::default()
.title(Span::styled(
format!(" {} ", title),
Style::default().fg(GOLD).add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CORNFLOWER_BLUE))
.style(Style::default().bg(PANEL_BG));
```
### Choice Selection Popup
```rust
// Selection indicator
let indicator = if is_selected { "βΆ " } else { " " };
let style = if is_selected {
Style::default()
.fg(SELECTION_GREEN)
.bg(LIST_HIGHLIGHT_BG)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(SOFT_WHITE)
};
Line::from(vec![
Span::styled(indicator, Style::default().fg(SELECTION_GREEN)),
Span::styled(option_text, style),
])
```
### Boolean Toggle Popup
```rust
// Enabled/Disabled options
Line::from(vec![
Span::styled(if value { "βΆ " } else { " " }, Style::default().fg(SELECTION_GREEN)),
Span::styled("β Enabled", if value { selected_style } else { normal_style }),
]),
Line::from(vec![
Span::styled(if !value { "βΆ " } else { " " }, Style::default().fg(SELECTION_GREEN)),
Span::styled("β Disabled", if !value { selected_style } else { normal_style }),
]),
```
### Text Input Popup
```rust
// Input field with cursor
let input_block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(GOLD))
.style(Style::default().bg(LIST_HIGHLIGHT_BG));
// Cursor indicator
let display = format!("{}|", value);
```
### Popup Help Bar
```rust
// Navigation hints at bottom
let help_items = vec![
("ββ", "select"), // For choice/boolean
("Enter", "confirm"),
("Esc", "cancel"),
];
// Or for text input
let help_items = vec![
("Enter", "save"),
("Esc", "cancel"),
];
```
### Reference Implementation
See `src/components/config_popup.rs` for complete popup component with:
- `ConfigPopupType` enum (Choice, TextInput, Boolean, NumberInput)
- `ConfigPopupState` for state management
- Full render implementation
## Quality Checklist
Before completing any TUI component:
- [ ] All panels use `BorderType::Rounded`
- [ ] All borders use `CORNFLOWER_BLUE` (or `SELECTION_GREEN` when focused)
- [ ] All backgrounds use `DARK_BG` or `PANEL_BG`
- [ ] Titles use gold icon + gold bold text
- [ ] Lists use `βΆ` indicator with `SELECTION_GREEN`
- [ ] Selected items have `LIST_HIGHLIGHT_BG` background
- [ ] Help bar uses gold keys + muted descriptions
- [ ] Empty states have icon + italic hint
- [ ] **Config/settings use popup-based editing (not inline)**
- [ ] Events added to `src/app/events.rs`
- [ ] `cargo check` passes
## Reference Components
Study these for patterns:
- `src/components/git_view.rs` - Tabs, lists, tree view, input, markdown
- `src/components/session_detail.rs` - Panel layouts, status indicators
- `src/components/new_session_panel.rs` - Modal dialogs, card selection
Attribution
Comments
Loading commentsβ¦
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.