procedure-cli 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1070 @@
1
+ # B3AWESOME CLI App UI/UX Guideline
2
+
3
+ A comprehensive design system and UI/UX specification for building terminal-based CLI applications using **React + Ink.js**. This document captures every visual pattern, interaction model, and component specification so that new projects can reproduce the same polished CLI experience.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Design Philosophy](#1-design-philosophy)
10
+ 2. [Color System & Theme Architecture](#2-color-system--theme-architecture)
11
+ 3. [Typography & Text Hierarchy](#3-typography--text-hierarchy)
12
+ 4. [Layout System — The Gutter Pattern](#4-layout-system--the-gutter-pattern)
13
+ 5. [Screen Anatomy](#5-screen-anatomy)
14
+ 6. [Navigation Patterns](#6-navigation-patterns)
15
+ 7. [Wizard & Timeline Pattern](#7-wizard--timeline-pattern)
16
+ 8. [Form Patterns](#8-form-patterns)
17
+ 9. [Component Library](#9-component-library)
18
+ 10. [Welcome & Instruction Pattern](#10-welcome--instruction-pattern)
19
+ 11. [Confirmation & Destructive Actions](#11-confirmation--destructive-actions)
20
+ 12. [Empty & Error States](#12-empty--error-states)
21
+ 13. [ASCII Art Elements](#13-ascii-art-elements)
22
+ 14. [Theme Switching (Future)](#14-theme-switching-future)
23
+ 15. [Quick-Start Checklist](#15-quick-start-checklist)
24
+
25
+ ---
26
+
27
+ ## 1. Design Philosophy
28
+
29
+ ### Core Principles
30
+
31
+ | Principle | Description |
32
+ |-----------|-------------|
33
+ | **Guided, not guessed** | Every screen tells the user what it does and what to do next. No blank screens, no mystery options. |
34
+ | **Vertical rhythm** | Content flows top-to-bottom in a single column. The gutter line (`│`) creates visual continuity. |
35
+ | **Progressive disclosure** | Show only what's needed at each step. Wizards reveal one step at a time. |
36
+ | **Forgiving navigation** | Shift+Tab goes back. Esc returns to menu. Data is preserved when navigating backward. |
37
+ | **Config-driven** | Criteria, labels, teams, titles, levels — all configurable. The UI adapts to config, not the reverse. |
38
+ | **Minimal footprint** | No external UI libraries beyond Ink.js core. Every component is purpose-built and lightweight. |
39
+
40
+ ### Interaction Model
41
+
42
+ ```
43
+ User sees Heading → reads Instruction → scans CTAs → selects action → enters content area
44
+ ```
45
+
46
+ The user should **never** need to guess what a screen does or where they are in a workflow.
47
+
48
+ ---
49
+
50
+ ## 2. Color System & Theme Architecture
51
+
52
+ ### Semantic Color Roles
53
+
54
+ The color system is **semantic** — components reference roles, not hex values. This enables runtime theme switching.
55
+
56
+ | Role | Token | Purpose | Usage |
57
+ |------|-------|---------|-------|
58
+ | **brand** | `C.mauve` | Brand identity, active focus, cursor highlight | Banner, active step indicator, active select item (`❯`) |
59
+ | **heading** | `C.sapphire` | Screen titles, section headers | `<Text color={C.sapphire} bold>` |
60
+ | **success** | `C.green` | Completed steps, confirmed values, save success | Step indicator `◇`, welcome message, saved total |
61
+ | **warning** | `C.peach` | Warnings, caution states | Warning messages (rarely used) |
62
+ | **error** | `C.red` | Errors, destructive confirmations | Validation errors, delete confirmations |
63
+ | **highlight** | `C.yellow` | In-progress input, typing indicator | Currently active TextInput value |
64
+ | **accent** | `C.teal` | Secondary accent | Custom option indicator in multi-select |
65
+ | **muted** | `C.overlay1` | Gutter lines, hints, instructions, dim labels | `┌ │ └`, descriptions, navigation hints |
66
+ | **dimmed** | `C.overlay0` | Pending/inactive steps | Pending step indicator `○`, "No notes" text |
67
+ | **input-active** | `C.inputing` | Value currently being typed | TextInput active value |
68
+ | **input-confirmed** | `C.inputed` | Value already confirmed | Completed step summaries, confirmed field values |
69
+ | **label** | `C.label` | General text labels, content text | Field labels, employee names, note content |
70
+
71
+ ### Built-in Theme Definitions
72
+
73
+ Four themes ship pre-installed. Each maps the same semantic roles to theme-specific hex values.
74
+
75
+ #### Catppuccin Mocha (Dark) — Default
76
+
77
+ ```typescript
78
+ export const catppuccinMocha = {
79
+ name: 'catppuccin-mocha',
80
+ type: 'dark',
81
+ colors: {
82
+ mauve: '#cba6f7', // Catppuccin Mocha Mauve
83
+ green: '#a6e3a1', // Catppuccin Mocha Green
84
+ yellow: '#f9e2af', // Catppuccin Mocha Yellow
85
+ sapphire: '#74c7ec', // Catppuccin Mocha Sapphire
86
+ teal: '#94e2d5', // Catppuccin Mocha Teal
87
+ red: '#f38ba8', // Catppuccin Mocha Red
88
+ peach: '#fab387', // Catppuccin Mocha Peach
89
+ overlay1: '#7f849c', // Catppuccin Mocha Overlay1
90
+ overlay0: '#6c7086', // Catppuccin Mocha Overlay0
91
+ inputing: '#f9e2af', // = yellow
92
+ inputed: '#a6e3a1', // = green
93
+ label: '#cdd6f4', // Catppuccin Mocha Text
94
+ },
95
+ } as const;
96
+ ```
97
+
98
+ #### Catppuccin Latte (Light)
99
+
100
+ ```typescript
101
+ export const catppuccinLatte = {
102
+ name: 'catppuccin-latte',
103
+ type: 'light',
104
+ colors: {
105
+ mauve: '#8839ef', // Catppuccin Latte Mauve
106
+ green: '#40a02b', // Catppuccin Latte Green
107
+ yellow: '#df8e1d', // Catppuccin Latte Yellow
108
+ sapphire: '#209fb5', // Catppuccin Latte Sapphire
109
+ teal: '#179299', // Catppuccin Latte Teal
110
+ red: '#d20f39', // Catppuccin Latte Red
111
+ peach: '#fe640b', // Catppuccin Latte Peach
112
+ overlay1: '#8c8fa1', // Catppuccin Latte Overlay1
113
+ overlay0: '#9ca0b0', // Catppuccin Latte Overlay0
114
+ inputing: '#df8e1d', // = yellow
115
+ inputed: '#40a02b', // = green
116
+ label: '#4c4f69', // Catppuccin Latte Text
117
+ },
118
+ } as const;
119
+ ```
120
+
121
+ #### Tokyo Night (Dark)
122
+
123
+ ```typescript
124
+ export const tokyoNightDark = {
125
+ name: 'tokyonight-dark',
126
+ type: 'dark',
127
+ colors: {
128
+ mauve: '#bb9af7', // Tokyo Night Purple
129
+ green: '#9ece6a', // Tokyo Night Green
130
+ yellow: '#e0af68', // Tokyo Night Yellow
131
+ sapphire: '#7aa2f7', // Tokyo Night Blue
132
+ teal: '#73daca', // Tokyo Night Teal
133
+ red: '#f7768e', // Tokyo Night Red
134
+ peach: '#ff9e64', // Tokyo Night Orange
135
+ overlay1: '#565f89', // Tokyo Night Comment
136
+ overlay0: '#414868', // Tokyo Night Dark3
137
+ inputing: '#e0af68', // = yellow
138
+ inputed: '#9ece6a', // = green
139
+ label: '#c0caf5', // Tokyo Night Foreground
140
+ },
141
+ } as const;
142
+ ```
143
+
144
+ #### Tokyo Night Light
145
+
146
+ ```typescript
147
+ export const tokyoNightLight = {
148
+ name: 'tokyonight-light',
149
+ type: 'light',
150
+ colors: {
151
+ mauve: '#7847bd', // Tokyo Night Light Purple
152
+ green: '#485e30', // Tokyo Night Light Green
153
+ yellow: '#8f5e15', // Tokyo Night Light Yellow
154
+ sapphire: '#2e7de9', // Tokyo Night Light Blue
155
+ teal: '#387068', // Tokyo Night Light Teal
156
+ red: '#f52a65', // Tokyo Night Light Red
157
+ peach: '#b15c00', // Tokyo Night Light Orange
158
+ overlay1: '#9699a3', // Tokyo Night Light Comment
159
+ overlay0: '#b4b5b9', // Tokyo Night Light Dark3
160
+ inputing: '#8f5e15', // = yellow
161
+ inputed: '#485e30', // = green
162
+ label: '#3760bf', // Tokyo Night Light Foreground
163
+ },
164
+ } as const;
165
+ ```
166
+
167
+ ### Theme File Structure
168
+
169
+ ```
170
+ src/lib/
171
+ ├── theme.ts # Exports `C` — the active color tokens
172
+ └── themes/
173
+ ├── index.ts # Theme registry, loader, switcher
174
+ ├── catppuccin-mocha.ts
175
+ ├── catppuccin-latte.ts
176
+ ├── tokyonight-dark.ts
177
+ └── tokyonight-light.ts
178
+ ```
179
+
180
+ ### Theme Type Contract
181
+
182
+ ```typescript
183
+ export interface ThemeDefinition {
184
+ name: string;
185
+ type: 'dark' | 'light';
186
+ colors: {
187
+ mauve: string;
188
+ green: string;
189
+ yellow: string;
190
+ sapphire: string;
191
+ teal: string;
192
+ red: string;
193
+ peach: string;
194
+ overlay1: string;
195
+ overlay0: string;
196
+ inputing: string;
197
+ inputed: string;
198
+ label: string;
199
+ };
200
+ }
201
+ ```
202
+
203
+ ### Runtime Theme Switching
204
+
205
+ ```bash
206
+ # Set theme via CLI command
207
+ cli-name theme catppuccin-mocha # default
208
+ cli-name theme catppuccin-latte
209
+ cli-name theme tokyonight-dark
210
+ cli-name theme tokyonight-light
211
+
212
+ # List available themes
213
+ cli-name theme --list
214
+ ```
215
+
216
+ **Implementation pattern:**
217
+ 1. Theme preference stored in config file (e.g., `data/config.json` → `{ "theme": "catppuccin-mocha" }`)
218
+ 2. On startup, `theme.ts` reads config → loads matching theme definition → exports `C` object
219
+ 3. All components import `C` from `theme.ts` — never reference hex values directly
220
+ 4. Theme switch updates config file → next render uses new colors
221
+
222
+ ---
223
+
224
+ ## 3. Typography & Text Hierarchy
225
+
226
+ Terminal UI has no font sizes, so hierarchy is conveyed through **color**, **boldness**, and **position**.
227
+
228
+ ### Text Levels
229
+
230
+ | Level | Style | Token | Example |
231
+ |-------|-------|-------|---------|
232
+ | **Banner** | Bold, brand color | `C.mauve` | ASCII art app name |
233
+ | **Screen Heading** | Bold, heading color | `C.sapphire bold` | `Main Menu`, `New Assessment` |
234
+ | **Screen Instruction** | Normal, muted | `C.overlay1` | `Follow each step to record an employee evaluation.` |
235
+ | **Section Header** | Bold, heading color | `C.sapphire bold` | `Technical (50%)` within compare |
236
+ | **Field Label** | Normal, muted | `C.overlay1` | `Name:`, `Team:`, `Scores:` |
237
+ | **Field Value** | Normal or bold, label color | `C.label` or `C.inputed` | Employee name, confirmed input |
238
+ | **Active Input** | Yellow/highlight | `C.inputing` | Text currently being typed |
239
+ | **Confirmed Input** | Green/success | `C.inputed` | Completed step summary |
240
+ | **Hint Text** | Normal, muted | `C.overlay1` | `Shift+Tab to go back`, `↑↓ move, enter select` |
241
+ | **Error Text** | Normal, error color | `C.red` | `Name is required` |
242
+ | **Dimmed Text** | Normal, dimmed | `C.overlay0` | Pending step labels, `No notes` |
243
+
244
+ ### Text Rules
245
+
246
+ - **Headings** are always the first line of a screen, left-aligned, no gutter prefix
247
+ - **Instructions** appear directly below the heading, same alignment, muted color, no bold
248
+ - **Labels** use colon suffix: `Name: `, `Team: `, `Score: `
249
+ - **Values** follow labels on the same line, using `C.label` or `C.inputed`
250
+ - **Hints** appear below input areas, always `C.overlay1`
251
+ - **Never** use ALL CAPS for emphasis — use bold + color instead
252
+
253
+ ---
254
+
255
+ ## 4. Layout System — The Gutter Pattern
256
+
257
+ The gutter creates a continuous vertical line that visually groups related content, similar to a card or container in graphical UIs.
258
+
259
+ ### Gutter Characters
260
+
261
+ ```
262
+ ┌ ← Opening corner (start of content block)
263
+ │ content ← Gutter line with 2-space indent after pipe
264
+ │ ← Empty gutter line (spacer)
265
+ └ ← Closing corner (end of content block)
266
+ ```
267
+
268
+ ### GutterLine Component
269
+
270
+ ```tsx
271
+ // Every line inside a gutter block uses this component
272
+ export function GutterLine({ children }: { children?: React.ReactNode }) {
273
+ return (
274
+ <Box flexDirection="row">
275
+ <Text color={C.overlay1}>{"│ "}</Text>
276
+ {children}
277
+ </Box>
278
+ );
279
+ }
280
+ ```
281
+
282
+ **Rules:**
283
+ - Everything between `┌` and `└` uses `GutterLine` for content
284
+ - `GutterLine` with no children renders an empty spacer line (`│`)
285
+ - The gutter prefix is always `│` + 2 spaces, colored `C.overlay1`
286
+ - CTAs (GutteredSelect) render their own gutter prefix internally — do not wrap in GutterLine
287
+ - `┌` and `└` are standalone `<Text>` elements, not inside GutterLine
288
+
289
+ ### Nesting Pattern
290
+
291
+ Gutter blocks do **not** nest. Each screen/view has exactly one gutter block.
292
+
293
+ ```
294
+ Heading ← outside gutter
295
+ Instruction ← outside gutter
296
+ ┌ ← gutter start
297
+ │ Welcome message ← GutterLine
298
+ │ Instruction line 1 ← GutterLine
299
+ │ ← GutterLine spacer
300
+ │ ❯ Option 1 ← GutteredSelect (internal gutter)
301
+ │ Option 2 ← GutteredSelect (internal gutter)
302
+ │ ↑↓ move, enter select ← GutteredSelect hint (internal gutter)
303
+ └ ← gutter end
304
+ ```
305
+
306
+ ---
307
+
308
+ ## 5. Screen Anatomy
309
+
310
+ Every screen follows this consistent structure:
311
+
312
+ ```
313
+ ┌─────────────────────────────────────────────────┐
314
+ │ Heading (C.sapphire, bold) │ ← 1. Screen title
315
+ │ Instruction (C.overlay1) │ ← 2. One-line description
316
+ │ ┌ │ ← 3. Gutter open
317
+ │ │ Context info (C.overlay1 labels) │ ← 4. Contextual data
318
+ │ │ │ ← 5. Spacer
319
+ │ │ ❯ Action 1 (GutteredSelect) │ ← 6. CTAs
320
+ │ │ Action 2 │
321
+ │ │ ↑↓ move, enter select │
322
+ │ └ │ ← 7. Gutter close
323
+ └─────────────────────────────────────────────────┘
324
+ ```
325
+
326
+ ### Screen Types
327
+
328
+ #### 1. Menu Screen (Main Menu, Sub-menus)
329
+
330
+ ```
331
+ Heading
332
+
333
+ │ Welcome / context info
334
+
335
+ │ ❯ CTA options...
336
+ │ ↑↓ move, enter select
337
+
338
+ ```
339
+
340
+ - Welcome message or context above CTAs inside gutter
341
+ - Always includes a "Back" option as last CTA
342
+
343
+ #### 2. List Screen (View Employees, Search Results)
344
+
345
+ ```
346
+ Heading
347
+ Instruction
348
+
349
+ │ Count info (e.g., "5 employees")
350
+ │ ❯ Item 1 — description
351
+ │ Item 2 — description
352
+ │ Back to Menu
353
+ │ ↑↓ move, enter select
354
+
355
+ ```
356
+
357
+ - Count summary before items
358
+ - Scrollable with `maxVisible` prop
359
+ - Back option at bottom of list
360
+
361
+ #### 3. Detail Screen (Employee Detail)
362
+
363
+ ```
364
+ Heading
365
+
366
+ │ Name (bold)
367
+ │ Label: Value
368
+ │ Label: Value
369
+
370
+ │ Section data...
371
+
372
+ │ ❯ Action options...
373
+ │ ↑↓ move, enter select
374
+
375
+ ```
376
+
377
+ - Read-only info block at top
378
+ - Actions at bottom
379
+
380
+ #### 4. Wizard Screen (New Assessment)
381
+
382
+ ```
383
+ Heading
384
+ Instruction
385
+
386
+ ◇ Step 1 (completed)
387
+ │ Summary of step 1
388
+
389
+ ◆ Step 2 (active)
390
+ │ [Active step content — forms, selects]
391
+
392
+ ○ Step 3 (pending)
393
+
394
+ ○ Step 4 (pending)
395
+
396
+ ```
397
+
398
+ - Uses Timeline component with StepIndicator
399
+ - One step active at a time
400
+ - Completed steps show summaries
401
+
402
+ #### 5. Confirmation Screen
403
+
404
+ ```
405
+ Heading
406
+
407
+ │ Warning/summary text
408
+ │ Detail text
409
+ │ ❯ Cancel (safe option first)
410
+ │ Destructive action
411
+ │ ↑↓ move, enter select
412
+
413
+ ```
414
+
415
+ - Safe option (Cancel) always listed first
416
+ - Destructive option uses red text or clear labeling
417
+
418
+ ---
419
+
420
+ ## 6. Navigation Patterns
421
+
422
+ ### Global Navigation
423
+
424
+ | Key | Context | Action |
425
+ |-----|---------|--------|
426
+ | `Esc` | Any screen except menu | Return to Main Menu |
427
+ | `↑` / `↓` | GutteredSelect active | Move selection cursor |
428
+ | `Enter` | GutteredSelect active | Confirm selection |
429
+ | `Space` | GutteredMultiSelect active | Toggle item |
430
+ | `Shift+Tab` | Wizard step / Form field | Go back to previous field/step |
431
+
432
+ ### Back Navigation Strategy
433
+
434
+ Every view must have a **visible** way to go back:
435
+
436
+ 1. **Wizard steps**: Shift+Tab navigates to previous step/field. Hint text shows `Shift+Tab to go back`.
437
+ 2. **List/Menu screens**: Last CTA option is always `Back to Menu` / `Back to List` / `Back to Detail`.
438
+ 3. **Global**: Esc returns to Main Menu from any screen.
439
+
440
+ ### Shift+Tab Implementation
441
+
442
+ ```tsx
443
+ useInput((_input, key) => {
444
+ if (!(key.shift && key.tab)) return;
445
+ // Navigate to previous phase, preserving form data
446
+ switch (phase) {
447
+ case 'field-2': setPhase('field-1'); break;
448
+ case 'field-3': setPhase('field-2'); break;
449
+ // ...
450
+ }
451
+ });
452
+ ```
453
+
454
+ **Rules:**
455
+ - Form data is **preserved** when going back — never clear on Shift+Tab
456
+ - Clear only the error state when going back
457
+ - Sub-phases (e.g., `team-custom`) go back to their parent phase (`team`)
458
+
459
+ ---
460
+
461
+ ## 7. Wizard & Timeline Pattern
462
+
463
+ ### Timeline Component
464
+
465
+ The Timeline renders a vertical wizard with step indicators and a continuous gutter line.
466
+
467
+ ```
468
+
469
+ ◇ Employee ← completed (green diamond outline)
470
+ │ John Doe — Senior ← summary (green text)
471
+
472
+ ◆ Interviewer ← active (animated purple diamond)
473
+ │ [form content] ← active step content
474
+
475
+ ○ Scores ← pending (dim circle)
476
+
477
+ ○ Notes ← pending
478
+
479
+ ```
480
+
481
+ ### Step Indicators
482
+
483
+ | Status | Symbol | Animation | Color |
484
+ |--------|--------|-----------|-------|
485
+ | **Completed** | `◇` | None | `C.green` |
486
+ | **Active** | `◆` → `◈` → `◇` → `◈` | 350ms cycle | `C.mauve` (bold) |
487
+ | **Pending** | `○` | None | `C.overlay0` |
488
+
489
+ ### Step State Machine
490
+
491
+ ```
492
+ pending → active → completed
493
+ ```
494
+
495
+ - Only one step is active at a time
496
+ - `currentStep` index controls which step is active
497
+ - Steps before `currentStep` are completed; steps after are pending
498
+ - Each step component calls `onComplete(data)` which advances `currentStep`
499
+
500
+ ### Summary Display
501
+
502
+ When a step is completed, its summary appears below the indicator:
503
+
504
+ ```
505
+ ◇ Scores
506
+ │ Tech: 4 | Eff: 3 | Adapt: 5 = 3.90
507
+ ```
508
+
509
+ Summary is computed by a `getSummary(stepIndex)` function in the parent screen.
510
+
511
+ ---
512
+
513
+ ## 8. Form Patterns
514
+
515
+ ### TextInput
516
+
517
+ ```
518
+ │ Field Label: ← GutterLine with bold text
519
+ │ [input value] ← GutterLine with TextInput
520
+ │ Error message ← GutterLine with C.red (conditional)
521
+ │ Hint text ← GutterLine with C.overlay1
522
+ ```
523
+
524
+ **Props pattern:**
525
+ ```tsx
526
+ <GutterLine><Text bold>Employee Name:</Text></GutterLine>
527
+ <GutterLine>
528
+ <TextInput
529
+ placeholder="Full name (required)"
530
+ defaultValue={form.name}
531
+ onSubmit={(val) => { /* validate and advance */ }}
532
+ />
533
+ </GutterLine>
534
+ {error && <GutterLine><Text color={C.red}>{error}</Text></GutterLine>}
535
+ <GutterLine><Text color={C.overlay1}>Shift+Tab to go back</Text></GutterLine>
536
+ ```
537
+
538
+ ### GutteredSelect (Single Select)
539
+
540
+ ```
541
+ │ ❯ Option 1 — description ← active item (C.mauve, bold)
542
+ │ Option 2 — description ← inactive item
543
+ │ Option 3 — description
544
+ │ ↑↓ move, enter select ← hint
545
+ ```
546
+
547
+ **Every option should have a `description` prop** for context.
548
+
549
+ **Scrolling**: When options exceed `maxVisible` (default 5), shows `↑ more` / `↓ more` indicators.
550
+
551
+ ### GutteredMultiSelect
552
+
553
+ ```
554
+ │ ❯ ● Item 1 — description ← selected (C.green dot)
555
+ │ ○ Item 2 — description ← unselected (C.overlay0 dot)
556
+ │ ● Item 3 — description
557
+ │ ↑↓ move, space toggle, enter confirm
558
+ │ Selected: Item 1, Item 3
559
+ ```
560
+
561
+ **Features:**
562
+ - `●` for selected, `○` for unselected
563
+ - Optional `allowCustom` for "Other (type custom)" option
564
+ - Selected items shown at bottom as comma-separated list
565
+
566
+ ### Validation Pattern
567
+
568
+ ```tsx
569
+ onSubmit={(val) => {
570
+ if (!validateRequired(val)) {
571
+ setError('Name is required');
572
+ return; // Don't advance
573
+ }
574
+ setError('');
575
+ setForm({ ...form, name: val });
576
+ setPhase('next-field');
577
+ }}
578
+ ```
579
+
580
+ - Validate on submit, not on change
581
+ - Show error below input in `C.red`
582
+ - Clear error on successful validation or Shift+Tab
583
+
584
+ ### Optional Fields
585
+
586
+ ```
587
+ │ Current Level:
588
+ │ [input]
589
+ │ Optional — press enter to skip · Shift+Tab to go back
590
+ ```
591
+
592
+ - Hint explicitly states "Optional — press enter to skip"
593
+ - Empty submit = `null` value
594
+
595
+ ---
596
+
597
+ ## 9. Component Library
598
+
599
+ ### Banner
600
+
601
+ ASCII art header at the top of the application. Displayed once, always visible.
602
+
603
+ ```
604
+ █▀▀ █▀█ █▀▄▀█ █▀█ █▀▀ ▀█▀ █▀▀ █▀█ █▀▀ █▄█
605
+ █ █ █ █ ▀ █ █▀▀ █▀▀ █ █▀▀ █ █ █ ░█░
606
+ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀▀▀ ▀ ▀ ▀▀▀ ░▀░
607
+ App Subtitle
608
+ ────────────────────────────────────────────────
609
+ ```
610
+
611
+ - Banner text: `C.mauve`
612
+ - Subtitle: default text color
613
+ - Separator: `─` repeated, `C.overlay1`
614
+ - `marginBottom={0}` — spacing handled by parent
615
+
616
+ ### Timeline
617
+
618
+ Vertical wizard container. Wraps step indicators + active step content.
619
+
620
+ ```tsx
621
+ <Timeline steps={timelineSteps}>
622
+ {renderActiveStep()}
623
+ </Timeline>
624
+ ```
625
+
626
+ - `steps: TimelineStep[]` — name, status, summary
627
+ - `children` — rendered inline under the active step
628
+ - Handles `┌` / `└` corners and `│` spacers between steps
629
+
630
+ ### GutterLine
631
+
632
+ Single line within a gutter block.
633
+
634
+ ```tsx
635
+ <GutterLine>
636
+ <Text color={C.overlay1}>Label: </Text>
637
+ <Text color={C.label}>Value</Text>
638
+ </GutterLine>
639
+ ```
640
+
641
+ - Prefix: `│` + 2 spaces (`C.overlay1`)
642
+ - Empty `<GutterLine />` = spacer line
643
+
644
+ ### GutteredSelect
645
+
646
+ Custom single-select with gutter integration.
647
+
648
+ ```tsx
649
+ <GutteredSelect
650
+ options={[
651
+ { label: 'Option 1', value: 'opt1', description: 'What this does' },
652
+ { label: 'Back', value: 'back', description: 'Return to previous screen' },
653
+ ]}
654
+ onChange={(val) => handleSelect(val)}
655
+ maxVisible={5}
656
+ />
657
+ ```
658
+
659
+ - Renders own gutter prefix
660
+ - Active item: `❯` in `C.mauve`
661
+ - Scroll indicators when items exceed `maxVisible`
662
+ - ANSI input normalization for deterministic key handling
663
+
664
+ ### GutteredMultiSelect
665
+
666
+ Multi-select variant with space-to-toggle and enter-to-confirm.
667
+
668
+ ```tsx
669
+ <GutteredMultiSelect
670
+ options={options}
671
+ onSubmit={(selectedIds) => handleSelection(selectedIds)}
672
+ allowCustom={true}
673
+ customPlaceholder="Type custom values"
674
+ />
675
+ ```
676
+
677
+ ### StepIndicator
678
+
679
+ Animated step status indicator for timeline.
680
+
681
+ ```tsx
682
+ <StepIndicator status="active" label="Scores" />
683
+ ```
684
+
685
+ - `completed`: `◇` green + label in `C.label`
686
+ - `active`: animated `◆◈◇◈` at 350ms in `C.mauve` + bold label
687
+ - `pending`: `○` in `C.overlay0` + dim label
688
+
689
+ ---
690
+
691
+ ## 10. Welcome & Instruction Pattern
692
+
693
+ ### Main Menu Welcome
694
+
695
+ Inside the gutter block, above CTAs:
696
+
697
+ ```
698
+ Main Menu
699
+
700
+ │ Welcome to [App Name]! ← C.green, bold, GutterLine
701
+ │ First line of description ← C.overlay1, GutterLine
702
+ │ Second line of description ← C.overlay1, GutterLine
703
+ │ ← GutterLine spacer
704
+ │ ❯ First action ← CTAs start here
705
+ │ Second action
706
+
707
+ ```
708
+
709
+ **Rules:**
710
+ - Welcome line uses `C.green` bold
711
+ - Description lines use `C.overlay1`
712
+ - One empty GutterLine spacer between description and CTAs
713
+ - Welcome and description are **inside** the gutter, same indent as CTAs
714
+
715
+ ### Per-Screen Instructions
716
+
717
+ Below the heading, outside the gutter, left-aligned:
718
+
719
+ ```
720
+ Screen Heading ← C.sapphire, bold
721
+ One-line instruction text ← C.overlay1, <Text>
722
+
723
+ │ Content...
724
+
725
+ ```
726
+
727
+ **Rules:**
728
+ - Instruction is a plain `<Text color={C.overlay1}>` — no GutterLine, no bold
729
+ - Aligns left with the heading (no indent)
730
+ - One line only — concise description of what the screen does
731
+ - Not all screens need instructions — use for primary entry screens
732
+
733
+ **Example instructions:**
734
+
735
+ | Screen | Instruction |
736
+ |--------|-------------|
737
+ | New Assessment | Follow each step to record an employee evaluation. |
738
+ | Edit Assessment | Update scores, notes, or assessed levels for this assessment. |
739
+ | View Employees | Browse the employee list to view details and manage assessments. |
740
+ | Search & Filter | Find employees by name, team, title, or level. |
741
+ | Compare | Select two or more employees to compare their assessment results side by side. |
742
+ | Export CSV | Export assessment data as CSV for reporting and analysis. |
743
+ | Edit Employee | Update this employee's information. |
744
+
745
+ ---
746
+
747
+ ## 11. Confirmation & Destructive Actions
748
+
749
+ ### Confirmation Dialog Pattern
750
+
751
+ ```
752
+ Delete Assessment ← C.sapphire heading
753
+
754
+ │ Delete assessment by John (2025-01-15)? ← C.red warning text
755
+ │ This cannot be undone. ← C.overlay1 detail
756
+ │ ❯ Cancel ← Safe option FIRST
757
+ │ Delete ← Destructive option
758
+
759
+ ```
760
+
761
+ ### Rules
762
+
763
+ 1. **Cancel/safe option is always first** in the CTA list
764
+ 2. **Destructive text uses `C.red`** for the warning message
765
+ 3. **"This cannot be undone."** standard disclaimer in `C.overlay1`
766
+ 4. **Never auto-select the destructive option** — cursor starts on Cancel
767
+ 5. **Cascade warnings** — if deleting an employee also deletes N assessments, state it explicitly:
768
+ ```
769
+ │ Delete John Doe and all 3 assessments?
770
+ ```
771
+
772
+ ### Description Props
773
+
774
+ Every CTA option includes a `description` for clarity:
775
+
776
+ ```tsx
777
+ { label: 'Cancel', value: 'cancel', description: 'Keep assessment and go back' }
778
+ { label: 'Delete', value: 'delete', description: 'Permanently remove this assessment' }
779
+ ```
780
+
781
+ ---
782
+
783
+ ## 12. Empty & Error States
784
+
785
+ ### Empty State
786
+
787
+ When a list or view has no data:
788
+
789
+ ```
790
+ View Employees
791
+
792
+ │ No employees found ← C.overlay1
793
+ │ ❯ Create New Assessment ← Constructive action
794
+ │ Back to Menu
795
+
796
+ ```
797
+
798
+ **Rules:**
799
+ - Show a clear message about what's empty
800
+ - Offer a constructive action (create something)
801
+ - Always include Back option
802
+
803
+ ### Error State
804
+
805
+ ```
806
+ │ Error: Failed to load employees: database locked ← C.red
807
+ ```
808
+
809
+ or inline field error:
810
+
811
+ ```
812
+ │ Employee Name:
813
+ │ [input]
814
+ │ Name is required ← C.red
815
+ ```
816
+
817
+ **Rules:**
818
+ - Errors use `C.red`
819
+ - Field errors appear directly below the input
820
+ - Screen-level errors appear as GutterLine inside the gutter
821
+ - Never crash — always show error and offer navigation back
822
+
823
+ ### Loading State
824
+
825
+ ```
826
+ Loading... ← C.overlay1
827
+ ```
828
+
829
+ - Shown during config/DB initialization
830
+ - Minimal — just text, no spinner needed for fast loads
831
+ - Use `ink-spinner` for longer async operations
832
+
833
+ ---
834
+
835
+ ## 13. ASCII Art Elements
836
+
837
+ ### Score Bars (Compare Screen)
838
+
839
+ ```
840
+ │ John Doe ████████████████░░░░ 4.00
841
+ │ Jane Smith ████████████░░░░░░░░ 3.00
842
+ ```
843
+
844
+ ```typescript
845
+ const BAR_WIDTH = 20;
846
+ function scoreBar(score: number | null, max: number = 5): string {
847
+ if (score === null) return '░'.repeat(BAR_WIDTH);
848
+ const filled = Math.round((score / max) * BAR_WIDTH);
849
+ return '█'.repeat(filled) + '░'.repeat(BAR_WIDTH - filled);
850
+ }
851
+ ```
852
+
853
+ - Filled: `█` in `C.mauve`
854
+ - Empty: `░`
855
+ - Score value: `C.inputed` bold
856
+ - Width: 20 characters
857
+
858
+ ### Separators
859
+
860
+ ```
861
+ ────────────────────────────────────────────────
862
+ ```
863
+
864
+ - `─` repeated, colored `C.overlay1`
865
+ - Used after Banner subtitle
866
+
867
+ ### Ranking Indicators
868
+
869
+ ```
870
+ │ John Doe 4.50 #1 ← C.green for rank 1
871
+ │ Jane Smith 3.80 #2 ← C.overlay1 for others
872
+ ```
873
+
874
+ - Top rank: `C.green` with `★` suffix
875
+ - Other ranks: `C.overlay1`
876
+
877
+ ### Gutter Corners & Lines
878
+
879
+ | Character | Purpose | Color |
880
+ |-----------|---------|-------|
881
+ | `┌` | Block open | `C.overlay1` |
882
+ | `│` | Vertical gutter | `C.overlay1` |
883
+ | `└` | Block close | `C.overlay1` |
884
+ | `─` | Horizontal separator | `C.overlay1` |
885
+
886
+ ### Selection Indicators
887
+
888
+ | Character | Meaning | Color |
889
+ |-----------|---------|-------|
890
+ | `❯` | Active cursor | `C.mauve` bold |
891
+ | `●` | Selected (multi) | `C.green` |
892
+ | `○` | Unselected (multi) | `C.overlay0` |
893
+ | `◇` | Completed step | `C.green` |
894
+ | `◆` | Active step | `C.mauve` bold |
895
+ | `✎` | Custom input option | default |
896
+
897
+ ---
898
+
899
+ ## 14. Theme Switching (Future)
900
+
901
+ ### CLI Command
902
+
903
+ ```bash
904
+ # Switch theme
905
+ cli-name theme tokyonight-dark
906
+
907
+ # List available themes
908
+ cli-name theme --list
909
+ # Output:
910
+ # catppuccin-mocha (dark) ← active
911
+ # catppuccin-latte (light)
912
+ # tokyonight-dark (dark)
913
+ # tokyonight-light (light)
914
+ ```
915
+
916
+ ### Implementation Architecture
917
+
918
+ ```
919
+ src/lib/
920
+ ├── theme.ts # Reads config, exports active `C`
921
+ └── themes/
922
+ ├── index.ts # Registry + loader
923
+ ├── catppuccin-mocha.ts # ThemeDefinition
924
+ ├── catppuccin-latte.ts # ThemeDefinition
925
+ ├── tokyonight-dark.ts # ThemeDefinition
926
+ └── tokyonight-light.ts # ThemeDefinition
927
+
928
+ data/
929
+ └── config.json # { "theme": "catppuccin-mocha" }
930
+ ```
931
+
932
+ ### theme.ts Pattern
933
+
934
+ ```typescript
935
+ import { loadThemePreference } from './themes/index.js';
936
+
937
+ const theme = loadThemePreference(); // reads config.json, falls back to default
938
+
939
+ export const C = theme.colors;
940
+ ```
941
+
942
+ ### themes/index.ts Pattern
943
+
944
+ ```typescript
945
+ import { catppuccinMocha } from './catppuccin-mocha.js';
946
+ import { catppuccinLatte } from './catppuccin-latte.js';
947
+ import { tokyoNightDark } from './tokyonight-dark.js';
948
+ import { tokyoNightLight } from './tokyonight-light.js';
949
+ import type { ThemeDefinition } from '../types.js';
950
+
951
+ const THEMES: Record<string, ThemeDefinition> = {
952
+ 'catppuccin-mocha': catppuccinMocha,
953
+ 'catppuccin-latte': catppuccinLatte,
954
+ 'tokyonight-dark': tokyoNightDark,
955
+ 'tokyonight-light': tokyoNightLight,
956
+ };
957
+
958
+ const DEFAULT_THEME = 'catppuccin-mocha';
959
+
960
+ export function loadThemePreference(): ThemeDefinition {
961
+ // Read from config file, fallback to default
962
+ try {
963
+ const config = JSON.parse(readFileSync('data/config.json', 'utf-8'));
964
+ return THEMES[config.theme] ?? THEMES[DEFAULT_THEME]!;
965
+ } catch {
966
+ return THEMES[DEFAULT_THEME]!;
967
+ }
968
+ }
969
+
970
+ export function setTheme(themeName: string): boolean {
971
+ if (!THEMES[themeName]) return false;
972
+ // Write to config.json
973
+ writeConfigTheme(themeName);
974
+ return true;
975
+ }
976
+
977
+ export function listThemes(): ThemeDefinition[] {
978
+ return Object.values(THEMES);
979
+ }
980
+ ```
981
+
982
+ ### Adding a Custom Theme
983
+
984
+ To add a new theme:
985
+
986
+ 1. Create `src/lib/themes/my-theme.ts` implementing `ThemeDefinition`
987
+ 2. Register it in `themes/index.ts` THEMES record
988
+ 3. Rebuild — the theme is now available via `cli-name theme my-theme`
989
+
990
+ ---
991
+
992
+ ## 15. Quick-Start Checklist
993
+
994
+ When creating a new CLI app with this design system:
995
+
996
+ ### Setup
997
+
998
+ - [ ] Initialize project: React 19 + Ink.js 6 + TypeScript (ES2022, Node16, `react-jsx`)
999
+ - [ ] Install: `react`, `ink`, `@inkjs/ui`, `ink-spinner`, `better-sqlite3`, `nanoid`
1000
+ - [ ] Create `src/lib/theme.ts` with semantic color tokens
1001
+ - [ ] Copy theme definitions for all 4 built-in themes
1002
+ - [ ] Create GutterLine, GutteredSelect, GutteredMultiSelect, StepIndicator, Timeline, Banner components
1003
+
1004
+ ### Screen Scaffolding
1005
+
1006
+ - [ ] Entry point: `cli.tsx` → renders `<App />` via Ink's `render()`
1007
+ - [ ] App shell: Banner + screen router with `useState<Screen>('menu')`
1008
+ - [ ] Global Esc handler: `useInput` → return to menu
1009
+ - [ ] Main Menu with welcome message inside gutter
1010
+ - [ ] Each screen: Heading → Instruction → Gutter block → Content → CTAs
1011
+
1012
+ ### Interaction
1013
+
1014
+ - [ ] Every GutteredSelect option has `label` + `value` + `description`
1015
+ - [ ] Every screen has a visible Back option
1016
+ - [ ] Shift+Tab navigates backward in all wizard steps and form fields
1017
+ - [ ] Form data preserved on backward navigation
1018
+ - [ ] Validation on submit, errors in `C.red`, cleared on re-attempt or Shift+Tab
1019
+ - [ ] Cancel before Delete in confirmation dialogs
1020
+
1021
+ ### Polish
1022
+
1023
+ - [ ] Empty states with constructive actions
1024
+ - [ ] Error states with `C.red` and navigation back
1025
+ - [ ] Scroll indicators (`↑ more` / `↓ more`) for long lists
1026
+ - [ ] Navigation hints on every interactive element (`↑↓ move, enter select`, `Shift+Tab to go back`)
1027
+ - [ ] ASCII banner with brand identity
1028
+
1029
+ ---
1030
+
1031
+ ## Appendix: Reference Implementation
1032
+
1033
+ This guideline was extracted from the **Team Competency Assessment Tool** — a production CLI app built with this exact design system. Source code structure:
1034
+
1035
+ ```
1036
+ src/
1037
+ ├── cli.tsx # Entry point
1038
+ ├── app.tsx # App shell, menu, screen router
1039
+ ├── components/
1040
+ │ ├── banner.tsx # ASCII art header
1041
+ │ ├── timeline.tsx # Vertical wizard with step indicators
1042
+ │ ├── step-indicator.tsx # Animated ◆/◇/○ indicators
1043
+ │ ├── gutter-line.tsx # │ prefix component
1044
+ │ └── guttered-select.tsx # Select + MultiSelect with gutter
1045
+ ├── screens/
1046
+ │ ├── new-assessment.tsx # 6-step wizard
1047
+ │ ├── edit-assessment.tsx # 4-step wizard
1048
+ │ ├── view-employees.tsx # List → Detail → Actions
1049
+ │ ├── search.tsx # Filter → Results → Detail
1050
+ │ ├── compare.tsx # MultiSelect → Score bars
1051
+ │ ├── export.tsx # Menu → Result
1052
+ │ ├── edit-employee.tsx # Multi-field form
1053
+ │ └── steps/ # Wizard step components
1054
+ │ ├── employee-step.tsx # 5-phase: Name→Team→Title→Level→Manager
1055
+ │ ├── interviewer-step.tsx # 4-phase: Name→Team→Title→Level
1056
+ │ ├── scores-step.tsx # Score input per criterion
1057
+ │ ├── notes-step.tsx # Multi-field text input
1058
+ │ ├── assessed-levels-step.tsx# Add/remove skill entries
1059
+ │ └── confirm-step.tsx # Review + save
1060
+ └── lib/
1061
+ ├── theme.ts # Color tokens (C.mauve, C.green, etc.)
1062
+ ├── types.ts # All TypeScript types
1063
+ ├── config.ts # JSON config loader
1064
+ ├── db.ts # SQLite CRUD
1065
+ ├── csv.ts # CSV export
1066
+ ├── employee-config.ts # Teams, titles, levels
1067
+ ├── validation.ts # Score validation
1068
+ └── input/
1069
+ └── normalize-keys.ts # ANSI input normalization
1070
+ ```