Skip to content

Commit eda5e65

Browse files
committed
chore: add tui-design-system agent skill
1 parent a201c8c commit eda5e65

1 file changed

Lines changed: 327 additions & 0 deletions

File tree

  • .agents/skills/tui-design-system
Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
---
2+
name: tui-design-system
3+
description: Use when designing or building any terminal user interface — choosing a layout paradigm, keybindings/interaction model, color system, data visualization, or motion. Framework-agnostic universal patterns that work with Ratatui, Ink, Textual, Bubbletea, or any TUI toolkit. For the repo's own pythinker-code TUI, use write-tui instead.
4+
---
5+
6+
# TUI Design System
7+
8+
Universal design patterns for building exceptional terminal user interfaces. Framework-agnostic — works with Ratatui, Ink, Textual, Bubbletea, or any TUI toolkit.
9+
10+
> For editing **this repo's** terminal UI (`apps/pythinker-code/src/tui`), use the **write-tui** skill instead. This skill is the cross-project design vocabulary.
11+
12+
**Core philosophy:** TUIs earn their power through spatial consistency, keyboard fluency, and information density that respects human attention. Design for the expert's speed without abandoning the beginner's discoverability.
13+
14+
## Design process
15+
16+
```dot
17+
digraph tui_design {
18+
rankdir=TB;
19+
"What are you building?" [shape=diamond];
20+
"Select layout paradigm" -> "Design interaction model" -> "Define visual system" -> "Validate against anti-patterns" -> "Ship it";
21+
"What are you building?" -> "Select layout paradigm";
22+
"Ship it" [shape=doublecircle];
23+
}
24+
```
25+
26+
1. Pick a **layout paradigm** from what you're building.
27+
2. Design the **interaction model** (navigation, keybindings, help, dialogs).
28+
3. Define the **visual system** (color tiers, semantic slots, hierarchy).
29+
4. Add **data visualization** and **motion** where they earn their place.
30+
5. **Validate** against the anti-patterns checklist, then ship.
31+
32+
---
33+
34+
## 1. Layout paradigm selector
35+
36+
| App type | Paradigm | Examples |
37+
|---|---|---|
38+
| File manager | Miller Columns | yazi, ranger |
39+
| Git / DevOps tool | Persistent Multi-Panel | lazygit, lazydocker |
40+
| System monitor | Widget Dashboard | btop, bottom, oxker |
41+
| Data browser / K8s | Drill-Down Stack | k9s, diskonaut |
42+
| SQL / HTTP client | IDE Three-Panel | harlequin, posting |
43+
| Shell augmentation | Overlay / Popup | atuin, fzf |
44+
| Log / event viewer | Header + Scrollable List | htop, tig |
45+
46+
### Persistent Multi-Panel
47+
All panels visible simultaneously; focus shifts between them. Users build spatial memory — "branches are always bottom-left."
48+
49+
```
50+
┌─ Status ──┬─────────── Detail ──────────┐
51+
├─ Files ───┤ │
52+
│ > file.rs │ diff content here... │
53+
│ main.rs │ │
54+
├─ Branches ┤ │
55+
│ * main │ │
56+
│ feat/x │ │
57+
└───────────┴─────────────────────────────┘
58+
[q]uit [c]ommit [p]ush [?]help
59+
```
60+
**Use for:** multi-faceted tools needing simultaneous context (git clients, container managers, monitoring).
61+
**Key rule:** panels keep fixed positions across sessions. Never rearrange without user action.
62+
63+
### Miller Columns
64+
Three-pane past/present/future navigation: parent (left), current (center), preview (right).
65+
66+
```
67+
┌── Parent ──┬── Current ──┬── Preview ────────┐
68+
│ .. │ > config/ │ port: 8080 │
69+
│ src/ │ lib/ │ host: localhost │
70+
│ > config/ │ main.rs │ log_level: debug │
71+
│ tests/ │ mod.rs │ db_url: postgres │
72+
└────────────┴─────────────┴───────────────────┘
73+
```
74+
**Use for:** navigating hierarchical data where context above and below matters.
75+
**Key rule:** selecting in the center shifts everything left; the preview always reflects the highlighted item.
76+
77+
### Drill-Down Stack
78+
One level at a time; navigation pushes/pops levels like a stack. Breadcrumb shows depth.
79+
80+
```
81+
Context > namespace: prod > pods
82+
┌─────────────────────────────────────────────┐
83+
│ NAME READY STATUS AGE │
84+
│ > api-7f9c 2/2 Running 3d │
85+
│ worker-1a2b 1/1 Running 3d │
86+
└─────────────────────────────────────────────┘
87+
:pods :deploy :svc [Enter] drill [Esc] up
88+
```
89+
**Use for:** deep hierarchies where showing all levels at once is impractical (Kubernetes, DB schemas).
90+
**Key rule:** always show the navigation path as a breadcrumb. Provide a `:resource` command mode for direct jumps.
91+
92+
### Widget Dashboard
93+
Self-contained widget panels with independent data. All info visible at once; no navigation required.
94+
95+
```
96+
┌─── CPU ──────────────┬─── Memory ──────────┐
97+
│ ▁▂▃▅▇█▇▅▃▂▁▂▃▅▇ │ ████████░░ 78% │
98+
│ core0: 45% core1: 67%│ 12.4G / 16.0G │
99+
├─── Network ──────────┼─── Disk ─────────────┤
100+
│ ▲ 1.2 MB/s ▼ 340KB/s │ /: 67% /home: 45% │
101+
├─── Processes ────────┴──────────────────────┤
102+
│ PID USER CPU% MEM% CMD │
103+
│ 1234 root 23.4 4.5 postgres │
104+
└──────────────────────────────────────────────┘
105+
```
106+
**Use for:** monitoring, real-time status, dashboards.
107+
**Key rule:** each widget is self-contained with its own title. Use braille/block characters for density.
108+
109+
### IDE Three-Panel
110+
Sidebar (left), editor/main (center), detail/output (bottom). Tab bar along top.
111+
**Use for:** editing-focused tools (SQL clients, HTTP tools, config editors).
112+
**Key rule:** sidebar toggles with a single key. Center supports tabs. Bottom panel can expand to full height.
113+
114+
### Overlay / Popup
115+
TUI appears on demand over the shell, disappears after use.
116+
**Use for:** shell augmentations (history search, file picker, command palette).
117+
**Key rule:** configurable height; return the selection to the caller; never disrupt scrollback.
118+
119+
### Header + Scrollable List
120+
Fixed header with meters/stats, scrollable data below, function bar at bottom.
121+
**Use for:** single-purpose viewers of one stream (process lists, logs, commit history).
122+
**Key rule:** header and footer stay pinned; only the middle scrolls.
123+
124+
---
125+
126+
## 2. Responsive layout
127+
128+
Terminals resize constantly. Pick a degradation strategy and test it.
129+
130+
| Strategy | Behavior |
131+
|---|---|
132+
| Priority collapse | Less important panels hide first below minimum width |
133+
| Stacking | Panels collapse to title-only bars; the active one expands (zellij pattern) |
134+
| Breakpoint modes | Switch layout entirely below a threshold (multi-panel → single panel) |
135+
| Minimum size gate | Show "terminal too small" below a usable minimum |
136+
137+
**Rules:**
138+
- Define a minimum size (typically **80×24**). Below it, show a resize message.
139+
- Never crash on resize. Handle SIGWINCH gracefully.
140+
- Use constraint-based layouts (percentages, min/max, ratios) — not absolute positions.
141+
- Test at **80×24, 120×40, 200×60**.
142+
143+
---
144+
145+
## 3. Interaction model
146+
147+
### Navigation style by complexity
148+
149+
| App complexity | Recommended model |
150+
|---|---|
151+
| Single-purpose, <20 actions | Direct keybinding (every key = action) |
152+
| Multi-view, complex | Vim-style modes + contextual footer |
153+
| IDE-like, many features | Command palette + tabs + vim motions |
154+
| Data browser | Drill-down + fuzzy search + `:` command mode |
155+
156+
### Keyboard design layers
157+
158+
| Layer | Keys | Audience | Always shown? |
159+
|---|---|---|---|
160+
| L0 Universal | arrows, Enter, Esc, q | Everyone | Yes (footer) |
161+
| L1 Vim motions | `hjkl / ? : gg G` | Intermediate | Yes (footer) |
162+
| L2 Actions | single mnemonics: `d`elete, `c`ommit, `p`ush | Regular | On `?` help |
163+
| L3 Power | composed commands, macros, custom bindings | Power | Docs only |
164+
165+
**Lingua franca (don't deviate):** `j/k` down/up · `h/l` left/right or collapse/expand · `/` search · `?` help · `:` command mode · `q` quit (or Esc back one level) · `Enter` select/confirm/drill · `Tab` switch focus · `Space` toggle selection · `g/G` top/bottom.
166+
167+
**Never bind:** `Ctrl+C` (interrupt), `Ctrl+Z` (suspend), `Ctrl+\` (quit). They belong to the terminal.
168+
169+
### Focus management
170+
- Only one widget receives input at a time. `Tab`/`Shift+Tab` cycle focus.
171+
- Focus indicator: highlighted border, color change, or cursor presence. Unfocused panels are dimmed or use thinner borders.
172+
- Modal dialogs are focus traps — the background receives no events.
173+
- Nested focus: the outer container routes events to the focused child.
174+
175+
### Search & filtering
176+
Universal pattern: press `/`, type, results filter live.
177+
- `n/N` next/previous match · `Esc` dismiss.
178+
- Fuzzy by default; `'` prefix for exact. Highlight matched characters. Preview updates for the highlighted result.
179+
180+
### Help — three tiers
181+
182+
| Tier | Trigger | Content | Audience |
183+
|---|---|---|---|
184+
| Always visible | Footer bar | 3–5 essential shortcuts | Everyone |
185+
| On demand | `?` | Full keybindings for current context | Regular |
186+
| Documentation | `--help` / man page | Complete reference | Power |
187+
188+
Footer format: `[q]uit [/]search [?]help [Tab]focus [Enter]select`. Make it context-sensitive — show only what's actionable right now.
189+
190+
### Dialogs & confirmation
191+
192+
| Severity | Pattern |
193+
|---|---|
194+
| Reversible | Just do it; brief status-bar confirmation |
195+
| Moderate (delete file) | Inline "Press y to confirm" |
196+
| Severe (drop database) | Modal requiring the resource name typed in |
197+
| Irreversible batch | `--dry-run` flag + explicit confirmation |
198+
199+
Modals render over a dimmed background. Toasts auto-dismiss in 3–5s. Status-bar messages are vim-style one-liners that auto-fade.
200+
201+
---
202+
203+
## 4. Color design system
204+
205+
### Terminal color tiers — design for graceful degradation
206+
207+
| Tier | Sequence | Colors | Strategy |
208+
|---|---|---|---|
209+
| 16 ANSI | `\033[31m` | 16 (relative) | Foundation; terminal theme controls appearance |
210+
| 256 | `\033[38;5;{n}m` | 256 | Extended; fixed colors may clash with themes |
211+
| True color | `\033[38;2;{r};{g};{b}m` | 16.7M (absolute) | Full control; needs `COLORTERM=truecolor` |
212+
213+
**Detection order:** `COLORTERM=truecolor|24bit` → true color · `TERM` contains `256color` → 256 · `NO_COLOR` set → no color · else 16 ANSI.
214+
215+
**Golden rule:** the TUI must be usable in 16-color mode. True color *enhances* — it never *creates* the hierarchy.
216+
217+
### Semantic color slots — name by function, not appearance
218+
219+
| Slot | Purpose | Typical dark |
220+
|---|---|---|
221+
| `fg.default` | Body text | `#c0caf5` |
222+
| `fg.muted` | Secondary / metadata | `#565f89` |
223+
| `fg.emphasis` | Headers, focused | `#e0e0e0` |
224+
| `bg.base` | Primary background | `#1a1b26` |
225+
| `bg.surface` | Panel/widget bg | `#24283b` |
226+
| `bg.overlay` | Popup/dialog bg | `#414868` |
227+
| `bg.selection` | Selected highlight | `#364a82` |
228+
| `accent.primary` | Interactive / focus | `#7aa2f7` |
229+
| `accent.secondary` | Supporting | `#bb9af7` |
230+
| `status.error` | Errors / deletions | `#f7768e` |
231+
| `status.warning` | Caution | `#e0af68` |
232+
| `status.success` | Success / additions | `#9ece6a` |
233+
| `status.info` | Informational | `#7dcfff` |
234+
235+
**Never hardcode hex in widget code. Always reference a semantic slot.**
236+
237+
### Visual hierarchy techniques
238+
239+
| Technique | Effect | Use for |
240+
|---|---|---|
241+
| Bold (SGR 1) | More weight | Headers, labels, active items |
242+
| Dim (SGR 2) | Less weight | Metadata, timestamps |
243+
| Italic (SGR 3) | Distinction | Comments, types |
244+
| Underline (SGR 4) | Actionable | Links, URLs |
245+
| Reverse (SGR 7) | Swap fg/bg | Selection (always works!) |
246+
| Strikethrough (SGR 9) | Negation | Deleted/deprecated |
247+
248+
**Recipe:** 80% of content in `fg.default`. Headers `bold + fg.emphasis`. Metadata `dim + fg.muted`. Status in semantic colors. Accents for interactive elements only.
249+
250+
### Background layering
251+
Create depth without borders by stepping lightness: `bg.base``bg.surface``bg.overlay`, each ~5–8% lighter in dark themes. The contrast gradient reads as depth and reduces the need for box-drawing.
252+
253+
### Theme architecture & accessibility
254+
- Base16 pattern: 8 monotones (background↔foreground gradient) + 8 accents. Ship a dark theme by default, at least one light variant, and respect `NO_COLOR`.
255+
- **WCAG AA:** 4.5:1 for body text, 3:1 for large text / UI elements.
256+
- **Never use color alone** — pair with symbols (✓ ✗ ▲), text, position, or typography.
257+
- Color-blind-safe pairs: blue+orange, blue+yellow, black+white. Avoid red vs green as the only signal.
258+
- Test: monochrome mode, a color-blindness simulator, 3+ terminal emulators, light and dark.
259+
260+
---
261+
262+
## 5. Data visualization
263+
264+
### Character-resolution building blocks
265+
266+
| Element | Characters | Resolution | Use for |
267+
|---|---|---|---|
268+
| Full blocks | `█▉▊▋▌▍▎▏` | 8 steps/cell | Progress bars, bar charts |
269+
| Shade blocks | `░▒▓█` | 4 densities | Heatmaps, density plots |
270+
| Braille | `⠁⠂…⣿` (U+2800–28FF) | 2×4 dots/cell | High-res line/scatter |
271+
| Sparkline | `▁▂▃▄▅▆▇█` | 8 heights | Inline mini-charts |
272+
273+
### Common widgets
274+
275+
| Widget | Pattern | Tips |
276+
|---|---|---|
277+
| Progress bar | `[████████░░░░] 67%` | Show % + ETA; gradient green→yellow→red by urgency |
278+
| Sparkline | `▁▂▃▅▇█▇▅▃▂` | Inline time-series in headers/status bars |
279+
| Gauge | `CPU [██████████░░] 83%` | Label + bar + value; color by threshold |
280+
| Table | Sortable, zebra stripes | Numbers right, text left; truncate with `` |
281+
| Tree | `├── └── │` guides | Indent 2–4/level; expand/collapse with Enter |
282+
| Diff | green `+`, red `-` | Word-level highlight within changed lines |
283+
| Log | colored level + ts + msg | TRACE dim · DEBUG cyan · INFO default · WARN yellow · ERROR red · FATAL red+bold |
284+
285+
### Spinners
286+
287+
| Context | Spinner | Interval |
288+
|---|---|---|
289+
| Default / modern | braille `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏` | 80ms |
290+
| Minimal | `-\|/` | 130ms |
291+
| Heavy processing | blocks `▖▘▝▗` | 100ms |
292+
293+
Spinners for indeterminate work, progress bars for determinate. Show spinners only after a ~200ms delay so fast operations don't flash.
294+
295+
---
296+
297+
## 6. Animation & motion
298+
299+
Flicker-free rendering, three layers — all required:
300+
1. **Double buffering** — render to an off-screen buffer, then swap. Never paint directly to the visible screen.
301+
2. **Diff-based updates** — compute the changed cells and emit only those escape sequences; don't repaint the whole screen each frame.
302+
3. **Frame budget** — cap at the display rate (15–60 fps is plenty for a TUI). Coalesce rapid state changes into one frame; throttle on resize.
303+
304+
Motion guidelines:
305+
- Animate to communicate state change (loading, transition, focus move), not for decoration.
306+
- Keep transitions short (<150ms feel). Anything longer needs a cancel path.
307+
- Respect reduced-motion preferences and `NO_COLOR`-style restraint — offer a static fallback.
308+
- Never animate on every keystroke; input must always feel instant.
309+
310+
---
311+
312+
## 7. Validate against anti-patterns
313+
314+
Before shipping, confirm none of these are true:
315+
316+
- [ ] Crashes or corrupts on resize / below minimum size (no size gate).
317+
- [ ] Hierarchy depends on true color — unusable in 16-color or `NO_COLOR`.
318+
- [ ] Information conveyed by color alone (no symbol/text/position backup).
319+
- [ ] Body-text contrast below WCAG AA (4.5:1).
320+
- [ ] Binds `Ctrl+C`, `Ctrl+Z`, or `Ctrl+\`.
321+
- [ ] No footer hints and no `?` help — undiscoverable.
322+
- [ ] Panels rearrange themselves between sessions (broken spatial memory).
323+
- [ ] Destructive action with no confirmation proportional to severity.
324+
- [ ] Full-screen repaint every frame (flicker, wasted bandwidth over SSH).
325+
- [ ] Hardcoded hex/ANSI in widget code instead of semantic slots.
326+
- [ ] Search doesn't filter live, or `Esc` doesn't dismiss it.
327+
- [ ] Disrupts shell scrollback (for overlay/popup tools).

0 commit comments

Comments
 (0)