What Are the Standardized Interactive States for Controls in Omarchy Themes?
Omarchy themes define four standardized interactive states—Normal, Hover/Cursor, Focus, and Selected—that provide consistent visual feedback across all UI controls through reusable tokens in the [controls] section of shell themes.
Omarchy implements a unified theming system that standardizes how buttons, dropdowns, and tab strips respond to user interaction. The standardized interactive states for controls in Omarchy themes are defined in the [controls] section of shell theme configuration files, allowing theme authors to manage appearance globally while ensuring consistent feedback throughout the Qt-based desktop environment.
The Four Standardized Interactive States
According to docs/omarchy-shell.md, the [controls] section specifies four distinct states that every interactive element can reference. Each state exposes five token types: color, fill alpha, border, border width, and border alpha.
Normal (Idle State)
The default appearance when no interaction occurs. This state uses normal-color, normal-fill-alpha, normal-border, normal-border-width, and normal-border-alpha tokens to define the control's resting visual state.
Hover / Cursor State
Triggered when the cursor enters the control area. This state employs hover-cursor-color, hover-cursor-fill-alpha, hover-cursor-border, hover-cursor-border-width, and hover-cursor-border-alpha tokens to provide immediate visual feedback during mouse navigation.
Focus State
Indicates Qt activeFocus when a control has keyboard focus. Governed by focus-color, focus-fill-alpha, focus-border, focus-border-width, and focus-border-alpha tokens, this state ensures accessibility by clearly identifying which element receives keyboard input.
Selected State
A persistent selection state for toggled or active items. Managed through selected-color, selected-fill-alpha, selected-border, selected-border-width, and selected-border-alpha tokens, this state maintains visual indication of ongoing selection.
Token Architecture and Value Types
Each state token supports specific value types that determine rendering behavior:
- Color tokens: Reference palette roles (
foreground,accent,urgent,background) or raw hex strings - Fill alpha: Controls background opacity independently of the color value
- Border tokens: Accept solid colors or gradient definitions using the syntax
rgba() rgba() angle - Border width: Integer values where
0disables the border while preserving fill colors - Border alpha: Controls border opacity separately from fill alpha
Configuring States in Theme Files
Themes implement these states in shell.toml or theme.toml files located in paths such as default/themes/gravitas/shell.toml. The configuration maps state tokens to palette roles or specific color values:
[controls]
# Map states to palette roles
hover-cursor-color = "accent"
focus-color = "accent"
selected-color = "foreground"
# Gradient borders for interactive states
hover-cursor-border = "rgba(33ccffee) rgba(00ff99ee) 45deg"
focus-border = "rgba(33ccffee) rgba(00ff99ee) 45deg"
# Solid border with disabled state example
selected-border = "rgba(00ff99ff) 1px solid"
selected-border-width = 0
QML Integration via Style Helpers
The shell/Ui/Style.qml file provides runtime resolution through helper functions that map token names to actual colors. QML components consume these tokens via qs.Style and qs.Border helpers:
import QtQuick 2.15
import qs 1.0
Rectangle {
id: control
color: Style.controlColor("normal")
border.color: Style.controlBorder("normal")
border.width: Style.controlBorderWidth("normal")
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: control.color = Style.controlColor("hover-cursor")
onExited: control.color = Style.controlColor("normal")
onPressed: control.color = Style.controlColor("selected")
}
}
The shell/Ui/Border.qml component handles border-specific rendering, supporting both solid colors and gradient definitions specified in the theme tokens.
Scope Limitations for Specialized Surfaces
While [controls] governs shared button and dropdown chrome, specific surfaces maintain independent token sets. The [menu], [launcher], and [image-picker] sections define their own selected-* tokens and do not inherit from [controls]. This architectural decision allows specialized styling for distinct UI contexts while keeping the shared control chrome consistent.
Summary
- Omarchy themes standardize four interactive states—Normal, Hover/Cursor, Focus, and Selected—through the
[controls]section in shell themes as documented indocs/omarchy-shell.md. - Each state exposes color, fill alpha, border, border width, and border alpha tokens for granular visual control.
- Theme authors configure these in
shell.tomlfiles, referencing palette roles or raw values, with full support for gradient borders. - QML components consume tokens via
Style.controlColor()andStyle.controlBorder()helpers implemented inshell/Ui/Style.qml. - Specialized surfaces like menus and launchers maintain separate token sets and do not inherit
[controls]definitions, allowing context-specific styling.
Frequently Asked Questions
How do I disable borders while keeping fill colors in Omarchy themes?
Set the specific state's border width token to 0. For example, selected-border-width = 0 removes the selected state's border while preserving the selected-color fill and its alpha value defined in your shell.toml.
Can Omarchy theme borders use gradients instead of solid colors?
Yes. Border tokens accept gradient definitions using the syntax rgba() rgba() angle. For instance, hover-cursor-border = "rgba(33ccffee) rgba(00ff99ee) 45deg" creates a 45-degree gradient between two semi-transparent colors, processed by the shell/Ui/Border.qml helper.
Why don't menu selections use the [controls] selected state?
Surfaces like [menu], [launcher], and [image-picker] define their own selected-* tokens independent of [controls]. According to the Omarchy shell documentation, these specialized UI contexts require distinct visual treatment and therefore do not inherit the shared control chrome tokens, allowing separate customization for menus versus buttons.
What is the difference between fill alpha and border alpha tokens?
Fill alpha controls the opacity of the state's background color (resolved by Style.controlColor()), while border alpha controls the opacity of the border stroke specifically (resolved by Style.controlBorder()). This separation allows themes to create controls with opaque borders but semi-transparent backgrounds, or vice versa, without modifying the underlying color values.
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 →