# How Shell Theme Tokens (Color, Style, Border) Work in Omarchy QML

> Discover how Omarchy QML's Color Style and Border theme tokens enable automatic visual updates from TOML configuration simplifying UI theming without hardcoding.

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

---

**Omarchy’s shell UI uses three QML modules—Color.qml, Style.qml, and Border.qml—to provide theme tokens that propagate visual changes automatically from TOML configuration without hard-coding values in components.**

The Omarchy desktop environment by Basecamp implements a fully theme-driven interface using Qt Modeling Language (QML). Instead of embedding literal color values or border widths directly into UI components, the shell references abstract **shell theme tokens** defined in three core modules. This architecture ensures that modifying a single entry in the [`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml) configuration instantly updates the entire interface.

## The Three Core Theme Token Modules

Omarchy separates visual concerns into three distinct QML singletons located in `shell/Commons/`. Each module handles a specific layer of the theming stack, from raw hex values to calculated interactive states.

### Color.qml – The Palette Registry

`Color.qml` serves as the central registry for every color value used throughout the shell. At startup, it ingests raw definitions from the TOML configuration file and exposes them as typed properties such as `Color.accent`, `Color.menu.background`, or `Color.foreground`. These properties are organized by semantic section (menus, panels, bars) so that components reference intent rather than specific values.

### Style.qml – State-Derived Color Logic

`Style.qml` provides pure functions that calculate colors for interactive states—hover, selected, disabled, and active—based on the raw palette from `Color.qml`. Rather than hard-coding transparency or shading math in individual components, the shell uses helpers like `Style.hoverFillFor(base, accent)` and `Style.selectedFillFor(base, accent)` to generate consistent visual feedback across all UI elements.

### Border.qml – Border Specifications

`Border.qml` constructs reusable border specifications containing width, color, and radius values. Functions such as `Border.controlSpec(state, bg, accent)` and `Border.surfaceSpec(section, element, baseColor, width)` return structured objects that QML elements consume via a `borderSpec` property. This ensures that borders remain consistent with the current theme state while supporting contextual variations for controls, surfaces, and flat decorative lines.

## Loading Tokens at Startup

When the shell initializes, it reads the user’s [`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml) file and hydrates the token modules before any UI elements render. In `shell/shell.qml`, the initialization sequence calls:

```qml
// shell/shell.qml (lines 884-885)
Color.loadColors(colorsRaw)
Color.loadShell(shellRaw)

```

The `loadColors()` method populates the base palette, while `loadShell()` applies shell-specific overrides for menus, panels, and bars defined in the configuration. Once loaded, these values remain immutable until the next session, ensuring consistent rendering performance.

## Using Style Helpers for Interactive States

Components never calculate hover or selection colors manually. Instead, they invoke `Style` helpers that blend the current foreground with the accent color. For example, in the power panel plugin:

```qml
// plugins/panels/power/Panel.qml (line 292)
color: Style.hoverFillFor(root.bar.foreground, Color.accent)

```

`Style.hoverFillFor()` returns a semi-transparent blend of the `base` (foreground) and `accent` colors, creating the standard highlight effect. For selected states, developers use `Style.selectedFillFor(base, accent)`, while `Style.normalBorderFor(color)` generates appropriate outline colors for disabled or inactive elements.

## Defining Borders with Border Specifications

Borders in Omarchy are not primitive values but structured specifications generated by `Border.qml`. The weather panel demonstrates the typical pattern:

```qml
// plugins/panels/weather/Panel.qml (line 1892)
borderSpec: Border.controlSpec("normal", root.bar.foreground, Color.accent)

```

`Border.controlSpec()` accepts a state string (`"normal"`, `"hover"`, or `"selected"`), a background color, and an accent color, returning a complete border definition. For simpler cases, `Border.flat(color, width)` creates a solid line without radius, suitable for dividers or separators. Complex surfaces like lock screens use `Border.surfaceSpec(section, element, baseColor, width)` to pull context-aware values from the theme.

## Complete Implementation Example

The following example demonstrates how a button combines all three token types to react to mouse events without hard-coding any color values:

```qml
Rectangle {
    id: button
    color: Style.hoverFillFor(root.bar.foreground, Color.accent)
    borderSpec: Border.controlSpec("normal", root.bar.foreground, Color.accent)

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        onEntered: button.state = "hover"
        onExited:  button.state = "normal"
    }
}

```

- **Color** supplies the raw palette via `root.bar.foreground` and `Color.accent`.
- **Style** computes the hover background dynamically.
- **Border** provides the correct thickness and color for the current state.

Because every component follows this token pattern, changing `Color.accent` in [`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml) immediately updates every button, panel, and menu border throughout the interface.

## Summary

- **Color.qml** acts as the single source of truth for raw color values loaded from [`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml) at startup (lines 884-885 of `shell.qml`).
- **Style.qml** exposes helper functions like `hoverFillFor()` and `selectedFillFor()` that derive interactive state colors from the base palette.
- **Border.qml** generates structured border specifications via `controlSpec()` and `flat()`, ensuring consistent outlines across the shell.
- Components reference tokens rather than literals, enabling complete theme changes without modifying QML source code.

## Frequently Asked Questions

### How do I change the accent color in Omarchy?

Edit the [`shell.toml`](https://github.com/basecamp/omarchy/blob/main/shell.toml) configuration file and modify the accent value under the `[colors]` section. When the shell restarts, `Color.loadColors()` in `shell/Commons/Color.qml` ingests the new value, and every component referencing `Color.accent` updates automatically.

### What is the difference between Color and Style tokens?

**Color** tokens represent static, raw palette values (e.g., `Color.menu.background`), while **Style** tokens are dynamic functions that calculate colors for specific UI states such as hover or selection (e.g., `Style.hoverFillFor()`). Style helpers always consume Color tokens as inputs.

### Can I customize border widths while using theme tokens?

Yes. Functions like `Border.controlSpec()` and `Border.surfaceSpec()` accept explicit width parameters that override defaults, while still pulling colors from the theme. For fixed-width decorative lines, use `Border.flat(color, width)` to specify exact pixel dimensions.

### Where are theme tokens initialized in the codebase?

Theme tokens initialize in `shell/shell.qml` at lines 884-885, where `Color.loadColors()` and `Color.loadShell()` parse the TOML configuration and populate the `Color`, `Style`, and `Border` singletons before the UI renders.