https://github.com/kud/shui
π Fluid terminal UI for Zsh β a design system for the shell
https://github.com/kud/shui
cli components design-system shell terminal theme tui ui zsh zsh-library
Last synced: 23 days ago
JSON representation
π Fluid terminal UI for Zsh β a design system for the shell
- Host: GitHub
- URL: https://github.com/kud/shui
- Owner: kud
- License: mit
- Created: 2026-04-06T21:12:56.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-23T23:02:48.000Z (about 2 months ago)
- Last Synced: 2026-04-24T01:16:55.945Z (about 2 months ago)
- Topics: cli, components, design-system, shell, terminal, theme, tui, ui, zsh, zsh-library
- Language: Shell
- Homepage: https://kud.github.io/shui
- Size: 1.31 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-terminal-aesthetics - shui - based design system for Zsh scripts. Semantic components, themes, and icon sets β one file to source. (Styling & Output)
README
shui Β· ζ°΄
Shell UI for Zsh. ζ°΄ β fluid by design.
Features β’
Quick Start β’
Component Reference β’
Development
---
**shui** = Shell UI. ζ°΄ = water in Chinese β fluid, effortless, takes the shape of its container.
Most Zsh scripts scatter raw `echo -e "\033[32m..."` calls everywhere. shui gives you a proper design system instead β semantic components, a token-based theme engine, and a single consistent API.
One file to source. No dependencies. Pure Zsh.
> Examples below use `SHUI_ICONS=emoji` β works everywhere without a Nerd Font.
> Swap to `SHUI_ICONS=nerd` for richer glyphs if you have one installed.
---
## β¨ Features
- **Unified message API** β `shui message ` covers success, error, warning, info, and muted in a single consistent command
- **Token-based theme engine** β swap colours, icons, and styles via environment variables without touching component code
- **Inline components** β `badge` and `pill` write to stdout without a newline, composing naturally inside `$(...)` expressions
- **Interactive prompts** β `confirm`, `select`, `radio`, `multiselect`, and `input` with keyboard navigation and sensible defaults
- **Progress & spinners** β `progress`, `spinner`, and `loader` with optional native iTerm2 dock badge integration
- **Zero dependencies** β a single `source shui.zsh` is all you need; no npm, no brew, no external tools
- **NO_COLOR aware** β respects the [NO_COLOR](https://no-color.org/) convention; degrades gracefully to plain ASCII in constrained environments
---
## π Quick Start
```zsh
git clone https://github.com/kud/shui ~/.shui
source ~/.shui/shui.zsh
shui message success "Deployment complete"
shui message error "Build failed"
shui message warning "Deprecated flag used"
shui message info "Running in dry-run mode"
shui message muted "Skipped (already up-to-date)"
```
```console
β
Deployment complete
β Build failed
β οΈ Deprecated flag used
βΉοΈ Running in dry-run mode
Skipped (already up-to-date)
```
### Importing in a script
Source shui at the top of any Zsh script:
```zsh
#!/usr/bin/env zsh
source "${0:A:h}/lib/shui/shui.zsh"
```
`${0:A:h}` is the Zsh idiom for the absolute directory of the current script β the path resolves correctly regardless of where you call the script from.
To select a theme before loading:
```zsh
SHUI_THEME=minimal SHUI_ICONS=emoji source shui.zsh
```
---
## π Component Reference
### Messages
`shui message` is the preferred unified API for inline messages. It replaces the old `*-simple` shorthand commands.
```zsh
shui message
```
| Type | Output |
| --------- | ---------------------------- |
| `success` | Icon + success-coloured text |
| `error` | Icon + error-coloured text |
| `warning` | Icon + warning-coloured text |
| `info` | Icon + info-coloured text |
| `muted` | Italic dimmed text, no icon |
```zsh
shui message success "Deployed to production"
shui message error "Connection refused"
shui message warning "API key expires in 3 days"
shui message info "Running in dry-run mode"
shui message muted "Skipped β already installed"
```
```console
β
Deployed to production
β Connection refused
β οΈ API key expires in 3 days
βΉοΈ Running in dry-run mode
Skipped β already installed
```
> **Deprecated commands** β `shui info-simple`, `shui warning-simple`, `shui success-simple`, and `shui error-simple` still work but print a deprecation warning to stderr and will be removed in a future release. Migrate to `shui message `.
The top-level shorthand commands `shui success`, `shui error`, `shui warning`, and `shui info` remain available for quick one-liners.
---
### Text
Inline text formatting and semantic colour helpers.
```zsh
shui bold "Bold text"
shui dim "Dimmed text"
shui italic "Italic text"
shui underline "Underlined text"
shui text --color=success "Success colour"
shui text --color=muted "Muted colour"
```
Available colour types: `success` `error` `warning` `info` `primary` `muted` `accent`
---
### Layout
Structure your script output with sections, headings, and spacing.
```zsh
shui section "Setup"
shui subtitle "Installing packages"
shui subsection "npm dependencies"
shui subsection "brew formulae"
shui divider
shui spacer
shui spacer 3
```
```console
Setup
β Installing packages
β’ npm dependencies
β’ brew formulae
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
---
### Screen
Renders a section header, runs a command, then prints elapsed time. Returns the command's exit code.
```zsh
shui screen "Building" -- npm run build
shui screen "Running tests" -- zsh tests/test-components.zsh
```
```console
Building
β
Build complete
β± Building Β· 3s
```
```zsh
shui screen -- [argsβ¦]
```
---
### Timer
Lightweight per-step timing. Call `timer-start` before a block and `timer-end ` after β prints elapsed time in muted colour without affecting output or exit codes.
```zsh
shui timer-start
mise plugins update
shui timer-end "Plugin index update"
```
```console
β± Plugin index update Β· 2s
```
```zsh
shui timer-start
shui timer-end
```
---
### Badge
Solid background inline label. Writes to stdout **without a newline** β use inside `$(...)`.
```zsh
echo "Version: $(shui badge success v2.0)"
echo "Build: $(shui badge error FAIL) $(shui badge success PASS) $(shui badge muted SKIP)"
```
```zsh
shui badge
```
Available types: `success` `error` `warning` `info` `primary` `muted`
---
### Pill
Rounded-edge inline tag. Writes to stdout **without a newline** β use inside `$(...)`.
```zsh
echo "Status: $(shui pill warning beta)"
echo "$(shui pill success stable) $(shui pill muted deprecated)"
```
```zsh
shui pill
shui pill 135 "custom" # any 256-colour code (0β255)
```
Available types: `success` `error` `warning` `info` `primary` `muted` `accent` or any `0β255` colour code
---
### Box
Bordered content block with an optional title. Inline components work inside content.
```zsh
shui box "Simple content inside a box"
shui box --title="Summary" "3 installed\n1 skipped\n0 errors"
shui box --title="Status" "$(shui badge success OK) All systems nominal"
```
```console
ββββββββββββββββββββββββββββββββββββββββββββββ
β Simple content inside a box β
ββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββ Summary ββββββββββββββββββββ
β 3 installed β
β 1 skipped β
β 0 errors β
ββββββββββββββββββββββββββββββββββββββββββββββ
```
---
### Table
Pipe-separated (`|`) rows by default. First argument is the header. Column widths adjust automatically. Use `--sep` to change the delimiter.
```zsh
shui table \
"Package|Version|Status" \
"node|20.11.0|$(shui badge success OK)" \
"bun|1.1.3|$(shui badge success OK)" \
"python|3.12.0|$(shui badge warning outdated)"
```
```console
βββββββββββ¬βββββββββββ¬βββββββββββ
β Package β Version β Status β
βββββββββββΌβββββββββββΌβββββββββββ€
β node β 20.11.0 β OK β
β bun β 1.1.3 β OK β
β python β 3.12.0 β outdatedβ
βββββββββββ΄βββββββββββ΄βββββββββββ
```
---
### Progress
Adds a newline by default. Use `--inline` for loop-based updates.
```zsh
shui progress 50 100
shui progress 50 100 --width=30 --label="Downloading "
# loop use
for i in {1..100}; do
shui progress $i 100 --inline
sleep 0.05
done
echo
```
```console
ββββββββββββββββββββββββββββββββββββββββ 50%
Downloading ββββββββββββββββββββββββββββββ 50%
```
**iTerm2 Dock/tab badge** β pass `--iterm` (or `--iterm=`) to also update the native macOS progress indicator. No-op in other terminals.
| Flag | iTerm2 state |
| ---------------------------- | ------------------------ |
| `--iterm` / `--iterm=normal` | Normal (blue) |
| `--iterm=success` | Success (green) |
| `--iterm=warning` | Warning (yellow) |
| `--iterm=error` | Error (red) |
| `--iterm=indeterminate` | Spinning (no percentage) |
| `--iterm=clear` | Dismiss the indicator |
---
### Spinner
Runs a command in the background with a spinner. Exits with the command's exit code.
In iTerm2, automatically emits an indeterminate badge while running, switches to success or error on completion, then clears.
```zsh
shui spinner "Installingβ¦" -- brew install ripgrep
shui spinner \
--success="Installed!" \
--fail="Installation failed" \
"Installingβ¦" -- npm install
```
---
### Loader
Indeterminate loading indicator β loops for a fixed duration then clears the line. Use when you can't wrap a command in `spinner`.
```zsh
shui loader "Installing packages"
shui loader --duration=10 "Building"
shui loader --style=dots "Connecting" # default
shui loader --style=pulse "Connecting"
shui loader --style=spinner "Connecting"
```
```zsh
shui loader [--style=dots|pulse|spinner] [--duration=N]
```
| Style | Description |
| --------- | ---------------------------------- |
| `dots` | Cycling dot trail β `.` `..` `...` |
| `pulse` | Bold/dim alternating text |
| `spinner` | Braille spinner character |
---
### Animation
One-shot text effects β play once and exit.
```zsh
shui typewriter "Deploying to productionβ¦"
shui typewriter --delay=0.05 --color=success "Done!"
shui fade-in "Welcome"
shui fade-in --steps=10 "Welcome"
```
```zsh
shui typewriter [--delay=N] [--color=]
shui fade-in [--steps=N]
```
---
### Interactive
Prompt the user for confirmation, a selection, or free-form input.
```zsh
# Confirm β exits 0 for yes, 1 for no
shui confirm "Deploy to production?"
shui confirm --default=y "Continue?"
# Select β numbered list, prints the chosen option to stdout
choice=$(shui select "Pick a profile:" work personal staging)
# Radio β ββ to move, Enter to confirm. Prints chosen option to stdout.
env=$(shui radio "Target environment:" development staging production)
# Multiselect β ββ to move, Space to toggle, Enter to confirm.
# Returns selected options newline-separated to stdout.
choices=$(shui multiselect "Which packages?" brew npm cargo)
selected=("${(@f)choices}")
# Input β prints the entered value to stdout
name=$(shui input "Your name:")
name=$(shui input --default="world" "Your name:")
```
---
## Themes
### Built-in themes
| Theme | Description |
| --------- | -------------------------------------------- |
| `default` | 256-colour with automatic 16-colour fallback |
| `minimal` | Clean 16-colour ANSI palette |
| `plain` | No colour β text and ASCII icons only |
### Selecting a theme
```zsh
SHUI_THEME=minimal source shui.zsh
```
Or export from your shell profile so all scripts pick it up automatically:
```zsh
export SHUI_THEME=minimal
```
### NO_COLOR
shui respects the [NO_COLOR](https://no-color.org/) convention. When `$NO_COLOR` is set, inline components (`badge`, `pill`) fall back to ASCII representations and no colour codes are emitted.
### Custom themes
Generate a new theme pre-filled with all tokens:
```zsh
shui theme create mytheme
# β src/themes/mytheme.zsh
```
A custom theme sources `default.zsh` first β only override the tokens you want to change:
```zsh
# src/themes/mytheme.zsh
source "${SHUI_DIR}/src/themes/default.zsh"
SHUI_COLOR_PRIMARY=$(_shui_color "38;5;135" "0;35") # purple
SHUI_COLOR_ACCENT=$(_shui_color "38;5;135" "0;35")
```
Load it:
```zsh
SHUI_THEME=mytheme source shui.zsh
```
Manage themes:
```zsh
shui theme list # list available themes
shui theme validate # check all required tokens are defined
```
### Token reference
| Token | Purpose |
| --------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| `SHUI_RESET` | Reset all styles |
| `SHUI_BOLD` `SHUI_DIM` `SHUI_ITALIC` `SHUI_UNDERLINE` | Text styles |
| `SHUI_COLOR_PRIMARY` | Primary accent colour |
| `SHUI_COLOR_SUCCESS` | Success colour |
| `SHUI_COLOR_WARNING` | Warning colour |
| `SHUI_COLOR_ERROR` | Error colour |
| `SHUI_COLOR_INFO` | Info colour |
| `SHUI_COLOR_MUTED` | Secondary / dim text |
| `SHUI_COLOR_ACCENT` | Highlight accent |
| `SHUI_BG_SUCCESS` `SHUI_BG_WARNING` `SHUI_BG_ERROR` `SHUI_BG_INFO` `SHUI_BG_PRIMARY` `SHUI_BG_MUTED` | Badge background colours |
| `SHUI_ICON_SUCCESS` `SHUI_ICON_ERROR` `SHUI_ICON_WARNING` `SHUI_ICON_INFO` | Status icons |
| `SHUI_ICON_BULLET` `SHUI_ICON_ARROW` `SHUI_ICON_CHECK` `SHUI_ICON_CROSS` | UI icons |
| `SHUI_ICON_ROBOT` `SHUI_ICON_APPLE` `SHUI_ICON_GIT` `SHUI_ICON_FOLDER` `SHUI_ICON_LINK` `SHUI_ICON_CLOUD` | Infrastructure icons |
| `SHUI_ICON_NODE` `SHUI_ICON_PYTHON` `SHUI_ICON_RUBY` `SHUI_ICON_RUST` `SHUI_ICON_GO` `SHUI_ICON_GEM` `SHUI_ICON_BREW` | Language & tool icons |
---
## Icons
### Icon sets
| Set | Requires | Description |
| --------- | --------------------------------------- | ---------------------------------------- |
| `nerd` | [Nerd Font](https://www.nerdfonts.com/) | Rich glyphicons _(default)_ |
| `emoji` | Nothing | Unicode emoji, works everywhere |
| `none` | Nothing | No icons β text only |
| `unicode` | Nothing | Base layer β always loaded automatically |
> `unicode.zsh` is sourced before the selected icon set on every load. It defines geometric symbols (`βΊ`, `β`, `β²`, etc.) that work in any terminal. The selected icon set can override them β `none` blanks them all, `emoji` inherits them unchanged.
### Selecting an icon set
```zsh
SHUI_ICONS=emoji source shui.zsh # emoji
SHUI_ICONS=nerd source shui.zsh # nerd font (default)
SHUI_ICONS=none source shui.zsh # no icons
```
Combine freely with any theme:
```zsh
SHUI_THEME=minimal SHUI_ICONS=emoji source shui.zsh
SHUI_THEME=plain SHUI_ICONS=none source shui.zsh # fully plain
```
---
## Installation
### Manually
```zsh
git clone https://github.com/kud/shui ~/.shui
```
### As a project submodule (recommended for scripts)
```zsh
git submodule add https://github.com/kud/shui lib/shui
```
### Antidote
Add to your `.zsh_plugins.txt`:
```
kud/shui
```
Then reload:
```zsh
antidote load
```
### Zinit
```zsh
zinit light kud/shui
```
### Oh My Zsh
Clone into your custom plugins directory:
```zsh
git clone https://github.com/kud/shui ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/shui
```
Then add `shui` to the `plugins` array in `~/.zshrc`:
```zsh
plugins=(... shui)
```
### Zplug
```zsh
zplug "kud/shui"
```
---
## π§ Development
### Project structure
```
shui/
βββ shui.zsh # entry point β source this
βββ src/
β βββ components/ # message, badge, pill, box, table, progress, spinnerβ¦
β βββ icons/ # nerd.zsh, emoji.zsh, unicode.zsh, none.zsh
β βββ themes/ # default.zsh, minimal.zsh, plain.zsh
β βββ tokens/ # colors.zsh, contract.zsh
βββ tests/ # Zsh test harness + suites
```
### Task runner
Tasks are managed with [mise](https://mise.jdx.dev/):
| Task | Description |
| --------------- | --------------------------------- |
| `mise run test` | Run all test suites |
| `mise run lint` | Syntax-check all Zsh source files |
| `mise run demo` | Run the visual component demo |
### Tests
The test suite lives in `tests/` and uses a lightweight Zsh harness with inline β/β output per assertion.
```zsh
mise run test
# or run a single suite directly
zsh tests/test-components.zsh
```
The shared harness (`tests/_harness.zsh`) provides `assert_eq`, `assert_contains`, `assert_not_contains`, `assert_exit_ok`, and `strip_ansi`.
### Syntax check
```zsh
mise run lint
# equivalent to:
zsh -n shui.zsh && zsh -n src/**/*.zsh
```
### Demo
```zsh
zsh demo.zsh
zsh demo.zsh --interactive # includes confirm, select, and input
```
---
## Requirements
- Zsh 5.0+
- A [Nerd Font](https://www.nerdfonts.com/) for the `default` and `minimal` themes β or use `SHUI_ICONS=emoji` or `SHUI_ICONS=none`
---
## License
MIT Β© [kud](https://github.com/kud) β Made with β€οΈ