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 tokensStyle.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:
colors.toml— Base palette definitions (foreground, background, accent, urgent, muted)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.tomlline-by-line - Extracts palette keys:
foreground,background,accent,muted,urgent
parseShell(raw)
- Walks the TOML-style
shell.tomlstructure - 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'sshell.tomluserShellValues— 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
omarchy-theme-set <name>writes the merged theme payload to$HOME/.local/local/state/omarchy/current/theme/- The Color singleton detects the change (via file watcher or IPC hook)
loadColors()andparseShell()re-parse the new filesshellValuesproperty assignment triggers QML binding re-evaluation- 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()andparseShell()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-setwriting to the state directory, triggering property updates and automatic UI refresh - No restart required because QML property bindings re-evaluate when
shellValuesis 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →