qs.Ui — Omarchy shell UI kit
Reusable QML components for omarchy-shell's built-in panels and any
third-party plugin that wants a consistent look and behaviour. Every
type is exposed under the qs.Ui module via the qmldir next to this
file, so consumers write:
import qs.Ui
Item {
PanelSeparator { foreground: someColor }
PillButton { text: "Click me"; onClicked: ... }
}
Theme tokens (corner radius, accent, focus styling) live on the
qs.Commons.Style singleton; every Ui component already binds to them,
so toggling omarchy style corners <sharp|round> updates every
consumer in one shot.
Visual reference
Run the dev gallery to see every component live:
omarchy-shell-ipc shell summon omarchy.dev-gallery '{}'
The gallery renders the real components (not copies), so it doubles as a smoke test — if a component starts misbehaving the gallery is the fastest place to see it.
Components
Grouped by what they're for, not alphabetically.
Panel surfaces
| Type | Purpose |
|---|---|
KeyboardPanel |
Layer-shell popup with WlrKeyboardFocus.Exclusive. Use for panels summoned from a bar widget that need keyboard focus on map (j/k navigation, inline editors). |
PopupCard |
xdg-popup based panel. Use for click/hover overlays that don't need to steal keyboard focus (calendar, weather flyout). |
Cursor model
| Type | Purpose |
|---|---|
CursorSurface |
Rectangle that paints fill + 1px border when hasCursor is true, and fill alone when current is true. The foundation for "single highlight on screen across keyboard and mouse" rows. Never read containsMouse for visuals — bind hasCursor from the panel's cursor state instead. |
PanelKeyCatcher |
Item that emits semantic signals: moveRequested(dx, dy), activateRequested(), closeRequested(), deleteRequested(), textKey(string). Drop inside a panel root, wire signals to the panel's state mutators. blocked: true freezes everything except — nothing; it's a hard gate for inline editors. |
Interactive primitives
| Type | Purpose |
|---|---|
PillButton |
Rounded button with optional icon + label + tooltip. Has active, hasCursor (keyboard cursor), focusable (Tab-focus with accent ring), bordered (persistent 1px idle border for primary form buttons), and enabled. Hover and keyboard cursor render identically (fill + border) via the shared hot state; Tab-focus uses an accent ring that wins over both. |
CursorPill |
PillButton that participates in a panel's single-cursor model. Adds a hovered(bool) signal so the panel can update its cursor state on mouse enter/leave. Use for DNS-pill / header-pill / segmented-choice patterns. |
ChoiceButton |
A single button in a mutually-exclusive choice group (segmented control). selected uses accent fill+border; focus uses Style.focusBorderColor so keyboard nav reads differently from selection. |
Toggle |
Title + description + switch. Click anywhere on the row to flip; caller updates checked in response. |
TextField |
Single-line input. Inherits from Qt Quick Controls TextField so all of its base API (text, placeholderText, accepted, validator, ...) is available. Adds password: bool, foreground / accent / selectionTint color overrides, and horizontalPadding / verticalPadding size knobs. Focus styling uses Style.focusBorderColor to match Toggle and ChoiceButton. |
PanelActionButton |
22×22 right-edge action button (confirm, forget, unpair). hoverColor swaps between default foreground tint and urgent (red) tint. focusable: true enables Tab-focus with an accent ring — used for the bar settings widget-card row controls. |
PanelSlider |
Volume/progress slider. Drag, click track, or wheel. moved(value) fires per change, released(value) once at end. (Named to avoid colliding with QtQuick.Controls.Slider.) |
WidgetButton |
Bar widget chrome — for the strip itself, not for inside panels. |
Structure
| Type | Purpose |
|---|---|
PanelSeparator |
1px alpha rule between sections. strength tweaks opacity. |
PanelSectionHeader |
Small-bold label that introduces a section ("DNS provider", "Wi-Fi networks"). |
PanelToolTip |
Styled wrapper around Qt's ToolTip. Drop inside the hovered item and bind visible to the hover state. Use property names panelForeground/panelBackground (not foreground/background) to avoid clashing with ToolTip's built-ins. |
Theme integration
qs.Commons.Style exposes:
cornerRadius— mirrors~/.local/state/omarchy/toggles/quickshell-menu.json, hot-reloadedfocusBorderColor,focusFillColor,focusBorderWidth— derived fromColor.accenthotFill— the standard hover/cursor tint (foreground at 0.12 alpha)
qs.Commons.Color exposes the foundational palette plus per-surface
roles loaded from the theme's colors.toml + shell.toml. Components
in this kit default-bind to Color.foreground / Color.accent /
Color.background so a caller with no explicit theme just works.
Conventions
- Components are stateless about the values they display. They emit signals and let the caller mutate. Don't bake panel-specific state machines into kit components.
- Mouse hover and keyboard cursor should converge on a single visual
state. Bind
hasCursorfrom the panel root; have hover handlers update the same root state viaonHovered. - Property names follow the underlying QML type's convention. Where a
clash is unavoidable (
Toggle/ToolTipalready definebackgroundin QQC), the kit uses apanel*prefix.
Adding a new component
- Write the QML file in this directory.
- Add a line to
qmldir:TypeName 1.0 TypeName.qml. - Add a live demo section to
plugins/dev-gallery/GalleryPanel.qmlusing the real component — copy/paste reimplementations defeat the smoke-test value of the gallery. - Update the table above.