tui-design
Skill18 starsChanged 7 months agoMITView on GitHub
Design specification for CLI TUI (Terminal User Interface). This skill provides comprehensive guidelines for implementing interactive terminal UI components, including page layout structure, color schemes, keyboard navigation, and multi-level navigation principles.
10 other projects use a version of this.
Adopted by
Most recently taken up by
- egregore-labs/egregoretheir own version, first seen Nov 6, 2026
- Praveensenpai/karakuritheir own version, first seen Nov 1, 2026
- hyperb1iss/hyperskillstheir own version, first seen Nov 1, 2026
- lrstanley/skillstheir own version, first seen Nov 1, 2026
- aewing/skillstheir own version, first seen Oct 13, 2026
- diegomarino/tui-designtheir own version, first seen Oct 8, 2026
Used with
Projects with it also use unslop.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
photon-groveSkillunslop
Detects and rewrites generic, overly polished, or AI-sounding prose while preserving meaning. Use when the user asks to "unslop", humanize, de-AI, tighten, or review whether writing sounds generated.
Also in 3 projects that use this
Other versions10 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 10
- pedronauck/skills★ 649same name
- egregore-labs/egregore★ 293same name
- hyperb1iss/hyperskills★ 36same name
- pageton/tui-design-skill★ 19same name
- diegomarino/tui-design★ 18same name
- FelipeAngeli/skills★ 2same name
- aewing/skills★ 1same name
- brian-bell/agent-skills★ 1same name
- lrstanley/skills★ 1same name
- Praveensenpai/karakuri★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository28 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- brainstormingSkill
- dispatching-parallel-agentsSkill
- executing-plansSkill
- find-skillsSkill
- finishing-a-development-branchSkill
- golang-cli-cobra-viperSkill
- golang-testingSkill
- receiving-code-reviewSkill
- requesting-code-reviewSkill
- skill-creatorSkill
- skills-xSkill
- subagent-driven-developmentSkill
- systematic-debuggingSkill
- test-driven-developmentSkill
- using-superpowersSkill
- verification-before-completionSkill
- writing-plansSkill
- writing-skillsSkill
- baidu-speech-to-textSkill
- feishu-notifySkill
- go-embedded-spaSkill
- go-i18nSkill
- go-pocketbase-integrationSkill
- minimal-ui-designSkill
- shadowrocket-claude-routingSkill
- skills-xSkill
The file
We split this skill into the rules it gives an agent
9 rules. 0 are common, 3 are distinctive, and 6 are very specific to this project.
0
Common
Most of the 10 edited versions we read keep these.
3
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Every TUI page follows a consistent four-section layout:
- Very specific: line 59
After logoStyle.Render(logo), add exactly one \n — this produces symmetric 1-blank-line padding on both sides
- Very specific: line 60
Never: add \n\n after logo — it creates asymmetric bottom padding
- Very specific: line 251
Use 2 spaces (" ") between columns in fmt.Sprintf:
- Distinctive: line 262
All separators use the same constant width:
- Very specific: line 275
All separators use
SeparatorWidth(60): Never hardcode different widths. - Very specific: line 404
Never: use tea.WithAltScreen() on individual programs — it causes exit/enter cycles that flash the normal terminal
- Distinctive: line 518
Consistent Layout: Every page has Header → Info Bar → Content → Footer
- Very specific: line 521
Visual-Width Alignment: Use padRight/termWidth for CJK-aware column alignment.
“Common” is measured: kept by at least 6 in 10 of the 10 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLI TUI Design Specification
This document provides comprehensive design guidelines for implementing interactive terminal user interface (TUI) applications.
When to Use This Skill
- Developing or modifying CLI TUI components
- Adding new UI pages or interactions
- Implementing new visual styles or themes
- Creating similar TUI applications following this pattern
1. Page Layout Structure
Every TUI page follows a consistent four-section layout:
┌────────────────────────────────────────────────────────────────┐
│ Header: Title Line + Separator + ASCII Logo + Separator │
├────────────────────────────────────────────────────────────────┤
│ Info Bar: Context-dependent information (Dynamic) │
├────────────────────────────────────────────────────────────────┤
│ Content Area: Interactive options/list (Scrollable) │
│ │
├────────────────────────────────────────────────────────────────┤
│ Footer: Status Bar + Separator + Keyboard Hints │
└────────────────────────────────────────────────────────────────┘Section Details
1.1 Header Section (Always Visible)
The header consists of a title line, separator, ASCII logo, and separator with symmetric spacing:
🚀 Skills-X - AI Agent Skills Manager v0.2.12
────────────────────────────────────────────────────────────
<- 1 blank line (from logo constant leading \n)
███████╗██╗ ██╗██╗██╗ ...
╚══════╝╚═╝ ╚═╝╚═╝╚══════╝...
<- 1 blank line (from logo constant trailing \n + 1 extra \n)
────────────────────────────────────────────────────────────Spacing rules:
- The
logoconstant uses backtick string, which naturally includes a leading\n(after opening backtick) and trailing\n(before closing backtick) - After
logoStyle.Render(logo), add exactly one\n— this produces symmetric 1-blank-line padding on both sides - Never add
\n\nafter logo — it creates asymmetric bottom padding
func RenderLogo(version string) string {
var b strings.Builder
// Title + version (strip -dirty suffix for display)
b.WriteString(accentStyle.Render("🚀 Skills-X - AI Agent Skills Manager"))
if version != "" {
displayVersion := strings.TrimSuffix(version, "-dirty")
b.WriteString(" " + hintStyle.Render(displayVersion))
}
b.WriteString("\n")
// Upper separator
b.WriteString(separatorStyle.Render(strings.Repeat("─", SeparatorWidth)))
b.WriteString("\n")
// Logo (constant has leading \n and trailing \n built-in)
b.WriteString(logoStyle.Render(logo))
b.WriteString("\n") // exactly 1 \n — symmetric with leading \n
// Lower separator
b.WriteString(separatorStyle.Render(strings.Repeat("─", SeparatorWidth)))
b.WriteString("\n")
return b.String()
}Newline budget between header and content:
RenderLogoends withseparator + \n- View should not add extra
\nafterRenderLogo()— content starts immediately
1.2 Info Bar Section (Dynamic)
- Page-specific context: table headers, counts, search box
- Context info uses dim/hint color
- No working directory display on LV1 — only shown on pages where it adds value (e.g. LV3 skills list)
1.3 Content Area (Interactive)
- Table View: Column headers + data rows with visual-width alignment
- List View: Scrollable list with checkbox multi-selection
- Progress View: Progress bar with percentage and counts
Key features:
- Visual-width-aware column alignment (see Section 3)
- Numeric columns right-aligned with
fmt.Sprintf("%4d") - Checkbox (☐/☑) for multi-selection
- Cursor indicator (❯) for current position
- Empty line padding to maintain stable page height
1.4 Footer Section
(共 30 个,已安装 12 个,将安装 3 个,将卸载 1 个)
────────────────────────────────────────────────────────────
/ 搜索 | 空格 选择/反选 | A 全选 | Enter 确认安装/卸载 | b 返回 | q 退出Composition:
- Status bar:
\n+ counts in dim color, parenthesized - Separator:
\n+ 60-char─line +\n - Hints: Keyboard shortcuts in dim color, pipe-separated
func RenderStatusBar(total, installed, newSelected, deselected int) string {
return "\n" + hintStyle.Render(
fmt.Sprintf("(共 %d 个,已安装 %d 个,将安装 %d 个,将卸载 %d 个)",
total, installed, newSelected, deselected))
}
func RenderHint(hint string) string {
return "\n" + RenderSeparator() + hintStyle.Render(hint)
}
func RenderSeparator() string {
return separatorStyle.Render(strings.Repeat("─", SeparatorWidth)) + "\n"
}2. Style Definitions
Color Palette
| Color Name | Hex Code | Variable | Usage |
| Primary | #00D4AA | primaryColor | Selected items, logo |
| Secondary | #FF6B6B | secondaryColor | Cursor, errors, alerts |
| Accent | #4ECDC4 | accentColor | Title line, highlights |
| Dim | #666666 | dimColor | Hints, separators |
| White | #FFFFFF | whiteColor | Section titles, headers |
| Yellow | #FFCC00 | yellowColor | Warnings (reserved) |
| Blue | #5599FF | blueColor | Links (reserved) |
| Cyan | #5A9FB8 | cyanColor | Selectable items |
Component Styles
logoStyle = lipgloss.NewStyle().Foreground(primaryColor).Bold(true)
titleStyle = lipgloss.NewStyle().Foreground(whiteColor).Bold(true)
selectedStyle = lipgloss.NewStyle().Foreground(primaryColor).Bold(true)
normalStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#AAAAAA"))
selectableStyle = lipgloss.NewStyle().Foreground(cyanColor)
hintStyle = lipgloss.NewStyle().Foreground(dimColor)
cursorStyle = lipgloss.NewStyle().Foreground(secondaryColor).Bold(true)
successStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#00FF00"))
separatorStyle = lipgloss.NewStyle().Foreground(dimColor)
searchStyle = lipgloss.NewStyle().Foreground(whiteColor).Background(lipgloss.Color("#333333"))Status Indicators
| Symbol | Meaning | Style |
☑ | Selected | selectedStyle |
☐ | Not selected | — |
❯ | Cursor position | cursorStyle |
3. Column Alignment & Visual Width
The Problem
fmt.Sprintf width specifiers count bytes, not terminal columns. This breaks alignment when:
- ANSI escape codes (lipgloss styles) add invisible bytes
- CJK characters (中文、日本語) occupy 2 terminal columns per rune
The Solution: Pad First, Style After
Rule: Always pad plain text to the target visual width, then apply lipgloss style.
// termWidth returns the visual width of a string in terminal columns.
func termWidth(s string) int {
w := 0
for _, r := range s {
if isCJK(r) {
w += 2
} else {
w++
}
}
return w
}
// padRight pads a string with spaces to reach the target visual width.
func padRight(s string, targetWidth int) string {
w := termWidth(s)
for w < targetWidth {
s += " "
w++
}
return s
}Usage Pattern
// Header: pad plain text, then style
headerName := padRight("AI 工具", 22) // "AI 工具" = 7 visual cols → 15 spaces added
b.WriteString(fmt.Sprintf(" %s %s %s\n",
titleStyle.Render(headerName), // styled AFTER padding
titleStyle.Render(padRight("全局", 4)), // "全局" = 4 visual cols → 0 spaces
titleStyle.Render(padRight("项目", 4))))
// Data rows: same column widths
name := padRight(p.Name, 22) // "Claude Code" = 11 cols → 11 spaces
globalStr := fmt.Sprintf("%4d", count) // right-aligned number, 4 chars
b.WriteString(fmt.Sprintf("%s%s %s %s\n",
prefix, // " " or "❯ " (always 2 visual cols)
style.Render(name), // styled AFTER padding
globalCountStyle.Render(globalStr), // styled AFTER formatting
projectCountStyle.Render(projectStr)))Column Width Standards
| Column | Visual Width | Alignment | Padding Method |
| Name | 22 | Left | padRight(text, 22) |
| Skill name | 40 | Left | padRight(text, 40) |
| Number | 4 | Right | fmt.Sprintf("%4d", n) |
| Status (✓/✗) | 1 | Center | Direct render |
| Prefix (❯) | 2 | Left | " " or cursorStyle("❯ ") |
Gap Between Columns
Use 2 spaces (" ") between columns in fmt.Sprintf:
fmt.Sprintf("%s%s %s %s\n", prefix, name, col1, col2)
// ^^ ^^ two-space gaps4. Separator Rules
All separators use the same constant width:
const SeparatorWidth = 60| Location | Character | Width | Color |
| Above ASCII logo | ─ | 60 | dimColor |
| Below ASCII logo | ─ | 60 | dimColor |
| Below table header | ─ | 60 | dimColor |
| Above footer hints | ─ | 60 | dimColor |
All separators use SeparatorWidth (60). Never hardcode different widths.
5. Keyboard Navigation
Universal Keys (All Levels)
| Key | Action |
q | Quit application |
Ctrl+C | Force quit |
List Navigation
| Key | Action |
↑ | Move cursor up (wrap to bottom) |
↓ | Move cursor down (wrap to top) |
PgUp | Page up |
PgDown | Page down |
Home | Jump to first item |
End | Jump to last item |
Selection
| Key | Action |
Space | Toggle selection (cursor stays) |
a / A | Select all visible items |
Enter | Confirm selection |
Multi-Level Navigation
| Key | Action |
Esc / b | Go back to previous level |
Enter | Proceed to next level |
Search Mode
| Key | Action |
/ / Ctrl+F | Enter search mode |
| Typing | Append to search query |
Backspace | Remove last character |
Esc | Exit search mode, clear query |
6. Multi-Level Page Architecture
Page Flow
Level 1 Level 2 Level 3 Level 4
│ │ │ │
▼ ▼ ▼ ▼
Select ────────► Select ──────────► Select ──────────► Install
AI Tool Target (global/ Skills (multi- Progress
project) select + search)Navigation Rules
- Forward:
Enterconfirms and proceeds to next level - Backward:
Escorbreturns to previous level (restarts the loop) - Quit:
qorCtrl+Cexits at any level - State preservation: Going back re-runs the previous level from scratch
Orchestration Pattern
Alt screen is managed once at the top level to avoid flicker between pages:
func RunTUI(opts TUIOptions) error {
// Enter alt screen ONCE for the entire TUI session
fmt.Print(EnterAltScreen)
fmt.Print(HideCursor)
defer func() {
fmt.Print(ShowCursor)
fmt.Print(ExitAltScreen)
}()
return runTUIFlow(opts)
}
func runTUIFlow(opts TUIOptions) error {
// Level 1
fmt.Print(ClearScreen)
product, err := RunProductSelect(opts.Version, opts.TargetDir)
if err != nil || product == nil {
return err
}
// Level 2
fmt.Print(ClearScreen)
target, err := RunInstallTargetSelect(product, opts.TargetDir)
...
// Level 3
fmt.Print(ClearScreen)
selected, deselected, err := RunSkillsSelect(...)
if err != nil && err.Error() == "go back" {
return runTUIFlow(opts) // recursive restart
}
// Level 4
fmt.Print(ClearScreen)
RunInstaller(selected, deselected, targetDir)
// Exit alt screen to show final result on normal terminal
fmt.Print(ShowCursor)
fmt.Print(ExitAltScreen)
fmt.Printf("Completed: %d\n", completed)
return nil
}7. Terminal Control
Two-Layer Rendering Strategy
The rendering uses a two-layer approach to eliminate flicker:
Layer 1: Alt screen lifecycle (managed by RunTUI)
- Enter alt screen once at the start, exit once at the end
ClearScreenbetween each page transition (within the alt screen buffer)- Never use
tea.WithAltScreen()on individual programs — it causes exit/enter cycles that flash the normal terminal
Layer 2: Per-page rendering (managed by Bubble Tea)
- Each page runs as
tea.NewProgram(model)(no WithAltScreen) - Bubble Tea handles incremental rendering within the page
View()returns complete page content; never includeClearScreenin View()
// Individual programs: NO WithAltScreen
p := tea.NewProgram(model) // Bubble Tea renders incrementally
// Page transitions: ClearScreen within the shared alt screen
fmt.Print(ClearScreen) // clears alt screen buffer between pagesAnti-patterns:
tea.NewProgram(model, tea.WithAltScreen())per level → flicker on every page switchClearScreeninsideView()→ full repaint on every keystrokefmt.Printf(...)between levels while in alt screen → cursor position corruption
ANSI Sequences
const ClearScreen = "\033[2J\033[H" // Clear screen + cursor home
const EnterAltScreen = "\033[?1049h" // Enter alternate screen buffer
const ExitAltScreen = "\033[?1049l" // Exit alternate screen buffer
const HideCursor = "\033[?25l" // Hide cursor
const ShowCursor = "\033[?25h" // Show cursor8. Layout Examples
Level 1: Product Selection
🚀 Skills-X - AI Agent Skills Manager v0.2.12
────────────────────────────────────────────────────────────
███████╗██╗ ██╗██╗██╗ ██╗ ███████╗ ██╗ ██╗
...
╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝ ╚═╝ ╚═╝
────────────────────────────────────────────────────────────
AI 工具 全局 项目
────────────────────────────────────────────────────────────
❯ Claude Code 45 0
Cursor 0 36
Windsurf 0 0
────────────────────────────────────────────────────────────
↑↓ 选择 | Enter 确定 | q 退出Level 3: Skills Selection
🚀 Skills-X - AI Agent Skills Manager v0.2.12
────────────────────────────────────────────────────────────
███████╗...
╚══════╝...
────────────────────────────────────────────────────────────
选择要安装的 Skills Cursor
📁 /path/to/project
🔍 搜索技能...
已安装
────────────────────────────────────────────────────────────
❯ ☑ anthropic/brainstorming ✓
☑ anthropic/doc-coauthoring ✓
☐ anthropic/frontend-design ✗
...
↑ 3/30 ↓
(共 30 个,已安装 12 个,将安装 3 个,将卸载 1 个)
────────────────────────────────────────────────────────────
/ 搜索 | 空格 选择/反选 | A 全选 | Enter 确认安装/卸载 | b 返回 | q 退出Level 4: Installation Progress
Installing Skills
Progress: [========================================] 100% (9/9)
Completed: 9 | Failed: 0
To uninstall:
[OK] anthropic/doc-coauthoring
[OK] anthropic/frontend-design
────────────────────────────────────────────────────────────
Done! 9 completed.9. Constants
const SeparatorWidth = 60 // All separators use this width
const DefaultPageSize = 10 // Default items per page in scrollable lists10. Design Principles Summary
- Consistent Layout: Every page has Header → Info Bar → Content → Footer
- Uniform Separators: All
─lines useSeparatorWidth(60), never hardcode other values - Symmetric Spacing: Logo has equal blank-line padding above and below
- Visual-Width Alignment: Use
padRight/termWidthfor CJK-aware column alignment; pad plain text first, style after - Flicker-Free: Manage alt screen at RunTUI level (enter once, exit once), use ClearScreen between pages, never ClearScreen in View()
- Stable UI: Fixed column widths, empty line fill, scroll offset tracking
- Multi-Level Flow: Independent programs per level, back/forward navigation
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

