# How the Omarchy Theming System Generates Templates and Applies Theme Tokens

> Explore how Omarchy's theming system generates templates and applies theme tokens. Learn about template rendering and theme activation for seamless shell customization.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: internals
- Published: 2026-08-19

---

**TLDR:** Omarchy’s theming system operates in two distinct stages—**template rendering** and **theme activation**—where `bin/omarchy-theme-set-templates` processes [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) palettes into concrete configuration files via `sed`-based substitution, and `bin/omarchy-theme-set` orchestrates atomic theme swaps with IPC notifications to the running shell.

The Omarchy desktop environment by Basecamp uses a declarative theming pipeline that transforms simple color palettes into fully rendered configuration files for Alacritty, Hyprland, and the Quickshell. By analyzing the source code in `basecamp/omarchy`, this article explains how template placeholders, color mixing, and gradient generation work together to apply theme tokens consistently across the entire desktop.

## Template Rendering Architecture

The first stage of the Omarchy theming system is handled by `bin/omarchy-theme-set-templates`. This script reads a theme’s [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) file and builds a comprehensive substitution map called `THEME_COLORS`.

### Color Palette Parsing and Token Mapping

When `bin/omarchy-theme-set-templates` executes, it parses the theme’s [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) to extract all color keys. For every key (such as `accent`, `background`, or `foreground`), the script generates three variants:

- **`{{ key }}`** – The raw hexadecimal value (e.g., `#7aa2f7`)
- **`{{ key_strip }}`** – The hex value without the hash symbol (e.g., `7aa2f7`)
- **`{{ key_rgb }}`** – An RGB tuple formatted for specific consumers (e.g., `122, 162, 247`)

These placeholders are stored in a `sed` script that will process all template files.

### Placeholder Substitution and Mix/Gradient Helpers

Beyond simple value replacement, the Omarchy theming system supports dynamic color manipulation through helper functions within the template syntax:

- **Mix helpers** – `{{ mix background foreground 15% }}` blends two colors using a Bash-awk routine named `mix_color`. Variants include `{{ mix_strip … }}` and `{{ mix_rgb … }}` for different output formats.
- **Gradient helpers** – `{{ hypr_gradient … }}` parses Hyprland-style gradient strings and resolves theme references to produce Lua tables. Similarly, `{{ shell_gradient … }}` generates plain CSS gradients, while `{{ gradient_start … }}` extracts the first stop as a hex value.

These helpers allow a single [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) palette to generate complex, interpolated color schemes for multiple config formats simultaneously.

### Template Processing and Staging

The script iterates over two template directories:

1. Built-in templates at `default/themed/*.tpl`
2. User-supplied templates at `~/.config/omarchy/themed/*.tpl`

For each `.tpl` file found, the renderer applies the `sed` substitution script and writes the result to `~/.local/state/omarchy/current/next-theme/`. Importantly, if a target file already exists in the staging directory, the script skips it, allowing hand-written configuration files to override generated ones. After processing all templates, the temporary `sed` script is removed.

## Theme Activation Flow

The second stage is orchestrated by `bin/omarchy-theme-set`, which coordinates the atomic swap from the current theme to the new selection.

### Theme Validation and Locking

When a user invokes `omarchy theme set <name>`, the script first normalizes the theme name and validates its existence in either the system path `$OMARCHY_PATH/themes` or the user path `~/.config/omarchy/themes`. To prevent race conditions during concurrent theme changes, the script acquires a file lock at `$XDG_RUNTIME_DIR/omarchy-theme-set.lock`.

### Staging and Template Generation

The activation process follows a strict staging sequence:

1. Copies the selected theme into a clean staging directory at `~/.local/state/omarchy/current/next-theme`, overlaying any user customizations
2. Generates [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) from an existing [`alacritty.toml`](https://github.com/basecamp/omarchy/blob/main/alacritty.toml) if needed via the `omarchy-theme-colors-from-alacritty` helper
3. Invokes `omarchy-theme-set-templates` to render all dynamic configuration files

### Atomic Swap and IPC Notification

After rendering templates, the system prepares for a smooth transition:

- Takes a snapshot of the current background to prevent flickering
- Atomically swaps the staging directory into the active location at `~/.local/state/omarchy/current/theme`
- Updates the active theme name stored in `~/.local/state/omarchy/current/theme.name`
- Adjusts the background symlink for wallpaper rendering

Finally, the script sends the new palette ([`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml)) and shell description ([`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml)) to the running Quickshell process via the IPC command `omarchy-shell shell applyTheme`. It then launches post-theme commands in parallel to retint running applications including terminals, Hyprland, btop, and VS Code.

## Key Files and Scripts

Understanding the Omarchy theming system requires familiarity with these specific source files:

- **`bin/omarchy-theme-set`** – The main orchestration script that handles theme validation, locking, staging, and IPC notification
- **`bin/omarchy-theme-set-templates`** – The template renderer that processes placeholders, mix functions, and gradients
- **`default/themed/*.tpl`** – Built-in templates defining how palette keys translate to concrete config files (e.g., `alacritty.toml.tpl`, `hyprland.lua.tpl`)
- **`bin/omarchy-theme-color`** – Helper utility that parses [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) and outputs key/value pairs for the renderer
- **`bin/omarchy-hook`** – Executes the `theme-set` hook after application, allowing user-defined scripts to run

## Summary

- The Omarchy theming system uses a two-stage pipeline: **template rendering** via `bin/omarchy-theme-set-templates` and **theme activation** via `bin/omarchy-theme-set`.
- Template placeholders support raw values, stripped hex, RGB tuples, and dynamic color mixing through special helper syntax.
- Themes are staged in `~/.local/state/omarchy/current/next-theme/` before an atomic swap into the active theme directory.
- Concurrent theme changes are serialized using a file lock at `$XDG_RUNTIME_DIR/omarchy-theme-set.lock`.
- The Quickshell receives theme updates via IPC using `omarchy-shell shell applyTheme` to ensure the UI reflects the new palette immediately.

## Frequently Asked Questions

### What file format does Omarchy use to define color palettes?

Omarchy uses **TOML files** named [`colors.toml`](https://github.com/basecamp/omarchy/blob/main/colors.toml) to define color palettes. Each key in the file (such as `accent` or `background`) maps to a hexadecimal color value that the template renderer converts into multiple token formats (raw, stripped, and RGB) for use in different configuration files.

### How do I create custom templates for the Omarchy theming system?

Create files ending in `.tpl` inside the directory `~/.config/omarchy/themed/`. These templates use the same placeholder syntax (`{{ accent }}`, `{{ mix background foreground 20% }}`) as the built-in templates at `default/themed/*.tpl`. When `omarchy-theme-set-templates` runs, it processes user templates alongside system templates, with user versions taking precedence if they already exist in the staging directory.

### What is the purpose of the `next-theme` staging directory?

The `~/.local/state/omarchy/current/next-theme/` directory serves as an **atomic staging area** where the Omarchy theming system assembles the new theme configuration before activation. This approach ensures that all templates are fully rendered and all files are in place before the system swaps the directory into the active location, preventing partial or broken theme states.

### How does Omarchy handle concurrent theme changes?

Omarchy prevents race conditions by using a **file-based lock** at `$XDG_RUNTIME_DIR/omarchy-theme-set.lock`. When `bin/omarchy-theme-set` executes, it acquires this lock before copying theme files or rendering templates, ensuring that only one theme change operation can run at a time even if multiple `omarchy theme set` commands are triggered simultaneously.