How the Color Singleton Works in Omarchy QML: A Deep Dive into Theme-Aware Colors

The Color singleton is a pragma Singleton object that centralizes all color values in Omarchy QML, providing reactive, theme-aware colors that update automatically when configuration files change.

The Omarchy window manager uses a sophisticated theming system where the Color singleton serves as the single source of truth for all UI colors. Located in shell/Commons/Color.qml, this singleton abstracts the complexity of theme parsing and provides a hierarchical, reactive color system that other QML components consume through simple property bindings.

What Is the Color Singleton?

The Color singleton is a QML singleton type declared with pragma Singleton at the top of shell/Commons/Color.qml. Unlike regular QML objects, singletons exist as unique global instances that can be imported anywhere in the application without instantiation.

Other components access the singleton through the import statement:

import "Commons/Color.qml" as Color

This design pattern ensures that every widget references the same color values, maintaining visual consistency across the entire shell while enabling instant updates when the user switches themes or modifies configuration files.

The Foundational Color Palette

At the core of the singleton lies a foundational palette defined on lines 19–23 of shell/Commons/Color.qml. These five properties establish the base color vocabulary:

  • foreground – Primary text color
  • background – Window and surface backgrounds
  • accent – Highlight and selection states
  • urgent – Critical notifications and alerts
  • muted – Secondary or disabled text

These properties initialize with fallback hex values ensuring the shell remains functional even without theme files. When the colorsFile loader completes (lines 24–30), the singleton populates these properties from the theme's colors.toml file, replacing the fallbacks with theme-specific values.

Surface-Specific Color Objects

Beyond the foundational palette, the singleton exposes surface-specific colors as read-only QtObject instances. Each major UI surface receives its own dedicated object:

  • bar – Top panel and status indicators
  • popup – Dialog windows and overlays
  • tooltip – Hover information panels
  • notifications – Alert toasts
  • menu – Context menus
  • polkit – Authentication dialogs
  • lock – Screen lock interface
  • image-picker – File selection dialogs

For example, the bar surface begins at line 73 as a QtObject containing properties like background, text, and border. These properties do not store hardcoded values; instead, they bind to the foundational palette through helper functions that resolve color tokens dynamically.

Helper Functions for Color Resolution

The singleton implements four specialized helper functions that transform theme tokens into actual color values:

pick(token, fallback) (lines 30–34)

Returns a string value from the reactive shellValues dictionary or the provided fallback. This function serves as the primary lookup mechanism for arbitrary color keys defined in theme configurations.

pickAlpha(token) (lines 35–41)

Extracts numeric alpha values from the shell configuration, clamping them between valid ranges using Util.clampAlpha. This ensures opacity values remain within 0.0–1.0 bounds before application.

flatColor(token, fallback) (lines 43–65)

Resolves color tokens including role aliases (foreground, accent) and gradient definitions. This function handles the indirection where a theme might reference "accent" rather than a raw hex code, recursively resolving the actual color value.

composed(token, alphaToken, fallback, fallbackAlpha) (lines 67–71)

Combines a base color with its alpha companion using Util.alpha, producing semi-transparent variants. This enables declarative definitions like Color.composed("popup.overlay", "popup.overlay-alpha", Color.background, 0.6) to generate overlays with variable opacity.

Theme Data Flow and Reactive Updates

The singleton implements a four-stage pipeline that converts static TOML files into reactive QML properties:

  1. Loading – colorsFile and shellFile loaders read colors.toml and shell.toml from the active theme directory (lines 24–38). Their onLoaded callbacks trigger loadColors() and loadShell() respectively.

  2. Parsing Colors – The loadColors() function (lines 35–65) parses color definitions using regex patterns to extract values for foreground, background, accent, muted, color0 through color15, and other palette entries.

  3. Parsing Shell Values – parseShell() (lines 73–99) transforms the raw shell.toml structure into a flat dictionary mapping "section.key" strings to their values, enabling dot-notation lookups like bar.background.

  4. Merging – Theme-provided values (themeShellValues) merge with user overrides (userShellValues) on lines 101–108, producing the reactive shellValues property. Because QML bindings automatically track property assignments, all surface colors re-evaluate instantly when shellValues changes.

Runtime Theme Reloading

A critical feature of the Omarchy Color singleton is hot-reloading of user customizations without restarting the shell. The singleton maintains a userShellFile watcher (lines 39–53) that monitors ~/.config/omarchy/shell.toml for modifications.

When the file changes:

  • The reload() method triggers
  • userShellValues reparses from disk
  • The merge logic recomputes shellValues
  • All QML bindings automatically refresh

This architecture enables users to tweak colors in real-time using a text editor, seeing immediate results across the entire desktop environment.

Practical Usage Examples

Basic Surface Colors

Reference predefined surface colors directly from the singleton:

import "Commons/Color.qml" as Color

Rectangle {
    color: Color.bar.background
    width: 200; height: 40
    
    Text {
        text: "Omarchy"
        color: Color.bar.text
        anchors.centerIn: parent
    }
}

Semi-Transparent Compositions

Create overlays using the composition helper:

Item {
    width: 100; height: 100
    
    Rectangle {
        anchors.fill: parent
        color: Color.composed(
            "popup.overlay", 
            "popup.overlay-alpha",
            Color.background, 
            0.6
        )
    }
}

Custom Theme Tokens

Access arbitrary values from shell.toml with fallback protection:

Item {
    property color highlight: Color.pick("custom.highlight", "#ff00ff")
    // Changing ~/.config/omarchy/shell.toml updates this instantly:
    // [custom]
    // highlight = "#00ff00"
}

Summary

  • The Color singleton in shell/Commons/Color.qml is a pragma Singleton type that provides global access to theme-aware colors without multiple instantiation.
  • Foundational colors (foreground, background, accent, urgent, muted) initialize with fallbacks on lines 19–23, then populate from colors.toml via the colorsFile loader.
  • Surface objects like bar, popup, and tooltip expose read-only color properties derived through helper functions including pick(), pickAlpha(), flatColor(), and composed().
  • The reactive pipeline loads theme files, parses TOML structures, and merges user overrides into shellValues, causing automatic UI updates when configurations change.
  • Runtime reloading watches ~/.config/omarchy/shell.toml (lines 39–53), enabling instant theme customization without shell restarts.

Frequently Asked Questions

How do I import the Color singleton in my Omarchy widget?

Import the singleton using import "Commons/Color.qml" as Color at the top of your QML file. Because it is registered as a singleton with pragma Singleton, you do not create an instance with Color {}; instead, access properties directly through the type name like Color.bar.background or Color.accent.

What happens if a color is missing from the theme configuration?

The singleton provides fallback values for all foundational colors (lines 19–23) and the pick() function accepts a fallback parameter. If a requested token does not exist in the merged shellValues dictionary, the system returns the fallback hex code specified in the function call or the default palette values.

Can I modify colors without restarting Omarchy?

Yes. The singleton monitors ~/.config/omarchy/shell.toml through the userShellFile watcher (lines 39–53). When you save changes to this file, the singleton automatically reparses the configuration, remerges the values, and updates all bound properties throughout the UI instantly.

Where are the color values actually stored?

Color values exist in three tiers: the theme files (themes/*/colors.toml and themes/*/shell.toml), the user override file (~/.config/omarchy/shell.toml), and the reactive shellValues property within the singleton. The singleton merges these sources at runtime, with user values taking precedence over theme defaults.

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 →