MATERIAL DESIGN 3 · LOCAL CLI · 19 REGISTERED TARGETS

One color system. Across your toolchain.

Theme Manager turns a seed color or included theme definition into coordinated light and dark files for terminals, editors, shell tools, Git UIs, and developer agents—then applies them through the same adapter contract.

tman generate -c "#6750A4" -V "Tonal spot"
material / darkgenerated
#6750A4Tonal spot
primary#CFBDFE
secondary#CBC2DB
tertiary#EFB8C8
error#FFB4AB
surface#141218
onSurface#E6E0E9

$ tman generate -c "#6750A4" -V "Tonal spot"

Generating color scheme (Tonal spot)

Writing Neovim colorschemes

Running registered generators

Neovim engine + registered adapters
Ghosttymaterial_dark
Neovimmaterial_dark.lua
VS Codematerial.vsix
Lazygitmaterial_dark_lazygit_theme.yml
Yazimaterial_dark.toml
Oh My Pimaterial_dark.json

One palette, 19 registered targets.

Generate and apply support varies by adapter.

NeovimGhosttyVS CodeJetBrainsZellijLazygitYaziOh My PiTermux

ONE PIPELINE · REAL OUTPUT

Watch one theme become an entire workspace.

Theme Manager resolves one semantic light/dark model, writes Neovim colorschemes, then runs each registered generator independently. One adapter failure does not stop the others.

tman generate -c "#6750A4" -V "Tonal spot"
  1. Generating color scheme (Tonal spot)done
  2. Writing Neovim colorschemesdone
  3. Ghosttymaterial_dark
  4. Alacrittymaterial_dark.toml
  5. Oh My Pimaterial_dark.json
  6. Updating configurationdone
19/ registered targets
9/ MD3 variants
168/ included definitions
2/ light + dark modes

Capability truth: 18 targets generate; 18 targets apply.

The hard part isn’t choosing colors. It’s keeping every format coherent.

01

Formats diverge

TOML, YAML, KDL, Lua, JSON, properties, packaged extensions—one semantic palette has to survive every syntax.

02

Paths drift

The same adapter defines generate write and select read paths, enforced by contract tests.

03

Switching has side effects

Pointers, reload hooks, installed-package discovery, and environment gates belong to each target.

Generate as a system. Apply as a lifecycle.

2040608095
.lua.toml.json.jar
01 / Semantic color

One model, both modes.

Start with a seed or a repository theme. Material roles expand into light and dark surfaces, accents, context colors, and ANSI palettes.

02 / Tool adapters

One owner per target.

Each adapter owns its generated filename, destination, environment gate, and reload behavior. Generate and select cannot silently disagree about the source path.

Four ways to work with the same core.

GENERATE

From seed color to tool-specific files.

Choose a seed color and one of the available Material variants, or load an included definition. Theme Manager resolves light and dark semantic colors, writes Neovim, then lets each generator format its own output.

Read the contract
Content · Expressive · Fidelity · Fruit salad · Monochrome · Neutral · Rainbow · Tonal spot · Vibrant
PREVIEW

Inspect color before you write it.

Start the local web UI to inspect a generated light/dark scheme without writing theme files. The interactive selector also renders a Ghostty-palette preview before you confirm a mode.

Read the contract
/api/preview → light + dark
APPLY

Select a light or dark theme. Let each adapter do the rest.

Select a theme and mode once. Applicable adapters copy or replace their live target, create target-specific extras, and run reload hooks sequentially; skipped and failed adapters do not erase successful ones.

Read the contract
select → 18 applicable adapters
EXTEND

Add one adapter, not another parallel workflow.

A ThemeGenerator owns the generation path and the application contract. Add the adapter once to ALL_ADAPTERS; derived flow lists and contract tests enforce its declared capabilities.

Read the contract
ThemeGenerator → ALL_ADAPTERS → tests

Choose. Preview. Generate. Apply.

  1. 01Seed hex or included theme

    Choose

    tman -c "#6750A4" -V "Tonal spot"

    Pass a hex seed and variant, pick a random, favorite, or last color, or name an included theme.

  2. 02Optional local, non-writing scheme preview

    Preview

    tman serve → Preview

    Open tman serve and request Preview. The route builds both schemes in memory and does not write theme files.

  3. 03Light/dark Neovim plus adapter outputs

    Generate

    tman generate -c "#6750A4" -V "Tonal spot"

    Write both modes for Neovim and every generation-capable adapter. Adapter failures are isolated.

  4. 04One mode across applicable adapters

    Apply

    tman select --theme material --dark

    Select a theme and mode. Each applicable adapter updates its live target and hook; VS Code remains package-only.

Generated files, not mockups.

Complete dark-mode output from three generation-capable adapters. Each excerpt is produced in memory by the same formatter used by the CLI.

Ghosttymaterial_dark
Generated excerpt
# Generated Material dark Theme for Ghostty
# Generated by theme-generator
# Format: key = RRGGBB (hex colors without #)
# Place this file in ~/.config/ghostty/themes/

# Basic colors
background = 141218
foreground = e6e0e9
cursor-color = cfbdfe
selection-background = 49454e
# Selection foreground usually contrasts with selection-background
selection-foreground = e6e0e9

# Color palette (ANSI Colors)
# Normal colors (0-7)
palette = 0=948f99
palette = 1=ffb4ab
palette = 2=6fe063
palette = 3=ffee5f
palette = 4=cfbdfe
palette = 5=efb8c8
palette = 6=cbc2db
palette = 7=e6e0e9

# Bright colors (8-15)
palette = 8=aea6b6
palette = 9=ffcbc0
palette = 10=84fb76
palette = 11=ffff70
palette = 12=ead5ff
palette = 13=ffcee2
palette = 14=e6dafa
palette = 15=fff8ff

# Optional: Define extended colors (16-255) if needed by specific applications
# palette = 16=RRGGBB # Example: map tertiaryContainer if available
# palette = 17=RRGGBB # Example: map secondaryContainer if available
# ...
Alacrittymaterial_dark.toml
Generated excerpt
# -----------------------------------------------------------------------------
# Material Theme (dark)
# Generated by theme-generator for Alacritty
# -----------------------------------------------------------------------------

# Import this file in your main alacritty.toml:
# import = ["~/.config/alacritty/material_themes/material_dark.toml"] # Adjust path as needed
[window]
blur = false
opacity = 1

[colors.primary]
background = '#141218'
foreground = '#e6e0e9'

# Dim/Bright foreground variants (optional, uncomment if needed)
# dim_foreground = '...'
# bright_foreground = '...'

[colors.cursor]
cursor = '#cfbdfe'
text = '#141218' # Use background color for cursor text for contrast

[colors.vi_mode_cursor] # Optional: Same styling for Vi mode cursor
cursor = '#cfbdfe'
text = '#141218'

[colors.selection]
background = '#4d3d75' # Use primaryContainer or fallback
text = '#e9ddff' # Use onPrimaryContainer or fallback

# Normal colors (ANSI colors 0-7)
[colors.normal]
black   = '#e6e0e9'
red     = '#ffb4ab'
green   = '#6fe063'
yellow  = '#ffee5f'
blue    = '#cfbdfe'
magenta = '#efb8c8'
cyan    = '#cbc2db'
white   = '#e6e0e9'

# Bright colors (ANSI colors 8-15)
[colors.bright]
black   = '#e6e0e9'
red     = '#ffcbc0'
green   = '#84fb76'
yellow  = '#ffff70'
blue    = '#ead5ff'
magenta = '#ffcee2'
cyan    = '#e6dafa'
white   = '#e6e0e9'

# Dim colors (optional, uncomment and define if needed)
# [colors.dim]
# black   = '...'
# ...
# white   = '...'

# Indexed Colors (Example: using warmAccent and error)
# Allows defining colors beyond the standard 16 ANSI colors.
[[colors.indexed_colors]]
index = 16
color = '#f5d2ff'

[[colors.indexed_colors]]
index = 17
color = '#ffb4ab'

# You can add more indexed colors (up to 255) if needed by applications.
Oh My Pimaterial_dark.json
Generated excerpt
{
  "name": "material dark",
  "colors": {
    "accent": "#cfbdfe",
    "border": "#948f99",
    "borderAccent": "#cfbdfe",
    "borderMuted": "#49454e",
    "success": "#6fe063",
    "error": "#ffb4ab",
    "warning": "#ffee5f",
    "muted": "#cac4cf",
    "dim": "#948f99",
    "text": "#e6e0e9",
    "thinkingText": "#cac4cf",
    "selectedBg": "#4d3d75",
    "userMessageBg": "#211f25",
    "customMessageBg": "#2b292f",
    "toolPendingBg": "#211f25",
    "toolSuccessBg": "#2b292f",
    "toolErrorBg": "#93000a",
    "statusLineBg": "#1d1b21",
    "userMessageText": "#e6e0e9",
    "customMessageText": "#e6e0e9",
    "customMessageLabel": "#cfbdfe",
    "toolTitle": "#e6e0e9",
    "toolOutput": "#cac4cf",
    "mdHeading": "#cfbdfe",
    "mdLink": "#cbc2db",
    "mdLinkUrl": "#cac4cf",
    "mdCode": "#efb8c8",
    "mdCodeBlock": "#e6e0e9",
    "mdCodeBlockBorder": "#49454e",
    "mdQuote": "#cac4cf",
    "mdQuoteBorder": "#948f99",
    "mdHr": "#49454e",
    "mdListBullet": "#cfbdfe",
    "toolDiffAdded": "#6fe063",
    "toolDiffRemoved": "#ffb4ab",
    "toolDiffContext": "#cac4cf",
    "syntaxComment": "#948f99",
    "syntaxKeyword": "#cbc2db",
    "syntaxFunction": "#cfbdfe",
    "syntaxVariable": "#e6e0e9",
    "syntaxString": "#efb8c8",
    "syntaxNumber": "#633b48",
    "syntaxType": "#cbc2db",
    "syntaxOperator": "#cfbdfe",
    "syntaxPunctuation": "#e6e0e9",
    "thinkingOff": "#948f99",
    "thinkingMinimal": "#49454e",
    "thinkingLow": "#cfbdfe",
    "thinkingMedium": "#cbc2db",
    "thinkingHigh": "#efb8c8",
    "thinkingXhigh": "#ffb4ab",
    "bashMode": "#cbc2db",
    "pythonMode": "#efb8c8",
    "statusLineSep": "#948f99",
    "statusLineModel": "#efb8c8",
    "statusLinePath": "#cfbdfe",
    "statusLineGitClean": "#6fe063",
    "statusLineGitDirty": "#ffee5f",
    "statusLineContext": "#cbc2db",
    "statusLineSpend": "#cfbdfe",
    "statusLineStaged": "#6fe063",
    "statusLineDirty": "#ffee5f",
    "statusLineUntracked": "#ffb4ab",
    "statusLineOutput": "#e6e0e9",
    "statusLineCost": "#633b48",
    "statusLineSubagents": "#cbc2db"
  }
}

Your theme should not stop at your editor.

Capability labels and counts come from the same canonical registry used by generate and select. Categories are an editorial grouping for browsing.

Lazygit

Git & diff
material_dark_lazygit_theme.yml.yml
GenerateApply
Any supported environmentView adapter source

Zellij

Multiplexers & layouts
material_dark.kdl.kdl
GenerateApply
Any supported environmentView adapter source

Termux

Terminal emulators
material_dark.properties.properties
GenerateApply

Ghostty

Terminal emulators
material_darkpackage
GenerateApply
Outside TermuxView adapter source

Eza

Shell & search
material_dark.yml.yml
GenerateApply
Any supported environment

Emits reference YAML; convert it to EZA_COLORS before use.

View adapter source

Zellij plugins

Multiplexers & layouts
material_dark.kdl.kdl
GenerateApply
Any supported environmentView adapter source

Delta

Git & diff
material_dark.gitconfig.gitconfig
GenerateApply
Any supported environmentView adapter source

Yazi

File managers
material_dark.toml.toml
GenerateApply
Any supported environmentView adapter source

FZF

Shell & search
material_dark.sh.sh
GenerateApply
Any supported environmentView adapter source

Alacritty

Terminal emulators
material_dark.toml.toml
GenerateApply
Outside TermuxView adapter source

Rio

Terminal emulators
material_dark.toml.toml
GenerateApply
Outside TermuxView adapter source

JetBrains

Editors & IDEs
material.jar.jar
GenerateApply
Outside Termux

Packages both modes in one JAR and discovers installed IDEs when applying.

View adapter source

WezTerm

Terminal emulators
material_dark.toml.toml
GenerateApply
Outside TermuxView adapter source

MiXplorer

File managers
material_dark.xml.xml
GenerateApply
Termux only

Termux-only; apply uses a privileged, non-atomic su copy and preserves or assigns file permissions.

View adapter source

Neovim

Editors & IDEs
material_dark.lua.lua
Apply
Any supported environment

The registry adapter applies current.lua; the separate highlight engine generates its colorschemes.

View adapter source

Kitty

Terminal emulators
material_dark.conf.conf
GenerateApply
Outside TermuxView adapter source

VS Code

Editors & IDEs
material.vsix.vsix
Generate
Any supported environment

Generate-only VSIX package; select does not live-apply it.

View adapter source

OpenCode

Developer UIs & agents
material_dark.json.json
GenerateApply
Outside Termux

Select validates and atomically replaces tman.json, then signals interactive TUI processes with SIGUSR2.

View adapter source

Oh My Pi

Developer UIs & agents
material_dark.json.json
GenerateApply
Outside Termux

Select validates and atomically replaces tman.json, then touches it for the built-in file watcher.

View adapter source

Not a collection of ports. An adapter system.

Theme source, semantic colors, output writer, generated file, live destination—each layer has one job. The registry orders the lifecycle; contract tests keep generate and select aligned.

01Theme source
02Semantic roles
03Tool adapter
04Generated file
05Live destination

Themes should be systems, not screenshots.

A workspace looks coherent only when its tools agree on more than one hex value. Theme Manager gives shared roles to surfaces, text, accents, context, and terminal ANSI colors, then translates those roles into each application’s format.

The same adapter contract owns generation and application, so maintaining support means changing one lifecycle instead of reconciling parallel lists.

The CLI, local web UI, TUI, source, and adapter registry stay inspectable. When your setup needs another target, the extension point is code—not another pile of copied themes.

Before you write the first file.

What can I generate from?

Start with a hex seed and one of 9 Material Design 3 variants, or choose one of 168 included theme definitions. Both paths resolve to the same light/dark semantic color model.

Which tools are supported?

19 targets are registered. 18 participate in generate, 18 participate in select, and 17 do both. VS Code is generate-only; Neovim’s registry adapter is apply-only because its colorschemes come from the separate highlight engine.

Does Theme Manager modify existing configuration?

Yes. Generate writes managed theme files and updates ~/.tg.json. Select copies or replaces individual live targets and may run reload hooks. Theme Manager uses atomic writes for managed files and many adapter destinations; MiXplorer’s privileged su copy is an explicit non-atomic exception. Successful targets are not rolled back when another adapter fails, and the current CLI has no backup or dry-run file-diff flag.

Can I preview without writing theme files?

Yes: start tman serve and use Preview. Its preview route builds the light and dark schemes in memory. The terminal selector also shows a Ghostty-palette preview, but confirming a selection applies it. Generate and Select are write operations.

Can I target only one application?

Not with the current CLI. Generate visits every generation-capable target; Select visits every apply-capable target and lets each adapter skip environments where it does not apply.

How do light and dark modes work?

Generation writes both modes. Select applies the mode you choose, and Theme Manager remembers the most recent light and dark selections independently.

How do I add another tool?

Implement the ThemeGenerator or ThemeAdapter contract, add one instance to ALL_ADAPTERS, and extend the path, registry, output, and hook tests described in the integration architecture guide.

What does the CLI require?

Node.js 22 or newer and the source-install steps below. The --pick flow also requires pastel. Adapters gate Termux-only and outside-Termux targets at apply time; consult the integration guide for each tool’s configuration and reload behavior.

Build the palette once. Keep the toolchain coherent.

Clone the repository, install with pnpm, and generate the default Material theme—or read the workflow first.

git clone https://github.com/Ajaymamtora/theme-manager.git theme-manager cd theme-manager pnpm install pnpm add --global .