Chromasync
A Rust CLI that generates consistent theme files for desktop apps and editors from a seed color or wallpaper image.
Seed/Wallpaper → OKLCH Palette → Template Rules → Theme Files
Install
cargo install --locked --path crates/chromasync-cli
Or build from source:
cargo build --release -p chromasync-cli
Usage
# Generate from a seed color
chromasync generate --seed "#ff6b6b" --template brutalist --mode dark \
--targets kitty,alacritty,examples/targets/gtk.toml
# Generate from a wallpaper
chromasync wallpaper --image wallpaper.png --template materialish --mode light \
--targets kitty,examples/targets/css.toml
# Preview palette and tokens without writing files
chromasync preview --seed "#4ecdc4" --template minimal --mode light
# Export tokens as JSON
chromasync tokens --seed "#7c3aed" --template terminal --mode dark --format json
# Batch multiple jobs from a manifest
chromasync batch --file jobs.toml
Output is written to ./chromasync by default.
Built-in Templates & Targets
| Templates | Targets |
|---|---|
minimal, brutalist, terminal, materialish | kitty, alacritty |
Additional targets (GTK, Hyprland, CSS, Waybar, Foot, Ghostty, Editor) are available as declarative TOML specs under examples/targets/. Custom targets can be added to ~/.config/chromasync/targets/.
chromasync templates # list available templates
chromasync targets # list available targets
Documentation
An mdBook is included under book/:
mdbook serve --open # preview locally
To regenerate book source from CLI metadata:
cargo run -p chromasync-docs -- generate
Development
cargo fmt --all --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace
cargo run -p chromasync-docs -- generate --check
Contributing
Pull requests from first-time or otherwise unvouched contributors are automatically closed until a maintainer vouches for the author. This is in place to reduce spammy or low-signal PRs.
If you want to contribute and are not yet vouched, open an issue describing the change you want to make or the area you want to work on. A maintainer can then comment vouch, vouch @user, lgtm, or lgtm @user on the issue or PR to add you to the trusted contributor list.
Maintainers can also comment unvouch or denounce to remove trust or explicitly block an account when needed. The trust list lives in .github/VOUCHED.td.
See the Packaging guide for release and packaging details.
Generate
The generate command creates theme files from a seed color. You provide a hex color, a template, and one or more targets — Chromasync resolves a full color palette, maps it to semantic tokens through the template, and writes output files for each target.
Basic usage
chromasync generate \
--seed "#ff6b6b" \
--template brutalist \
--targets kitty
This writes kitty.conf into the default output directory (./chromasync/).
Options
| Flag | Required | Default | Description |
|---|---|---|---|
--seed | yes | — | Seed color in #RRGGBB format |
--template | yes | — | Template name or path to a .toml file |
--targets | yes | — | Comma-separated list of target names or .toml paths |
--mode | no | dark | Theme mode: dark or light |
--contrast | no | relative-luminance | Contrast strategy: relative-luminance or apca-experimental |
--output | no | chromasync | Directory to write artifacts into |
Choosing a template
Pass a built-in template by name:
--template minimal
Or point to a custom template file:
--template ./my-templates/warm-dark.toml
If the value contains /, ., or ends with .toml, it is treated as a file path. Otherwise it is looked up by name from built-in templates and installed packs.
List available templates with:
chromasync templates
Choosing targets
Targets can be built-in names (kitty, alacritty), paths to declarative target TOML files, or targets provided by packs. Mix them freely in a comma-separated list:
--targets kitty,alacritty,examples/targets/gtk.toml,examples/targets/css.toml
Declarative example targets for GTK, Hyprland, CSS, Waybar, Foot, Ghostty, and Editor ship under examples/targets/.
List available targets with:
chromasync targets
Output
Artifacts are written to the output directory, which is created if it does not exist. On success, each written file path is printed to stdout:
chromasync/kitty.conf
chromasync/gtk.css
chromasync/theme.css
Overwrite protection
Generate refuses to overwrite existing files. If an artifact already exists at the destination, the command fails before writing anything. Delete or move the existing output directory first, or use a different --output path.
Collision detection
If two targets would produce the same output file name, the command fails before writing anything.
Contrast strategies
The --contrast flag controls how Chromasync picks foreground colors that are readable against their backgrounds.
relative-luminance(default) — WCAG 2.0 luminance contrast ratio, targeting a minimum of 4.5:1.apca-experimental— APCA (Advanced Perceptual Contrast Algorithm) for more perceptually uniform results. This is experimental and may change.
Examples
Generate a dark theme for multiple targets:
chromasync generate \
--seed "#4ecdc4" \
--template minimal \
--mode dark \
--targets kitty,alacritty,examples/targets/gtk.toml,examples/targets/hyprland.toml
Generate a light theme with APCA contrast into a custom directory:
chromasync generate \
--seed "#7c3aed" \
--template materialish \
--mode light \
--contrast apca-experimental \
--targets examples/targets/editor.toml \
--output ./my-theme
How it works
- The seed color is parsed and converted to OKLCH color space.
- Nine palette families are derived (primary, secondary, tertiary, neutral, neutral-variant, error, success, warning, info), each with 16 tone samples spanning black to white.
- The template maps each of 17 semantic tokens (like
bg,accent,text) to a palette family and tone. - Each target substitutes the resolved token hex values into its output template and produces one or more artifact files.
Generation is deterministic — the same seed, template, and mode always produce identical output.
Wallpaper
The wallpaper command creates theme files from a wallpaper image. Chromasync extracts up to three dominant colors from the image, builds a multi-seed palette where each seed drives a separate palette family, maps it to semantic tokens through the template, and writes output files for each target.
Basic usage
chromasync wallpaper \
--image ~/wallpapers/mountain.png \
--template brutalist \
--targets kitty
This writes kitty.conf into the default output directory (./chromasync/).
Options
| Flag | Required | Default | Description |
|---|---|---|---|
--image | yes | — | Path to a wallpaper image file |
--template | yes | — | Template name or path to a .toml file |
--targets | yes | — | Comma-separated list of target names or .toml paths |
--mode | no | dark | Theme mode: dark or light |
--contrast | no | relative-luminance | Contrast strategy: relative-luminance or apca-experimental |
--output | no | chromasync | Directory to write artifacts into |
Choosing a template
See Choosing a template in the generate guide. The --template flag works identically.
Choosing targets
See Choosing targets in the generate guide. The --targets flag works identically.
Output
Output behavior is identical to generate — see Output for details on overwrite protection and collision detection.
Color extraction
Instead of a single --seed color, the wallpaper command extracts colors from the image and uses them to build a richer palette.
Multi-seed palette construction
Extraction returns up to three dominant colors ranked by pixel count. Each seed drives a different palette family:
- Seed 0 (most dominant) → primary family — also used as the base seed for all derived families (neutral, neutral-variant, error, success, warning, info).
- Seed 1 (second most dominant) → secondary family, replacing the secondary family that would otherwise be derived from seed 0.
- Seed 2 (third most dominant) → tertiary family, replacing the derived tertiary family.
If the image yields fewer than three seeds, the missing families remain derived from the primary seed, exactly as in generate.
Region labeling
Each extracted seed is labeled with its average spatial position in the image using a 3x3 grid: top-left, top-center, top-right, center-left, center, center-right, bottom-left, bottom-center, bottom-right.
Noisy image fallback
If no single color bucket accounts for at least 10% of visible pixels, the image is considered noisy. In this case, extraction returns a single seed computed as the average color of all visible pixels rather than attempting to separate clusters.
Contrast strategies
See Contrast strategies in the generate guide. The --contrast flag works identically.
Examples
Generate a dark theme for Kitty from a wallpaper:
chromasync wallpaper \
--image ~/wallpapers/forest.jpg \
--template minimal \
--targets kitty
Generate a light theme for multiple targets:
chromasync wallpaper \
--image ~/wallpapers/sunset.png \
--template materialish \
--mode light \
--targets kitty,alacritty,examples/targets/gtk.toml,examples/targets/hyprland.toml
Generate with APCA contrast into a custom directory:
chromasync wallpaper \
--image ~/wallpapers/abstract.png \
--template brutalist \
--contrast apca-experimental \
--targets examples/targets/editor.toml \
--output ./my-theme
How it works
- The image is loaded and downscaled to fit within 128x128 pixels (preserving aspect ratio) using triangle filtering.
- Each pixel is quantized to 4-bit color (shifting RGB channels right by 4 bits), grouping similar colors into buckets. Pixels with alpha below 16 are skipped.
- Buckets are sorted by pixel count. If the largest bucket holds less than 10% of visible pixels, the image is treated as noisy and a single average-color seed is returned.
- Otherwise, up to three seeds are taken from the largest buckets, each with a dominance score and a region label derived from the bucket’s average pixel position.
- The primary seed generates the full nine-family OKLCH palette. If a second or third seed exists, it replaces the secondary or tertiary family respectively.
- The template maps semantic tokens to the palette, and each target renders its output files — identical to the final steps of
generate.
Batch
The batch command runs multiple generation jobs from a single TOML manifest. Each job can be seed-based or wallpaper-based with its own template, mode, targets, and output directory. Jobs are processed sequentially in manifest order.
Basic usage
chromasync batch --file themes.toml
Where themes.toml is a manifest defining one or more jobs.
Options
| Flag | Required | Default | Description |
|---|---|---|---|
--file | yes | — | Path to a TOML batch manifest |
Manifest format
A batch manifest is a TOML file containing a [[jobs]] array. Each entry defines one generation job.
[[jobs]]
name = "dark-terminal"
seed = "#4ecdc4"
template = "minimal"
mode = "dark"
targets = ["kitty", "alacritty"]
output = "dark-terminal"
[[jobs]]
name = "light-editor"
seed = "#7c3aed"
template = "materialish"
mode = "light"
contrast = "apca-experimental"
targets = ["targets/editor.toml"]
output = "light-editor"
The singular [[job]] form is also accepted as an alias.
Job fields
| Field | Required | Default | Description |
|---|---|---|---|
name | no | <unnamed> | Label used in error messages |
seed | conditionally | — | Seed color in #RRGGBB format |
image | conditionally | — | Path to a wallpaper image |
template | yes | — | Template name or path to a .toml file |
mode | no | dark | Theme mode: dark or light |
contrast | no | relative-luminance | Contrast strategy: relative-luminance or apca-experimental |
targets | no | [] | List of target names or .toml paths |
output | yes | — | Output directory for this job’s artifacts |
Each job must define exactly one of seed or image. Defining both or neither is an error.
Path resolution
All relative paths in a manifest — image, template (when it looks like a file path), and targets entries — are resolved relative to the manifest file’s parent directory, not the working directory. This makes manifests portable: you can keep a manifest alongside its target specs and wallpapers and run it from any directory.
A value is treated as a file path if it contains a path separator (/), starts with an absolute path prefix, or ends with .toml. Otherwise it is looked up as a built-in name.
# Given a manifest at ~/themes/batch.toml:
[[jobs]]
image = "wallpapers/forest.jpg" # resolves to ~/themes/wallpapers/forest.jpg
template = "minimal" # looked up as a built-in template name
targets = ["kitty", "targets/gtk.toml"] # kitty = built-in, targets/gtk.toml = ~/themes/targets/gtk.toml
output = "forest-output"
Mixing seed and wallpaper jobs
A manifest can freely mix seed-based and wallpaper-based jobs. Seed jobs behave identically to generate and wallpaper jobs behave identically to wallpaper — see those guides for details on palette construction and color extraction.
[[jobs]]
name = "seed-job"
seed = "#4ecdc4"
template = "minimal"
targets = ["kitty", "alacritty"]
output = "seed-output"
[[jobs]]
name = "wallpaper-job"
image = "wallpaper.png"
template = "terminal"
contrast = "apca-experimental"
targets = ["targets/waybar.toml", "targets/foot.toml"]
output = "wallpaper-output"
Output
Each job writes its artifacts to its own output directory. On success, every written file path is printed to stdout. Output behavior per job is identical to generate — see Output for overwrite protection and collision detection.
Error handling
Batch execution is fail-fast: if any job fails, the remaining jobs are skipped. Errors include context about which job failed:
batch job 3 failed for output 'dark-theme'— wraps the underlying generation error with the 1-indexed job number and its output directory.batch job 'my-job' must define exactly one of 'seed' or 'image'— validation error when a job defines both or neither source.batch manifest 'path.toml' does not define any jobs— the manifest parsed successfully but thejobsarray is empty.
Examples
A single manifest generating dark and light variants of the same seed:
[[jobs]]
name = "dark"
seed = "#ff6b6b"
template = "brutalist"
mode = "dark"
targets = ["kitty", "alacritty", "targets/gtk.toml"]
output = "coral-dark"
[[jobs]]
name = "light"
seed = "#ff6b6b"
template = "brutalist"
mode = "light"
targets = ["kitty", "alacritty", "targets/gtk.toml"]
output = "coral-light"
A manifest generating themes from multiple wallpapers:
[[jobs]]
name = "forest"
image = "wallpapers/forest.jpg"
template = "minimal"
targets = ["kitty", "targets/hyprland.toml", "targets/waybar.toml"]
output = "forest-theme"
[[jobs]]
name = "sunset"
image = "wallpapers/sunset.png"
template = "minimal"
mode = "light"
targets = ["kitty", "targets/hyprland.toml", "targets/waybar.toml"]
output = "sunset-theme"
[[jobs]]
name = "abstract"
image = "wallpapers/abstract.png"
template = "materialish"
contrast = "apca-experimental"
targets = ["targets/editor.toml", "targets/css.toml"]
output = "abstract-theme"
Run either manifest:
chromasync batch --file themes.toml
Templates
Templates are TOML files that map 17 semantic tokens to palette family and tone rules. When you run generate or wallpaper, the template controls which colors from the resolved palette end up as your background, text, accent, borders, and status colors. The same seed with different templates produces different-looking themes.
Listing templates
chromasync templates
This prints all discovered templates with their name, mode, source type, and file location.
Built-in templates
Chromasync ships with four templates, each available in dark and light modes:
| Name | Description |
|---|---|
minimal | Restrained theme with subdued surfaces and a single clean accent |
materialish | Softer system-style theme with layered surfaces and calmer accents |
terminal | Terminal-oriented theme with deep backgrounds and crisp signal colors |
brutalist | High-contrast theme with louder borders and harder accent separation |
Use them by name:
chromasync generate --seed "#4ecdc4" --template minimal --targets kitty
Template sources
Templates are discovered from multiple locations. When multiple sources provide the same name and mode, the highest-precedence source wins:
| Precedence | Source | Location |
|---|---|---|
| 0 (lowest) | Built-in | Embedded in the binary |
| 1 | Pack | ~/.local/share/chromasync/packs/*/templates/ |
| 2 | User config | ~/.config/chromasync/templates/ |
| 3 (highest) | Filesystem path | Direct path passed to --template |
When loading by name, Chromasync prefers the variant matching the requested --mode. If no exact mode match exists, it falls back to the other mode from the highest-precedence source.
If the --template value contains /, starts with an absolute path, or ends with .toml, it is loaded as a file path (precedence 3). Otherwise it is looked up by name.
Writing a custom template
A template is a TOML file with three top-level fields and exactly 17 token sections:
name = "my-theme"
mode = "dark"
description = "Optional description of what this template looks like."
[tokens.bg]
family = "neutral"
tone = 0.10
chroma = 0.008
[tokens.accent]
family = "primary"
tone = 0.68
chroma_scale = 0.95
# ... remaining 15 tokens
Top-level fields
| Field | Required | Description |
|---|---|---|
name | yes | Template name used for lookup |
mode | yes | dark or light |
description | no | Short description shown by chromasync templates |
Token rule fields
Each [tokens.<name>] section defines how one semantic token is resolved from the palette:
| Field | Required | Default | Description |
|---|---|---|---|
family | yes | — | Palette family to sample from |
tone | yes | — | Lightness level, 0.0 (black) to 1.0 (white) |
chroma | no | family base chroma | Absolute chroma override (must be ≥ 0.0) |
chroma_scale | no | 1.0 | Multiplier applied to chroma (must be ≥ 0.0) |
The final chroma for a token is:
final_chroma = (chroma OR family.base_chroma) * chroma_scale
All 17 tokens must be defined. Omitting any token is a validation error.
Semantic tokens
Every template must define all 17 tokens:
| Token | Role |
|---|---|
bg | Primary background |
bg_secondary | Secondary background (side panels, alternate rows) |
surface | Interactive surface (buttons, inputs) |
surface_elevated | Elevated surface (dialogs, tooltips, floating panels) |
text | Primary text |
text_muted | Secondary/muted text |
border | Regular border |
border_strong | Emphasized border |
accent | Primary accent (buttons, highlights) |
accent_hover | Accent hover state |
accent_active | Accent active/pressed state |
accent_fg | Text on accent backgrounds |
selection | Text selection/highlight background |
link | Link color |
success | Success state |
warning | Warning state |
error | Error state |
Palette families
Nine families are available for token rules:
| Family | Description |
|---|---|
primary | Main accent, derived from the seed color |
secondary | Secondary accent, derived from the seed (or second wallpaper seed) |
tertiary | Tertiary accent, derived from the seed (or third wallpaper seed) |
neutral | Desaturated neutral tones, derived from the seed |
neutral_variant | Slightly chromatic neutral, derived from the seed |
error | Red tones |
success | Green tones |
warning | Yellow tones |
info | Blue tones |
Tone
Tone is OKLCH lightness mapped to a 0.0–1.0 range:
0.0— black1.0— white
Dark mode templates typically use low tones for backgrounds (0.04–0.15) and high tones for text (0.90–0.98). Light mode reverses this — high tones for backgrounds (0.88–0.98) and low tones for text (0.08–0.14). Accents sit in the middle range in both modes.
Chroma control
Use chroma to set an absolute saturation value, overriding the family’s base chroma. This is common for backgrounds where you want near-neutral surfaces regardless of the seed color:
[tokens.bg]
family = "neutral"
tone = 0.10
chroma = 0.008
Use chroma_scale to proportionally adjust the family’s base chroma. This preserves the seed’s character while tuning intensity:
[tokens.accent]
family = "primary"
tone = 0.68
chroma_scale = 0.95
A scale of 1.0 keeps the family chroma unchanged. Values above 1.0 boost saturation (the brutalist template uses up to 1.20 on accents). Values below 1.0 mute it.
You can combine both — chroma replaces the base, then chroma_scale multiplies the result.
Installing user templates
Drop .toml template files into:
~/.config/chromasync/templates/
All .toml files in this directory are auto-discovered. A user template with the same name and mode as a built-in template overrides it.
Contrast adjustment
After token resolution, Chromasync validates that text has sufficient contrast against bg and that accent_fg has sufficient contrast against accent. If contrast is insufficient, the resolver falls back to neutral light or dark alternatives. See Contrast strategies for details on the available algorithms.
Examples
A custom dark template using the tertiary family for links and secondary for selection:
name = "my-colorful"
mode = "dark"
description = "A more colorful theme using multiple palette families."
[tokens.bg]
family = "neutral"
tone = 0.08
chroma = 0.006
[tokens.bg_secondary]
family = "neutral"
tone = 0.12
chroma = 0.008
[tokens.surface]
family = "neutral_variant"
tone = 0.16
chroma = 0.012
[tokens.surface_elevated]
family = "neutral_variant"
tone = 0.22
chroma = 0.016
[tokens.text]
family = "neutral"
tone = 0.94
[tokens.text_muted]
family = "neutral_variant"
tone = 0.72
chroma_scale = 0.65
[tokens.border]
family = "neutral_variant"
tone = 0.30
chroma_scale = 0.60
[tokens.border_strong]
family = "primary"
tone = 0.42
chroma_scale = 0.50
[tokens.accent]
family = "primary"
tone = 0.70
chroma_scale = 1.10
[tokens.accent_hover]
family = "primary"
tone = 0.76
chroma_scale = 1.15
[tokens.accent_active]
family = "primary"
tone = 0.62
chroma_scale = 1.05
[tokens.accent_fg]
family = "neutral"
tone = 0.98
[tokens.selection]
family = "secondary"
tone = 0.32
chroma_scale = 0.70
[tokens.link]
family = "tertiary"
tone = 0.76
chroma_scale = 1.00
[tokens.success]
family = "success"
tone = 0.72
chroma_scale = 0.92
[tokens.warning]
family = "warning"
tone = 0.76
chroma_scale = 0.90
[tokens.error]
family = "error"
tone = 0.74
chroma_scale = 0.95
Use it with a file path:
chromasync generate \
--seed "#e06c75" \
--template ./my-colorful.toml \
--targets kitty,alacritty
Or install it to ~/.config/chromasync/templates/my-colorful.toml and use it by name:
chromasync generate \
--seed "#e06c75" \
--template my-colorful \
--targets kitty,alacritty
Packs
Packs are self-contained theme collections that bundle templates and target specs into a single directory. They let you distribute complete theme kits — a template that defines the color mapping alongside targets that produce output for specific apps — as a unit that others can drop into their system and use by name.
Listing packs
chromasync packs
This prints all discovered packs with their name, version, and root directory.
To inspect a specific pack’s metadata, templates, and targets:
chromasync pack info aurora
Pack directory structure
A pack is a directory containing a pack.toml manifest and one or more subdirectories of templates and/or targets:
my-pack/
├── pack.toml
├── templates/
│ ├── my-theme-dark.toml
│ └── my-theme-light.toml
└── targets/
├── ghostty.toml
└── waybar.toml
If no [templates] or [targets] sections are declared in pack.toml, Chromasync auto-discovers templates/ and targets/ subdirectories if they exist.
Manifest format
The pack.toml file declares the pack’s metadata and asset paths:
name = "my-pack"
version = "1.0.0"
description = "A complete theme kit for my setup."
author = "Your Name"
license = "MIT"
homepage = "https://example.com/my-pack"
[templates]
paths = ["templates"]
[targets]
paths = ["targets"]
Fields
| Field | Required | Description |
|---|---|---|
name | yes | Identifier matching [a-z0-9_-]+ |
version | yes | Version string (conventionally semver) |
description | no | Human-readable description |
author | no | Creator or maintainer |
license | no | License identifier (e.g. MIT, Apache-2.0) |
homepage | no | URL to project or repository |
[templates] | no | Section with paths = [...] listing template subdirectories |
[targets] | no | Section with paths = [...] listing target subdirectories |
Asset paths must be relative to the pack root. Parent directory references (..) are not allowed. A pack must contain at least one template or target directory.
Installing packs
There is no install command — drop the pack directory into one of the search locations:
| Location | Use case |
|---|---|
~/.config/chromasync/packs/ | Personal packs |
~/.local/share/chromasync/packs/ | System-distributed packs |
.chromasync/packs/ | Project-local packs (relative to working directory) |
All three locations are scanned on every run. A pack with the same name must not appear in multiple locations.
Using pack templates and targets
Pack templates and targets are referenced by name, the same way as built-in ones. You do not need to qualify them with the pack name:
chromasync generate \
--seed "#89b4fa" \
--template catppuccin \
--targets ghostty,waybar
If a pack template has the same name and mode as a built-in template, the pack version takes precedence. See the Template sources table for the full precedence order.
Writing pack templates
Pack templates use the same TOML format as any other template. See the Templates guide for the full schema. The template’s name field is what users pass to --template.
Writing pack targets
Pack targets are declarative TOML files that define output artifacts using {{tokens.<name>}} placeholders:
name = "my-app"
description = "Theme output for My App."
[[artifacts]]
file_name = "theme.conf"
template = """
foreground={{tokens.text}}
background={{tokens.bg}}
accent={{tokens.accent}}
"""
Each [[artifacts]] entry produces one output file. Placeholders are substituted with resolved hex values at generation time.
Placeholder transforms
Placeholders support transforms for different output formats:
| Syntax | Output |
|---|---|
{{tokens.bg}} | #rrggbb (default hex) |
{{tokens.bg | hex_no_hash}} | rrggbb (hex without #) |
{{tokens.bg | rgba(FF)}} | rgba(RRGGBBFF) (uppercase hex with alpha) |
Target inheritance
A target can extend another user-defined target with the extends field. The child inherits all artifacts from the base and can add new ones or override existing ones by matching file_name:
name = "my-child"
extends = "my-base"
[[artifacts]]
file_name = "extra.conf"
template = """
color={{tokens.accent}}
"""
Inheritance rules:
- The base target must be a user-defined or pack target — extending built-in targets (
kitty,alacritty) is not allowed. - Chains are supported: target A can extend B, which extends C.
- If a child artifact has the same
file_nameas a base artifact, the child’s version replaces it. - A target with
extendscan omit[[artifacts]]entirely to inherit the base unchanged under a new name.
Validation
Chromasync validates packs at discovery time:
- Pack names must match
[a-z0-9_-]+. - Duplicate pack names across search locations are an error.
- All declared asset paths must exist and be relative.
- Templates and targets within packs are validated the same way as standalone files.
Example: Catppuccin pack with Ghostty and Waybar targets
This example builds a complete pack that provides a Catppuccin-style template and targets for Ghostty and Waybar. It uses target inheritance to share a common terminal palette definition between terminal targets and a common GTK color block between GTK-based targets.
Directory layout
~/.config/chromasync/packs/catppuccin/
├── pack.toml
├── templates/
│ ├── catppuccin-dark.toml
│ └── catppuccin-light.toml
└── targets/
├── base-terminal.toml
├── ghostty.toml
├── base-gtk-colors.toml
└── waybar.toml
pack.toml
name = "catppuccin"
version = "1.0.0"
description = "Catppuccin-inspired theme with Ghostty and Waybar targets."
author = "Your Name"
license = "MIT"
[templates]
paths = ["templates"]
[targets]
paths = ["targets"]
templates/catppuccin-dark.toml
name = "catppuccin"
mode = "dark"
description = "Catppuccin-inspired dark theme with pastel accents on deep surfaces."
[tokens.bg]
family = "neutral"
tone = 0.12
chroma = 0.014
[tokens.bg_secondary]
family = "neutral"
tone = 0.15
chroma = 0.016
[tokens.surface]
family = "neutral_variant"
tone = 0.19
chroma = 0.018
[tokens.surface_elevated]
family = "neutral_variant"
tone = 0.24
chroma = 0.020
[tokens.text]
family = "neutral"
tone = 0.92
[tokens.text_muted]
family = "neutral_variant"
tone = 0.70
chroma_scale = 0.60
[tokens.border]
family = "neutral_variant"
tone = 0.30
chroma_scale = 0.50
[tokens.border_strong]
family = "neutral_variant"
tone = 0.40
chroma_scale = 0.65
[tokens.accent]
family = "primary"
tone = 0.74
chroma_scale = 0.85
[tokens.accent_hover]
family = "primary"
tone = 0.80
chroma_scale = 0.90
[tokens.accent_active]
family = "primary"
tone = 0.66
chroma_scale = 0.80
[tokens.accent_fg]
family = "neutral"
tone = 0.12
[tokens.selection]
family = "primary"
tone = 0.30
chroma_scale = 0.45
[tokens.link]
family = "info"
tone = 0.76
chroma_scale = 0.88
[tokens.success]
family = "success"
tone = 0.72
chroma_scale = 0.82
[tokens.warning]
family = "warning"
tone = 0.78
chroma_scale = 0.80
[tokens.error]
family = "error"
tone = 0.70
chroma_scale = 0.85
templates/catppuccin-light.toml
name = "catppuccin"
mode = "light"
description = "Catppuccin-inspired light theme with pastel accents on warm surfaces."
[tokens.bg]
family = "neutral"
tone = 0.95
chroma = 0.010
[tokens.bg_secondary]
family = "neutral"
tone = 0.91
chroma = 0.012
[tokens.surface]
family = "neutral_variant"
tone = 0.87
chroma = 0.014
[tokens.surface_elevated]
family = "neutral_variant"
tone = 0.82
chroma = 0.016
[tokens.text]
family = "neutral"
tone = 0.14
[tokens.text_muted]
family = "neutral_variant"
tone = 0.40
chroma_scale = 0.58
[tokens.border]
family = "neutral_variant"
tone = 0.76
chroma_scale = 0.45
[tokens.border_strong]
family = "neutral_variant"
tone = 0.64
chroma_scale = 0.60
[tokens.accent]
family = "primary"
tone = 0.50
chroma_scale = 0.82
[tokens.accent_hover]
family = "primary"
tone = 0.56
chroma_scale = 0.88
[tokens.accent_active]
family = "primary"
tone = 0.44
chroma_scale = 0.78
[tokens.accent_fg]
family = "neutral"
tone = 0.96
[tokens.selection]
family = "primary"
tone = 0.84
chroma_scale = 0.30
[tokens.link]
family = "info"
tone = 0.48
chroma_scale = 0.85
[tokens.success]
family = "success"
tone = 0.50
chroma_scale = 0.80
[tokens.warning]
family = "warning"
tone = 0.56
chroma_scale = 0.78
[tokens.error]
family = "error"
tone = 0.52
chroma_scale = 0.82
targets/base-terminal.toml
This base target defines the 16-color terminal palette mapping. Terminal targets extend it and add their app-specific settings.
name = "base-terminal"
description = "Shared 16-color terminal palette. Not useful on its own — extend it."
[[artifacts]]
file_name = "colors.txt"
template = """
color0={{tokens.bg_secondary | hex_no_hash}}
color1={{tokens.error | hex_no_hash}}
color2={{tokens.success | hex_no_hash}}
color3={{tokens.warning | hex_no_hash}}
color4={{tokens.link | hex_no_hash}}
color5={{tokens.accent | hex_no_hash}}
color6={{tokens.selection | hex_no_hash}}
color7={{tokens.text_muted | hex_no_hash}}
color8={{tokens.surface_elevated | hex_no_hash}}
color9={{tokens.error | hex_no_hash}}
color10={{tokens.success | hex_no_hash}}
color11={{tokens.warning | hex_no_hash}}
color12={{tokens.accent_hover | hex_no_hash}}
color13={{tokens.accent_active | hex_no_hash}}
color14={{tokens.border_strong | hex_no_hash}}
color15={{tokens.text | hex_no_hash}}
"""
targets/ghostty.toml
Ghostty extends the base terminal palette. It overrides the colors.txt artifact with Ghostty’s config format (using palette=N syntax) and adds Ghostty-specific keys like cursor-color and selection colors.
name = "ghostty"
description = "Ghostty terminal theme, extends base-terminal."
extends = "base-terminal"
[[artifacts]]
file_name = "colors.txt"
template = """
background={{tokens.bg | hex_no_hash}}
foreground={{tokens.text | hex_no_hash}}
cursor-color={{tokens.accent | hex_no_hash}}
selection-background={{tokens.selection | hex_no_hash}}
selection-foreground={{tokens.text | hex_no_hash}}
palette=0={{tokens.bg_secondary | hex_no_hash}}
palette=1={{tokens.error | hex_no_hash}}
palette=2={{tokens.success | hex_no_hash}}
palette=3={{tokens.warning | hex_no_hash}}
palette=4={{tokens.link | hex_no_hash}}
palette=5={{tokens.accent | hex_no_hash}}
palette=6={{tokens.selection | hex_no_hash}}
palette=7={{tokens.text_muted | hex_no_hash}}
palette=8={{tokens.surface_elevated | hex_no_hash}}
palette=9={{tokens.error | hex_no_hash}}
palette=10={{tokens.success | hex_no_hash}}
palette=11={{tokens.warning | hex_no_hash}}
palette=12={{tokens.accent_hover | hex_no_hash}}
palette=13={{tokens.accent_active | hex_no_hash}}
palette=14={{tokens.border_strong | hex_no_hash}}
palette=15={{tokens.text | hex_no_hash}}
"""
Because the child’s colors.txt artifact has the same file_name as the base’s, it replaces the base version entirely. The result is a single file with Ghostty’s format. If you later added a Foot or WezTerm target, each could extend base-terminal the same way — overriding colors.txt with its own format while keeping the palette mapping consistent.
targets/base-gtk-colors.toml
This base target defines GTK @define-color variables. GTK-based targets extend it and add their own widget styles.
name = "base-gtk-colors"
description = "Shared GTK @define-color block. Extend to add widget styles."
[[artifacts]]
file_name = "style.css"
template = """
@define-color bg {{tokens.bg}};
@define-color bg_secondary {{tokens.bg_secondary}};
@define-color surface {{tokens.surface}};
@define-color surface_elevated {{tokens.surface_elevated}};
@define-color text {{tokens.text}};
@define-color text_muted {{tokens.text_muted}};
@define-color border {{tokens.border}};
@define-color border_strong {{tokens.border_strong}};
@define-color accent {{tokens.accent}};
@define-color accent_hover {{tokens.accent_hover}};
@define-color accent_active {{tokens.accent_active}};
@define-color selection {{tokens.selection}};
@define-color success {{tokens.success}};
@define-color warning {{tokens.warning}};
@define-color error {{tokens.error}};
"""
targets/waybar.toml
Waybar extends the base GTK colors. It overrides style.css to include both the color definitions and Waybar-specific widget rules in a single file.
name = "waybar"
description = "Waybar GTK CSS theme, extends base-gtk-colors."
extends = "base-gtk-colors"
[[artifacts]]
file_name = "style.css"
template = """
@define-color bg {{tokens.bg}};
@define-color bg_secondary {{tokens.bg_secondary}};
@define-color surface {{tokens.surface}};
@define-color surface_elevated {{tokens.surface_elevated}};
@define-color text {{tokens.text}};
@define-color text_muted {{tokens.text_muted}};
@define-color border {{tokens.border}};
@define-color border_strong {{tokens.border_strong}};
@define-color accent {{tokens.accent}};
@define-color accent_hover {{tokens.accent_hover}};
@define-color accent_active {{tokens.accent_active}};
@define-color selection {{tokens.selection}};
@define-color success {{tokens.success}};
@define-color warning {{tokens.warning}};
@define-color error {{tokens.error}};
* {
border: none;
border-radius: 0;
font-family: monospace;
font-size: 13px;
min-height: 0;
}
window#waybar {
background: @bg;
color: @text;
}
tooltip {
background: @surface_elevated;
color: @text;
border: 1px solid @border_strong;
}
#workspaces {
background: @bg_secondary;
margin: 4px 6px;
padding: 0 4px;
border: 1px solid @border;
border-radius: 8px;
}
#workspaces button {
padding: 0 10px;
color: @text_muted;
background: transparent;
border-bottom: 2px solid transparent;
}
#workspaces button:hover {
background: @surface;
color: @text;
box-shadow: inset 0 -2px @accent_hover;
}
#workspaces button.active {
background: @surface;
color: @accent;
border-bottom-color: @accent;
}
#workspaces button.urgent {
background: @accent_active;
color: @text;
}
#clock,
#tray,
#cpu,
#memory,
#network,
#pulseaudio,
#battery {
margin: 4px 6px;
padding: 0 10px;
background: @surface;
color: @text;
border: 1px solid @border;
border-radius: 8px;
}
#battery.charging,
#battery.plugged {
color: @success;
border-color: @success;
}
#battery.warning:not(.charging) {
color: @warning;
border-color: @warning;
}
#battery.critical:not(.charging) {
color: @error;
border-color: @error;
background: @selection;
}
"""
Generate the theme
With the pack installed, generate a Catppuccin Mocha-style dark theme using a lavender seed:
chromasync generate \
--seed "#b4befe" \
--template catppuccin \
--mode dark \
--targets ghostty,waybar \
--output ~/.config/chromasync-themes/catppuccin-mocha
Or a Latte-style light theme with a pink seed:
chromasync generate \
--seed "#ea76cb" \
--template catppuccin \
--mode light \
--targets ghostty,waybar \
--output ~/.config/chromasync-themes/catppuccin-latte
Copy the outputs to their final locations:
cp ~/.config/chromasync-themes/catppuccin-mocha/colors.txt \
~/.config/ghostty/themes/catppuccin-mocha
cp ~/.config/chromasync-themes/catppuccin-mocha/style.css \
~/.config/waybar/style.css
Targets
Targets are declarative TOML files that define how semantic color tokens are rendered into application-specific configuration files. When you run generate or wallpaper, each requested target produces one or more output files with your resolved colors substituted into format-specific templates. The same tokens with different targets produce a GTK stylesheet, a terminal config, a Hyprland color scheme, or any other format you define.
Listing targets
chromasync targets
This prints all discovered targets with their name, source type, and file location in tab-separated columns:
alacritty built-in alacritty
kitty built-in kitty
css user-config /home/user/.config/chromasync/targets/css.toml
waybar pack catppuccin [/home/user/.config/chromasync/packs/catppuccin/targets/waybar.toml]
Built-in targets
Chromasync ships with two built-in targets compiled into the binary:
| Name | Default artifact | Description |
|---|---|---|
kitty | kitty.conf | Kitty terminal emulator theme (foreground, background, cursor, selection, borders, tabs, 16-color ANSI palette) |
alacritty | alacritty.toml | Alacritty terminal emulator theme (primary colors, cursor, selection, search, hints, 16-color ANSI palette) |
Use them by name:
chromasync generate --seed "#4ecdc4" --template minimal --targets kitty,alacritty
Built-in targets cannot be overridden or extended by user-defined targets.
Target sources
Targets are discovered from multiple locations. When multiple sources provide the same name, the highest-precedence source wins:
| Precedence | Source | Location |
|---|---|---|
| 0 (highest) | Built-in | Compiled into the binary |
| 1 | Pack | ~/.local/share/chromasync/packs/*/targets/ or other pack search locations |
| 2 | User config | ~/.config/chromasync/targets/ |
| 3 (lowest) | Filesystem path | Direct path passed to --targets |
If the --targets value contains /, starts with an absolute path, or ends with .toml, it is loaded as a file path. Otherwise it is looked up by name.
Specifying targets
Pass a comma-separated list of target names or file paths to --targets:
chromasync generate \
--seed "#89b4fa" \
--template minimal \
--targets kitty,gtk,/path/to/custom.toml
Duplicates in the list are silently deduplicated. Target names are trimmed of whitespace.
Writing a custom target
A target is a TOML file with a name, optional description, and one or more artifact definitions:
name = "my-app"
description = "Theme output for My App."
[[artifacts]]
file_name = "theme.conf"
template = """
foreground={{tokens.text}}
background={{tokens.bg}}
accent={{tokens.accent}}
"""
Each [[artifacts]] entry produces one output file. Placeholders are substituted with resolved color values at generation time.
Top-level fields
| Field | Required | Description |
|---|---|---|
name | yes | Identifier matching [a-z0-9_-]+ |
description | no | Human-readable description shown by chromasync targets |
extends | no | Name of another user-defined target to inherit from |
Target names must not collide with built-in target names (kitty, alacritty).
Artifact fields
| Field | Required | Description |
|---|---|---|
file_name | yes | Output file name (no path separators, no . or ..) |
template | yes | Template content with {{...}} placeholders |
A target must have at least one artifact unless it uses extends.
Placeholders
Placeholders use the syntax {{<value>}} or {{<value> | <transform>}}. They are replaced with resolved values at generation time.
Token values
Reference any of the 17 semantic tokens:
| Placeholder | Description |
|---|---|
{{tokens.bg}} | Primary background |
{{tokens.bg_secondary}} | Secondary background |
{{tokens.surface}} | Interactive surface |
{{tokens.surface_elevated}} | Elevated surface |
{{tokens.text}} | Primary text |
{{tokens.text_muted}} | Muted text |
{{tokens.border}} | Regular border |
{{tokens.border_strong}} | Strong border |
{{tokens.accent}} | Primary accent |
{{tokens.accent_hover}} | Accent hover state |
{{tokens.accent_active}} | Accent active state |
{{tokens.accent_fg}} | Text on accent backgrounds |
{{tokens.selection}} | Selection background |
{{tokens.link}} | Link color |
{{tokens.success}} | Success state |
{{tokens.warning}} | Warning state |
{{tokens.error}} | Error state |
Context values
Reference generation context:
| Placeholder | Description |
|---|---|
{{ctx.mode}} | Theme mode (dark or light) |
{{ctx.template_name}} | Name of the template used |
{{ctx.output_dir}} | Output directory path |
{{ctx.seed}} | Seed color (if generation used one) |
Context values do not support transforms.
Transforms
Transforms modify the output format of token values:
| Syntax | Output | Example |
|---|---|---|
{{tokens.bg}} | #rrggbb (default hex) | #1a1b26 |
{{tokens.bg | hex_no_hash}} | rrggbb (hex without #) | 1a1b26 |
{{tokens.bg | rgba(FF)}} | rgba(RRGGBBAA) (uppercase hex with alpha) | rgba(1A1B26FF) |
The rgba() transform accepts any two-digit hex alpha value. Use FF for full opacity or lower values like CC for transparency.
Target inheritance
A target can extend another user-defined target with the extends field. The child inherits all artifacts from the base and can add new ones or override existing ones by matching file_name:
name = "my-terminal"
extends = "base-terminal"
[[artifacts]]
file_name = "colors.txt"
template = """
fg={{tokens.text | hex_no_hash}}
bg={{tokens.bg | hex_no_hash}}
"""
Inheritance rules
- The base target must be user-defined or from a pack — extending built-in targets (
kitty,alacritty) is not allowed. - Chains are supported: target A can extend B, which extends C. Cycles are detected and rejected.
- If a child artifact has the same
file_nameas a base artifact, the child’s version replaces it. - A target with
extendscan omit[[artifacts]]entirely to inherit the base unchanged under a new name.
Installing user targets
Drop .toml target files into:
~/.config/chromasync/targets/
All .toml files in this directory are auto-discovered. Targets can also be distributed as part of a pack.
Validation
Chromasync validates targets at load time:
- Target names must match
[a-z0-9_-]+. - Target names must not collide with built-in target names.
- Artifact file names must be non-empty with no path separators.
- All placeholders must reference valid token or context names.
- Transforms must be valid (
hex_no_hashorrgba(XX)). - Unterminated placeholders (missing
}}) are rejected. - Duplicate target names across sources of equal precedence are an error.
Examples
CSS custom properties
A target that outputs CSS custom properties for use in web projects:
name = "css"
description = "CSS design token target."
[[artifacts]]
file_name = "theme.css"
template = """
:root {
--chromasync-bg: {{tokens.bg}};
--chromasync-bg-secondary: {{tokens.bg_secondary}};
--chromasync-surface: {{tokens.surface}};
--chromasync-surface-elevated: {{tokens.surface_elevated}};
--chromasync-text: {{tokens.text}};
--chromasync-text-muted: {{tokens.text_muted}};
--chromasync-border: {{tokens.border}};
--chromasync-border-strong: {{tokens.border_strong}};
--chromasync-accent: {{tokens.accent}};
--chromasync-accent-hover: {{tokens.accent_hover}};
--chromasync-accent-active: {{tokens.accent_active}};
--chromasync-accent-fg: {{tokens.accent_fg}};
--chromasync-selection: {{tokens.selection}};
--chromasync-link: {{tokens.link}};
--chromasync-success: {{tokens.success}};
--chromasync-warning: {{tokens.warning}};
--chromasync-error: {{tokens.error}};
}
"""
Hyprland with rgba transforms
Hyprland expects rgba() color values. The rgba(FF) transform outputs uppercase hex with an alpha suffix:
name = "hyprland"
description = "Hyprland window manager theme."
[[artifacts]]
file_name = "hyprland.conf"
template = """
$background = {{tokens.bg | rgba(FF)}}
$surface = {{tokens.surface | rgba(FF)}}
$text = {{tokens.text | rgba(FF)}}
$text_muted = {{tokens.text_muted | rgba(FF)}}
$accent = {{tokens.accent | rgba(FF)}}
$accent_hover = {{tokens.accent_hover | rgba(FF)}}
$border = {{tokens.border | rgba(FF)}}
$border_strong = {{tokens.border_strong | rgba(FF)}}
$shadow = {{tokens.bg | rgba(CC)}}
general {
col.active_border = $accent $accent_hover 45deg
col.inactive_border = $border
}
decoration {
col.shadow = $shadow
shadow_range = 12
shadow_render_power = 3
}
group {
col.border_active = $accent
col.border_inactive = $border
col.group_border = $border_strong
col.group_border_active = $accent_hover
}
misc {
background_color = $background
}
"""
Foot terminal with hex_no_hash
Foot expects bare hex values without the # prefix:
name = "foot"
description = "Foot terminal emulator theme."
[[artifacts]]
file_name = "foot.ini"
template = """
[colors]
foreground={{tokens.text | hex_no_hash}}
background={{tokens.bg | hex_no_hash}}
selection-foreground={{tokens.text | hex_no_hash}}
selection-background={{tokens.selection | hex_no_hash}}
urls={{tokens.link | hex_no_hash}}
regular0={{tokens.bg_secondary | hex_no_hash}}
regular1={{tokens.error | hex_no_hash}}
regular2={{tokens.success | hex_no_hash}}
regular3={{tokens.warning | hex_no_hash}}
regular4={{tokens.link | hex_no_hash}}
regular5={{tokens.accent | hex_no_hash}}
regular6={{tokens.selection | hex_no_hash}}
regular7={{tokens.text_muted | hex_no_hash}}
bright0={{tokens.surface_elevated | hex_no_hash}}
bright1={{tokens.error | hex_no_hash}}
bright2={{tokens.success | hex_no_hash}}
bright3={{tokens.warning | hex_no_hash}}
bright4={{tokens.accent_hover | hex_no_hash}}
bright5={{tokens.accent_active | hex_no_hash}}
bright6={{tokens.border_strong | hex_no_hash}}
bright7={{tokens.text | hex_no_hash}}
"""
Editor theme with context values
An editor theme target that uses {{ctx.mode}} to set the theme type dynamically:
name = "editor"
description = "VS Code editor theme."
[[artifacts]]
file_name = "theme.json"
template = """
{
"name": "Chromasync {{ctx.mode}}",
"type": "{{ctx.mode}}",
"colors": {
"editor.background": "{{tokens.bg}}",
"editor.foreground": "{{tokens.text}}",
"editor.selectionBackground": "{{tokens.selection}}",
"editorCursor.foreground": "{{tokens.accent}}",
"activityBar.background": "{{tokens.bg_secondary}}",
"sideBar.background": "{{tokens.surface}}",
"statusBar.background": "{{tokens.surface_elevated}}",
"button.background": "{{tokens.accent}}",
"button.foreground": "{{tokens.accent_fg}}",
"textLink.foreground": "{{tokens.link}}"
}
}
"""
GTK stylesheet
A GTK target mapping semantic tokens to @define-color variables with widget styling rules:
name = "gtk"
description = "GTK theme."
[[artifacts]]
file_name = "gtk.css"
template = """
@define-color window_bg_color {{tokens.bg}};
@define-color window_fg_color {{tokens.text}};
@define-color view_bg_color {{tokens.surface}};
@define-color headerbar_bg_color {{tokens.surface_elevated}};
@define-color accent_bg_color {{tokens.accent}};
@define-color accent_fg_color {{tokens.accent_fg}};
@define-color selection_bg_color {{tokens.selection}};
@define-color border_color {{tokens.border}};
@define-color link_color {{tokens.link}};
@define-color success_color {{tokens.success}};
@define-color warning_color {{tokens.warning}};
@define-color error_color {{tokens.error}};
window, dialog, popover {
background-color: @window_bg_color;
color: @window_fg_color;
}
headerbar {
background-color: @headerbar_bg_color;
border-color: @border_color;
}
button.suggested-action, button:checked {
background-color: @accent_bg_color;
color: @accent_fg_color;
}
"""
Using custom targets
Install a target to ~/.config/chromasync/targets/ and use it by name:
chromasync generate \
--seed "#e06c75" \
--template minimal \
--targets kitty,foot,hyprland,gtk
Or reference a target file directly:
chromasync generate \
--seed "#e06c75" \
--template minimal \
--targets kitty,/path/to/my-custom.toml
Preview
The preview command shows the resolved palette families and semantic tokens for a seed color and template without writing any files. It is a quick way to inspect what colors a particular seed and template combination produces before committing to a full generate run.
Usage
chromasync preview --seed "#ff6b6b" --template brutalist
Preview prints a plain-text summary to stdout and exits. No targets are invoked and no files are created.
Options
| Flag | Required | Default | Description |
|---|---|---|---|
--seed | yes | — | Seed color in #RRGGBB format |
--template | yes | — | Template name or path to a template TOML file |
--mode | no | dark | Theme mode: dark or light |
--contrast | no | relative-luminance | Contrast heuristic: relative-luminance or apca-experimental |
The --template flag accepts the same values as in generate — a built-in template name, a user-config template name, or a file path.
Output format
The output has three sections: a header with generation parameters, a palette families block, and a semantic tokens block.
Header
Seed: #ff6b6b
Mode: dark
Template: brutalist
Contrast: relative-luminance
Template Source: built-in (brutalist-dark.toml)
Description: High-contrast theme with louder borders and harder accent separation
The header shows the seed, mode, template name, contrast strategy, where the template was loaded from, and its description (if any).
Palette families
Palette Families
primary hue=12.34 chroma=0.123 0=#111111 10=#1a1a1a 20=#222222 ...
secondary hue=45.67 chroma=0.098 0=#111111 10=#1a1a1a 20=#222222 ...
neutral hue=12.34 chroma=0.008 0=#111111 10=#1a1a1a 20=#222222 ...
...
Each row shows a palette family with its hue and chroma values, followed by tone samples rendered as hex colors. This lets you see the full color ramp available to template rules.
Semantic tokens
Semantic Tokens
bg #1a1a1a
bg_secondary #202020
surface #252525
surface_elevated #2a2a2a
text #e0e0e0
text_muted #a0a0a0
border #404040
border_strong #505050
accent #ff6b6b
accent_hover #ff5555
accent_active #ff4444
accent_fg #ffffff
selection #ff6b6b
link #6b9bff
success #6bff6b
warning #ffff6b
error #ff6b6b
All 17 resolved tokens are listed with their final hex values. These are the exact colors that targets would substitute into output files during generate.
Comparing templates
Preview is useful for comparing how different templates interpret the same seed:
chromasync preview --seed "#4ecdc4" --template minimal
chromasync preview --seed "#4ecdc4" --template brutalist
chromasync preview --seed "#4ecdc4" --template terminal
Comparing modes
Check how dark and light modes differ for the same seed and template:
chromasync preview --seed "#4ecdc4" --template minimal --mode dark
chromasync preview --seed "#4ecdc4" --template minimal --mode light
Contrast strategies
The --contrast flag controls how Chromasync ensures readable foreground colors. The default relative-luminance uses WCAG 2.x contrast ratio (minimum 4.5:1). The apca-experimental option uses the APCA algorithm instead. Preview shows which strategy was used in the header so you can verify the resolved tokens meet your accessibility requirements.
chromasync preview \
--seed "#4ecdc4" \
--template minimal \
--contrast apca-experimental
See Contrast strategies for details on the available algorithms.
Preview vs generate
| preview | generate | |
|---|---|---|
| Output | Text to stdout | Files to disk |
| Targets | Not used | Required (--targets) |
| Output directory | Not used | Required (--output) |
| Purpose | Inspect colors | Create theme files |
Both commands use the same palette generation and token resolution pipeline, so the colors shown by preview are exactly what generate would produce.
Tokens
The tokens command exports the 17 resolved semantic color tokens as structured data. It runs the same palette generation and template resolution pipeline as generate but outputs the token values instead of rendering target artifacts. This is useful for feeding colors into scripts, custom tooling, or formats that Chromasync does not have a target for.
Usage
chromasync tokens --seed "#4ecdc4" --template minimal
The command prints the resolved tokens to stdout and exits. No files are written.
Options
| Flag | Required | Default | Description |
|---|---|---|---|
--seed | yes | — | Seed color in #RRGGBB format |
--template | yes | — | Template name or path to a template TOML file |
--mode | no | dark | Theme mode: dark or light |
--contrast | no | relative-luminance | Contrast heuristic: relative-luminance or apca-experimental |
--format | no | json | Serialization format: json |
The --template flag accepts the same values as in generate — a built-in name, a user-config name, or a file path.
Output format
The default (and currently only) format is JSON. The output is a flat object with all 17 token names as keys and #RRGGBB hex strings as values:
{
"bg": "#040303",
"bg_secondary": "#090706",
"surface": "#0E0A0A",
"surface_elevated": "#1B1413",
"text": "#F0E9E9",
"text_muted": "#C4B3AF",
"border": "#342521",
"border_strong": "#533C36",
"accent": "#E86C6C",
"accent_hover": "#FA827F",
"accent_active": "#CF5051",
"accent_fg": "#010100",
"selection": "#511718",
"link": "#78AFEE",
"success": "#8BAC55",
"warning": "#D1A33C",
"error": "#EC817A"
}
The JSON is pretty-printed with indentation. Token names use snake_case.
Semantic tokens
Every template resolves all 17 tokens. They are grouped by role:
Backgrounds and surfaces
| Token | Role |
|---|---|
bg | Primary background — the main canvas of the application |
bg_secondary | Secondary background — side panels, alternate rows, grouped sections |
surface | Interactive surface — buttons, inputs, cards |
surface_elevated | Elevated surface — dialogs, tooltips, floating panels, popovers |
Text
| Token | Role |
|---|---|
text | Primary text — body copy, headings |
text_muted | Secondary text — placeholders, captions, disabled labels |
Borders
| Token | Role |
|---|---|
border | Regular border — input outlines, dividers, separators |
border_strong | Emphasized border — focused inputs, section boundaries |
Accent
| Token | Role |
|---|---|
accent | Primary accent — buttons, highlights, active indicators |
accent_hover | Accent hover state — slightly lighter or more saturated variant |
accent_active | Accent active/pressed state — slightly darker or less saturated variant |
accent_fg | Text on accent backgrounds — guaranteed readable against accent |
Selection and links
| Token | Role |
|---|---|
selection | Text selection or highlight background |
link | Hyperlink color |
Status
| Token | Role |
|---|---|
success | Success indicator — confirmations, passing checks |
warning | Warning indicator — caution states, degraded status |
error | Error indicator — failures, validation errors, destructive actions |
Resolution pipeline
The tokens command runs the same pipeline as generate and preview:
-
Palette generation — the seed color is converted to OKLCH and used to derive 9 palette families (primary, secondary, tertiary, neutral, neutral_variant, error, success, warning, info), each with 16 tone samples spanning black to white.
-
Template loading — the template is loaded from built-in, user config, pack, or filesystem sources. Each template defines a rule for every token specifying which palette family and tone to sample, with optional chroma overrides.
-
Token resolution — for each of the 17 tokens, the resolver looks up the rule’s palette family, computes the final chroma as
(chroma OR family.base_chroma) * chroma_scale, and converts the OKLCH triplet (family hue, final chroma, rule tone) to a#RRGGBBhex color. -
Contrast adjustment — after all tokens are resolved, the resolver checks that
textis readable againstbgand thataccent_fgis readable againstaccent. If a pair fails the contrast threshold, the resolver tries neutral light (tone=0.98) and dark (tone=0.06) fallbacks and picks the candidate with the best score. This ensures the exported tokens always meet accessibility requirements.
Contrast strategies
The --contrast flag selects the algorithm used in step 4:
| Strategy | Algorithm | Minimum threshold |
|---|---|---|
relative-luminance | WCAG 2.0 relative luminance ratio | 4.5:1 |
apca-experimental | APCA (Advanced Perceptual Contrast Algorithm) | Score of 60 |
The default relative-luminance is the widely adopted WCAG standard. The apca-experimental option uses a perceptually uniform model that better accounts for how the eye perceives contrast at different luminance levels. It is marked experimental and may change in future versions.
See Contrast strategies for more details.
Scripting with tokens
Because tokens outputs structured JSON, it integrates well with tools like jq:
Extract a single token
chromasync tokens --seed "#4ecdc4" --template minimal | jq -r '.accent'
# #4CBDB5
Build a shell color palette
eval "$(chromasync tokens --seed "#4ecdc4" --template minimal \
| jq -r 'to_entries[] | "CHROMASYNC_\(.key | ascii_upcase)=\(.value)"')"
echo "$CHROMASYNC_ACCENT"
# #4CBDB5
Generate a custom config file
chromasync tokens --seed "#4ecdc4" --template minimal \
| jq -r '"cursor_color=\(.accent)\nforeground=\(.text)\nbackground=\(.bg)"' \
> ~/.config/myapp/colors.conf
Compare dark and light tokens
diff <(chromasync tokens --seed "#4ecdc4" --template minimal --mode dark) \
<(chromasync tokens --seed "#4ecdc4" --template minimal --mode light)
Tokens vs preview
Both commands resolve the same tokens from the same pipeline. The difference is in output format and detail:
| tokens | preview | |
|---|---|---|
| Output format | JSON (machine-readable) | Plain text (human-readable) |
| Palette families | Not shown | Shown with hue, chroma, and tone samples |
| Metadata | Not shown | Seed, mode, template source, contrast strategy |
| Use case | Scripting, piping to other tools | Quick visual inspection |
Use preview when you want to eyeball the colors. Use tokens when you need to feed them into another program.
Tokens vs generate
| tokens | generate | |
|---|---|---|
| Output | Token values to stdout | Artifact files to disk |
| Targets | Not used | Required (--targets) |
| Purpose | Export raw color data | Produce app-specific config files |
If you need output for a specific application, write a target instead of processing token JSON manually. Targets handle format-specific details like placeholder substitution and file naming.
MCP Server
The chromasync-mcp binary exposes Chromasync as a Model Context Protocol (MCP) server. This lets AI assistants generate themes, preview palettes, export tokens, and query available templates, targets, and packs — using the same pipeline as the CLI.
Running the server
The server communicates over stdio using JSON-RPC:
chromasync-mcp
It reads MCP requests from stdin and writes responses to stdout. Logs go to stderr and can be controlled with the RUST_LOG environment variable:
RUST_LOG=info chromasync-mcp
Configuring with Claude Code
Add to your Claude Code MCP settings (.claude/settings.json or project-level):
{
"mcpServers": {
"chromasync": {
"command": "chromasync-mcp"
}
}
}
If the binary is not on your PATH, use the full path:
{
"mcpServers": {
"chromasync": {
"command": "/path/to/chromasync-mcp"
}
}
}
Available tools
The server exposes 10 tools:
Theme generation
| Tool | Description |
|---|---|
generate | Generate theme artifacts from a seed color and write them to disk |
wallpaper | Generate theme artifacts from a wallpaper image and write them to disk |
batch | Execute a TOML batch manifest containing multiple generation jobs |
Inspection (read-only)
| Tool | Description |
|---|---|
preview | Preview palette families and resolved semantic tokens for a seed color |
export_tokens | Export the 17 resolved semantic token hex values as JSON |
generate_palette | Generate the full OKLCH palette (9 families, 16 tones each) from a seed color |
Discovery (read-only)
| Tool | Description |
|---|---|
list_templates | List all available templates with their name, mode, source, and location |
list_targets | List all available render targets with their name, source, and location |
list_packs | List all discovered theme packs |
pack_info | Get metadata, templates, and targets for a specific theme pack |
Common parameters
Several tools share common parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
seed | string | (required) | Seed color in #RRGGBB hex format |
template | string | (required) | Template name or path to a .toml file |
mode | string | "dark" | Theme mode: "dark" or "light" |
contrast | string | "relative-luminance" | Contrast strategy: "relative-luminance" or "apca-experimental" |
targets | string[] | (required for generation) | Target names or paths to target TOML files |
output_dir | string | (required for generation) | Directory to write artifact files into |
Example interactions
Generate a theme
{
"name": "generate",
"arguments": {
"seed": "#ff6b6b",
"template": "brutalist",
"mode": "dark",
"targets": ["kitty", "alacritty"],
"output_dir": "./my-theme"
}
}
Returns a JSON array of written artifact paths.
Preview tokens without writing files
{
"name": "preview",
"arguments": {
"seed": "#4ecdc4",
"template": "minimal"
}
}
Returns a human-readable summary of palette families and semantic tokens.
Export tokens as JSON
{
"name": "export_tokens",
"arguments": {
"seed": "#7c3aed",
"template": "terminal",
"mode": "light"
}
}
Returns the 17 semantic token hex values as a JSON object.
Discover available templates
{
"name": "list_templates",
"arguments": {}
}
Returns a JSON array with each template’s name, mode, description, source, and location.
Output format
- Generation tools (
generate,wallpaper,batch) return JSON arrays describing written files, including the target name, file name, and full path. previewreturns plain text with palette families and semantic tokens.export_tokensandgenerate_palettereturn structured JSON.- Discovery tools (
list_templates,list_targets,list_packs,pack_info) return JSON arrays or objects.
Overwrite protection
Like the CLI, the generate, wallpaper, and batch tools refuse to overwrite existing files. If an artifact already exists at the destination, the tool returns an error. Delete or move the existing output first, or use a different output_dir.
Building from source
cargo build --release -p chromasync-mcp
The binary is written to target/release/chromasync-mcp.
CLI Reference
This page is generated from the Clap command tree in chromasync-cli.
chromasync
Dynamic color engine and theme generator CLI
Usage: chromasync <COMMAND>
Commands:
generate Generate theme artifacts from a seed color
wallpaper Generate theme artifacts from a wallpaper image
batch Execute a batch manifest with multiple generation jobs
templates List the available templates and where they were loaded from
packs List the discovered theme packs
pack Inspect a discovered theme pack
targets List available renderer targets and where they were loaded from
preview Show palette families and resolved semantic tokens
tokens Export resolved semantic tokens
completions Generate shell completion scripts
help Print this message or the help of the given subcommand(s)
Options:
-h, --help
Print help
-V, --version
Print version
chromasync generate
Generate theme artifacts from a seed color
Usage: generate [OPTIONS] --seed <SEED> --targets <TARGETS>
Options:
--seed <SEED>
Seed color in #RRGGBB format
--template <TEMPLATE>
Template name or path to a template TOML file. Optional if targets specify preferred_template
--mode <MODE>
Theme mode to generate
[default: dark]
[possible values: dark, light]
--contrast <CONTRAST>
Contrast selection heuristic used when resolving readable foregrounds
[default: relative-luminance]
[possible values: relative-luminance, apca-experimental]
--chroma <CHROMA>
Chroma strategy used when generating palette families
[default: normal]
[possible values: subtle, normal, vibrant, muted, industrial]
--targets <TARGETS>
Comma-separated list of target names or target TOML paths to generate
--output <OUTPUT>
Output directory for generated artifacts
[default: chromasync]
-h, --help
Print help
chromasync wallpaper
Generate theme artifacts from a wallpaper image
Usage: wallpaper [OPTIONS] --image <IMAGE> --targets <TARGETS>
Options:
--image <IMAGE>
Wallpaper image path
--template <TEMPLATE>
Template name or path to a template TOML file. Optional if targets specify preferred_template
--mode <MODE>
Theme mode to generate
[default: dark]
[possible values: dark, light]
--contrast <CONTRAST>
Contrast selection heuristic used when resolving readable foregrounds
[default: relative-luminance]
[possible values: relative-luminance, apca-experimental]
--chroma <CHROMA>
Chroma strategy used when generating palette families
[default: normal]
[possible values: subtle, normal, vibrant, muted, industrial]
--targets <TARGETS>
Comma-separated list of target names or target TOML paths to generate
--output <OUTPUT>
Output directory for generated artifacts
[default: chromasync]
-h, --help
Print help
chromasync batch
Execute a batch manifest with multiple generation jobs
Usage: batch --file <FILE>
Options:
--file <FILE>
Path to a TOML manifest containing multiple jobs
-h, --help
Print help
chromasync templates
List the available templates and where they were loaded from
Usage: templates
Options:
-h, --help
Print help
chromasync packs
List the discovered theme packs
Usage: packs
Options:
-h, --help
Print help
chromasync pack
Inspect a discovered theme pack
Usage: pack <COMMAND>
Commands:
info Show metadata and assets for an installed pack
help Print this message or the help of the given subcommand(s)
Options:
-h, --help
Print help
chromasync pack info
Show metadata and assets for an installed pack
Usage: info <NAME>
Arguments:
<NAME>
Pack name from pack.toml
Options:
-h, --help
Print help
chromasync targets
List available renderer targets and where they were loaded from
Usage: targets
Options:
-h, --help
Print help
chromasync preview
Show palette families and resolved semantic tokens
Usage: preview [OPTIONS] --seed <SEED> --template <TEMPLATE>
Options:
--seed <SEED>
Seed color in #RRGGBB format
--template <TEMPLATE>
Template name or path to a template TOML file
--mode <MODE>
Theme mode to preview
[default: dark]
[possible values: dark, light]
--contrast <CONTRAST>
Contrast selection heuristic used when resolving readable foregrounds
[default: relative-luminance]
[possible values: relative-luminance, apca-experimental]
--chroma <CHROMA>
Chroma strategy used when generating palette families
[default: normal]
[possible values: subtle, normal, vibrant, muted, industrial]
-h, --help
Print help
chromasync tokens
Export resolved semantic tokens
Usage: tokens [OPTIONS] --seed <SEED> --template <TEMPLATE>
Options:
--seed <SEED>
Seed color in #RRGGBB format
--template <TEMPLATE>
Template name or path to a template TOML file
--mode <MODE>
Theme mode to resolve
[default: dark]
[possible values: dark, light]
--contrast <CONTRAST>
Contrast selection heuristic used when resolving readable foregrounds
[default: relative-luminance]
[possible values: relative-luminance, apca-experimental]
--chroma <CHROMA>
Chroma strategy used when generating palette families
[default: normal]
[possible values: subtle, normal, vibrant, muted, industrial]
--format <FORMAT>
Serialization format for token export
[default: json]
[possible values: json]
-h, --help
Print help
chromasync completions
Generate shell completion scripts
Usage: completions <SHELL>
Arguments:
<SHELL>
Shell to generate completions for
[possible values: bash, elvish, fish, powershell, zsh]
Options:
-h, --help
Print help
Built-in Templates
| Name | Mode | Description | Source |
|---|---|---|---|
brutalist | dark | Built-in high-contrast dark theme with louder borders and harder accent separation. | brutalist-dark.toml |
brutalist | light | Built-in high-contrast light theme with louder borders and harder accent separation. | brutalist-light.toml |
materialish | dark | Built-in softer system-style dark theme with layered surfaces and calmer accents. | materialish-dark.toml |
materialish | light | Built-in softer system-style light theme with layered surfaces and calmer accents. | materialish-light.toml |
minimal | dark | Built-in restrained dark theme with subdued surfaces and a single clean accent. | minimal-dark.toml |
minimal | light | Built-in restrained light theme with paper surfaces and a single clean accent. | minimal-light.toml |
terminal | dark | Built-in terminal-oriented dark theme with deep backgrounds and crisp signal colors. | terminal-dark.toml |
terminal | light | Built-in terminal-oriented light theme with bright backgrounds and crisp signal colors. | terminal-light.toml |
Built-in Targets
| Name | Default Artifact |
|---|---|
kitty | kitty.conf |
alacritty | alacritty.toml |