# How Themes Are Rendered in Omarchy: Complete Technical Guide

> Discover how Omarchy renders themes at runtime using a QML architecture. Learn how Color.qml and Style.qml parse TOML for instant UI updates.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: deep-dive
- Published: 2026-09-03

---

**Themes in Omarchy are rendered at runtime through a singleton-based QML architecture where two core objects—`Color.qml` and `Style.qml`—parse TOML configuration files and expose reactive properties that UI components bind to for instant visual updates.**

Omarchy's theming system combines declarative Qt Quick bindings with file-based configuration to deliver dynamic, runtime-switchable visual themes. The entire pipeline lives in the [omacom/omarchy](https://github.com/omacom/omarchy) repository and centers on two QML singletons that transform raw TOML data into reactive style tokens.

## Core Components: Color and Style Singletons

Theme rendering in Omarchy depends on two singleton objects in `shell/Commons/`:

- **`Color.qml`** (`shell/Commons/Color.qml`): Manages the complete color palette and surface-specific color tokens
- **`Style.qml`** (`shell/Commons/Style.qml`): Provides typography, spacing, dimensions, and geometric style values

These singletons expose properties that any QML component can import and bind to, creating a reactive theming layer where changes propagate automatically.

## How Theme Files Are Parsed and Loaded

### Theme Directory Structure

Each theme resides in `themes/<name>/` and supplies two essential files:

1. [`colors.toml`](https://github.com/omacom/omarchy/blob/main/colors.toml) — Base palette definitions (foreground, background, accent, urgent, muted)
2. [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml) — Generated surface tokens and style overrides

### Parsing Pipeline in Color.qml

The `Color.qml` singleton implements the theme loading logic through two primary functions:

**`loadColors(raw)`**
- Parses [`colors.toml`](https://github.com/omacom/omarchy/blob/main/colors.toml) line-by-line
- Extracts palette keys: `foreground`, `background`, `accent`, `muted`, `urgent`

**`parseShell(raw)`**
- Walks the TOML-style [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml) structure
- Builds a flat dictionary keyed as `section.key` (e.g., `bar.background`, `notifications.text`)

```qml
// Color.qml exposes properties like:
property color barBackground: pick("bar.background")
property color notificationsText: pick("notifications.text")
property color menuScrim: pickAlpha("menu.scrim", 0.8)

```

### Theme Merging and Reactivity

Color.qml maintains two internal dictionaries:

- `themeShellValues` — Base values from the theme's [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml)
- `userShellValues` — User-specific overrides

The `mergeShell` function combines these into `shellValues`. Because `shellValues` is a **property** (not mutated in place), any QML binding depending on it automatically re-evaluates when the theme changes.

### Color Resolution Helpers

Color.qml provides utility functions for token-to-color conversion:

| Function | Purpose |
|----------|---------|
| `pick(key)` | Resolve a token to `Qt.rgba` with fallback handling |
| `pickAlpha(key, alpha)` | Resolve with explicit alpha override |
| `composed(key, alpha)` | Blend or override alpha on resolved color |
| `flatColor(key)` | Final color extraction with default resolution |

## Style Tokens: Typography and Geometry

`Style.qml` (`shell/Commons/Style.qml`) complements the color system with non-color style properties:

```qml
// Components reference style tokens like:
border.width: qs.Commons.Style.borderWidth
radius: qs.Commons.Style.cornerRadius
font.pixelSize: qs.Commons.Style.fontSize

```

Themes override these via entries in [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml), allowing per-theme control over spacing, border widths, corner radii, and typography scales.

## Runtime Theme Switching

Theme changes in Omarchy happen without application restart through an IPC-driven workflow:

### Trigger Mechanism

The background plugin (`shell/plugins/background/Background.qml`) initiates switches via:

```qml
command: ["bash", "-c", "theme=$(omarchy-theme-switcher); [[ -n $theme ]] && omarchy-theme-set \"$theme\" >/dev/null 2>&1 &"]

```

### Update Flow

1. `omarchy-theme-set <name>` writes the merged theme payload to `$HOME/.local/local/state/omarchy/current/theme/`
2. The Color singleton detects the change (via file watcher or IPC hook)
3. `loadColors()` and `parseShell()` re-parse the new files
4. `shellValues` property assignment triggers QML binding re-evaluation
5. All components update instantly with new colors and styles

## Practical Code Examples

### Switching Themes from Command Line

```bash

# List available themes

omarchy-theme-list

# Apply a specific theme

omarchy-theme-set tokyo-night

```

### Using Theme Colors in Custom QML Components

```qml
import QtQuick 2.15
import Quickshell
import qs.Commons.Color as Color

Rectangle {
    width: 200; height: 100
    color: Color.bar.background        // Binds to active theme's bar background
    
    Text {
        anchors.centerIn: parent
        text: "Hello"
        color: Color.bar.text           // Theme-aware text color
    }
}

```

### Adapting to Style Tokens

```qml
import QtQuick 2.15
import qs.Commons.Style as Style

Rectangle {
    width: 120; height: 50
    color: "#222"
    radius: Style.cornerRadius          // Automatically rounds when theme defines it
}

```

## Key Source Files

| File | Role |
|------|------|
| `shell/Commons/Color.qml` | Singleton palette, parses TOML, exposes surface colors |
| `shell/Commons/Style.qml` | Singleton for typography, spacing, corner radius |
| `themes/*/colors.toml` | Base color definitions per theme |
| `themes/*/shell.toml` | Generated surface and style tokens |
| `shell/plugins/background/Background.qml` | Runtime theme switching trigger |
| `default/themed/shell.toml.tpl` | Template for generating [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml) |

## Summary

- **Theme rendering in Omarchy** is driven by two QML singletons that parse TOML configuration into reactive properties
- **Color.qml** handles palette and surface-specific colors through `loadColors()` and `parseShell()` with automatic merging of theme and user values
- **Style.qml** provides geometric and typographic tokens that components bind to with `qs.Commons.Style.*`
- **Runtime switching** works via `omarchy-theme-set` writing to the state directory, triggering property updates and automatic UI refresh
- **No restart required** because QML property bindings re-evaluate when `shellValues` is reassigned

## Frequently Asked Questions

### What file format does Omarchy use for theme definitions?

Omarchy uses **TOML** for theme configuration. Each theme provides a [`colors.toml`](https://github.com/omacom/omarchy/blob/main/colors.toml) for base palette values and a generated [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml) for surface-specific color tokens and style overrides. These files are parsed at runtime by the Color.qml singleton.

### How do I create a custom theme for Omarchy?

Create a directory under `themes/<your-theme>/` with a [`colors.toml`](https://github.com/omacom/omarchy/blob/main/colors.toml) defining `foreground`, `background`, `accent`, `urgent`, and `muted` keys. Run `omarchy-theme-set <your-theme>` to generate the [`shell.toml`](https://github.com/omacom/omarchy/blob/main/shell.toml) from the template at `default/themed/shell.toml.tpl`, then customize the generated values as needed.

### Why does Omarchy use QML singletons instead of global objects?

QML singletons provide **automatic property binding re-evaluation** across all components. When `Color.shellValues` is replaced (not mutated), every binding like `color: Color.bar.background` updates instantly without manual refresh logic or application restart.

### Can I override specific theme values without creating a new theme?

Yes. User overrides are stored separately in `userShellValues` and merged with base theme values via `mergeShell()`. The Color.qml singleton maintains both dictionaries and applies user values on top of theme defaults, allowing targeted customization while preserving the base theme.