How Themes Are Rendered in Omarchy: Complete Technical Guide

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 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 — Base palette definitions (foreground, background, accent, urgent, muted)
  2. 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 line-by-line
  • Extracts palette keys: foreground, background, accent, muted, urgent

parseShell(raw)

  • Walks the TOML-style shell.toml structure
  • Builds a flat dictionary keyed as section.key (e.g., bar.background, notifications.text)
// 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
  • 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:

// 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, 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:

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


# List available themes

omarchy-theme-list

# Apply a specific theme

omarchy-theme-set tokyo-night

Using Theme Colors in Custom QML Components

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

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

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 for base palette values and a generated 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 defining foreground, background, accent, urgent, and muted keys. Run omarchy-theme-set <your-theme> to generate the 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →