Skip to content

github/TUIKit

TUIkit Specs

A spec-driven system for building UI components across programming languages. Each component is defined as a language-agnostic markdown spec with behavioral tests. An LLM agent acts as the "compiler" — reading specs and generating idiomatic implementations per target framework.

How it works

flowchart LR
    Specs["Spec files\n(.md)"] --> Compile["compile.ts\n(prompt)"]
    Compile --> Agent["LLM Agent\n(compiler)"]
    Agent --> Dist["dist/{target}/\n(generated code)"]
    Agent --> Lock["lock file\n(.json)"]
Loading
  1. Specs define behavior + semantic tokens (like headless UI libraries)
  2. Target specs define how to translate to a specific language/framework
  3. compile.ts detects changed specs and generates a self-contained prompt
  4. An LLM agent reads the prompt and generates idiomatic code
  5. Generated code goes to dist/ — specs stay clean
  6. Lock files track which spec versions have been compiled

Quick start

Prerequisites

  • Bun 1.1+ installed (bun --version)

Install dependencies

bun install

Common commands

# Lint all specs against the schema
bun run lint

# Check what needs compiling
bun run compile status

# Generate a prompt for a target
bun run compile prompt --target go

# The prompt is written to dist/go/_compile-prompt.md
# Feed it to an LLM agent (e.g. Copilot CLI, Claude, etc.)
# The agent writes generated code to dist/go/

# After verifying the generated code works, lock the hashes
bun run compile lock --target go

Repository structure

TUIKit/
  components/       Component specs, tests, and preview definitions
  tokens/           Semantic design tokens (colors, icons, breakpoints)
  targets/          Target language/framework definitions
  docs/             Meta-schema and design foundations
  scripts/          Compiler and linter CLIs
  dist/             Compiled output per target (gitignored)

Writing specs

Component spec

Each component is a markdown file with YAML frontmatter and prose body:

---
kind: component
name: MyComponent
description: One-line summary.
version: 1
category: input          # input | display | navigation | layout | feedback

tokens:
    colors: [textPrimary, selected]
    icons: [iconPrompt]

props:
    label:
        type: string
        required: true
        description: Display text.

dependencies:
    tokens:
        - name: textPrimary
          kind: color
          usage: "Label text"
          required: true
    components: []

accessibility:
    role: button
    announce:
        on_mount: "Button: {label}"
---

## Visual rules

- Label text MUST use the `textPrimary` color token
- Active state MUST use the `selected` color token

## Rendering example

Given label: "Click me"

​```
Click me
​```

## Dependencies

| Dependency | Kind | Usage | Required |
|------------|------|-------|----------|
| `textPrimary` | color | Label text | Yes |
| `selected` | color | Active state | Yes |

Test spec

Test specs live alongside component specs and use a block-based format:

---
kind: test
component: MyComponent
version: 1
---

## renders label text

​`props
label: "Hello"
​`

​`expect
Hello
​`

See docs/schema.md for the full format reference, including input, state, style, and accessibility test blocks.

Conformance language

All normative sections (Visual rules, Behavior, Edge cases) use RFC 2119 keywords:

  • MUST — absolute requirement
  • SHOULD — strong recommendation
  • MAY — optional behavior
  • MUST NOT — absolute prohibition

Compiling to a target

Available targets

Target Language Framework File
go Go Bubbletea + Lipgloss targets/go.md
node TypeScript Ink + React (Node.js) targets/node.md
bun TypeScript OpenTUI + React (Bun) targets/bun.md
rust Rust Ratatui + Crossterm targets/rust.md

Workflow

# 1. See what's changed
bun run compile status

# 2. Generate the compilation prompt
bun run compile prompt --target go

# 3. Feed dist/go/_compile-prompt.md to an LLM agent
#    The agent generates code into dist/go/

# 4. Verify: run tests, check the demo CLI
cd dist/go && go test ./... && go run ./cmd/demo

# 5. Lock the hashes
bun run compile lock --target go

Multi-pass compilation

A single compilation pass across the full component suite (17 components + tokens + demo) is usually not enough to reach production quality. We've found that 2–3 passes produce notably better results:

Pass Focus Typical outcome
1st Initial generation All components scaffold correctly, most tests pass, demo wires up. Expect rough edges — missing edge cases, incomplete keybindings, demo wiring bugs.
2nd Review & fix Agent reviews its own output against specs, fixes test failures, fills in missing behavior, improves demo interactivity. Test count typically grows 30–50%.
3rd Polish Catches subtle spec violations, improves accessibility, hardens demo --snapshot smoke tests. Diminishing returns after this point.

To run a follow-up pass, generate a new prompt and tell the agent to review and complete its existing work:

# Generate a fresh prompt (it sees the current dist/ state)
bun run compile prompt --target go

# Feed to the agent with instructions like:
# "Review your existing implementation against the specs.
#  Fix any test failures, fill in missing behavior,
#  and ensure all --snapshot smoke tests pass."

Each pass is fast because the agent builds on its own prior output rather than starting from scratch. The demo's --list and --snapshot flags make it easy for the agent to self-verify between passes.

Custom output directory

By default, compiled code goes to dist/. Override with --out:

# Output to a separate repo or directory
bun run compile prompt --target go --out ~/my-tuikit-go

# The prompt and generated code go to ~/my-tuikit-go/go/

Adding a new target

  1. Create targets/{name}.md following the target spec format in docs/schema.md
  2. Define: architecture pattern, type mapping, callback translation, state machine pattern, token access, styling, composition, test pattern, key mapping, dependencies, and demo CLI
  3. Run bun run compile status — your target will show up with all specs dirty
  4. Run bun run compile prompt --target {name} and compile

Linting

# Lint all specs
bun run lint

# Lint a single component
bun run lint --component Select

# Show fix suggestions
bun run lint --fix

# See all rules
bun run lint --help

The linter checks:

  • Required frontmatter fields and valid values (zod schemas)
  • Naming conventions (PascalCase components, camelCase props)
  • RFC 2119 keyword usage in normative sections
  • ARIA accessibility structure for interactive components
  • Token cross-references resolve to known tokens
  • Required body sections (Visual rules, Rendering example, Dependencies)
  • Test specs reference existing components
  • Broken internal markdown links

Rule definitions live in scripts/lint-rules.ts — edit that file to add or change rules, severities, and fix hints.

CI checks

The GitHub Actions workflow (.github/workflows/specs-ci.yml) runs on every PR:

  1. Spec lintbun run lint
  2. Compiler healthbun run compile status for each target
  3. Prompt smoke testbun run compile prompt for each target
  4. No generated output committed — ensures dist/ is not tracked
  5. Changed-spec completeness — if {Name}.md changes, matching .test.md and .preview.md must also change

Design principles

  • Specs capture intent, not implementation — ~95% behavioral intent vs. ~5% framework hints. This lets agents generate idiomatic code per framework rather than awkward transliterations.

  • Color tokens define meaning, not color values — tokens like textPrimary and selected define UI roles. The color engine (Rampa, hardcoded hex, ANSI palette) is an implementation detail per target.

  • Layout is out of scope — specs define behavior and semantic tokens. Spacing, padding, and spatial polish are per-target decisions (similar to headless UI libraries like Radix or Base UI).

  • Lock files enable incremental compilation — only dirty specs trigger regeneration. Schema changes invalidate everything. Lock files are gitignored; a fresh clone starts with everything dirty.

For TUI design foundations — color systems, typography, iconography, layout grids, accessibility patterns, keybinding conventions, and buffer management — see docs/foundations.md.

License

This project is licensed under the MIT License.

About

A spec-driven system for building TUI components across programming languages

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors