Two findings from an Omarchy field session, one root cause each.
The output resolution reset on every theme change
omarchy-theme-set ends in omarchy-restart-hyprctl, which is hyprctl reload — and a reload drops EVERY runtime hyprctl keyword, our monitor rule for the streamed head included. This crate already documented that fact (it is what restore_heads relies on to re-light an exclusive session's monitors); nothing put the rule back. So the head silently fell to its default mode mid-stream.
Resizing the client window appeared to fix it only because a resize on Linux re-creates the output, which runs set_monitor_rule again.
The Hyprland backend now subscribes to the compositor's event socket and re-applies the rule on configreloaded. Any reload gets it — the operator's own hyprctl reload, omarchy-refresh-config, a theme switch — not just the one that was reported. The watcher is a socket read, not a poll: an idle session costs nothing, and its guard shuts the socket down on teardown so the thread cannot outlive the output (sessions are minted on every mid-stream resize).
The mode only. A reload also undoes topology: exclusive's head disables, but re-disabling them from this watcher risks a permanently dark desk — teardown's own restore_heads runs a reload to re-light them, and nothing orders the two today. Noted in the code where the fix belongs.
The console did not follow the theme, and only half wore it
Two separate causes:
useUiConfig was staleTime: Infinity. The ports on that payload genuinely cannot change without a restart, but the theme can, so the console sat in the old palette until someone reloaded it. Now polled at 2 s (and refetched on focus). Polled and not pushed because the console's SSE stream is a proxy of the host's, and the host reads nothing about themes.
Only accent and mode were carried. The template has rendered background and foreground all along and they were dropped on the floor, which is why a themed button sat on the console's own violet chrome.
All three colours now reach the page, and a [data-omarchy] block in styles.css expands them into every surface with color-mix(in oklab, …) — cards, hovers, borders, the accent surface — plus the three tints of the lens mark, whose fills were hard-coded violet and now follow the brand tokens. One block covers light and dark: :root[data-omarchy] outranks both :root and .dark, and the mixes take their direction from the theme's own background/foreground pair.
--success / --warning / --destructive deliberately do not follow: they encode meaning, and a theme whose accent is red must not leave "delete" and "save" the same colour.
The palette gate
Every ratio was measured, not chosen. web/tools/check-omarchy-palette.mjs reads them back out of the stylesheet, redoes the mixes and asserts WCAG contrast across six shipped themes (Tokyo Night, Gruvbox, Nord, Catppuccin Latte, Rose Pine Dawn, Everforest Light). It runs in postbuild beside check-i18n, because nothing else in the repo can catch an unreadable palette — the mixes resolve in the browser, so the typecheck, the tests and the build all pass on one.
Tuning against that table caught three real defects in the first draft: muted text at 2.96:1 on Catppuccin Latte, button text at 3.92:1 on Rose Pine Dawn, and the lens mark collapsing to 1.05:1 internal separation on Tokyo Night.
Everforest Light is in the table on purpose: its own foreground is 5.2:1 against its own background, which is what caps a derived muted colour at 3.8:1 there. That ceiling is recorded in the stylesheet rather than papered over.
Verification
pf-vdisplay — 273 tests pass on linux-gnu (docker), xcheck.sh linux clippy clean, cargo fmt --check clean. The new is_config_reload test proved non-vacuous by relaxing the matcher to a contains and watching it fail.
web — build + tsc --noEmit + 37 tests + biome all clean. The theme-file validator test proved non-vacuous by dropping the background check; the palette gate proved non-vacuous by re-tuning a ratio.
check-docs-drift.sh — exit 0.
On glass — the trigger chain confirmed on the Omarchy box (omarchy-theme-set → omarchy-restart-hyprctl → hyprctl reload), configreloaded present in the Hyprland 0.56.2 binary, and the event socket found and connectable at exactly the path the new code computes. The built console served the box's live theme end to end over /_auth/ui-config.
Not verified
The re-apply itself. A stream was live on the box and a hyprctl reload would have disrupted it. One hyprctl reload during a stream is the whole check.
Screenshots. Playwright cannot launch on the dev Mac under Node 25 (heap OOM on chromium.launch()), so the palette was validated numerically rather than visually.
Known, left alone
The console still renders its own violet server-side and flips to the theme after hydration, so a light Omarchy theme flashes dark→light. Pre-existing, not introduced here; closing it needs SSR'd theming.
theme-color meta stays #6c5bf3 — the Omarchy webapp window already inherits Chromium's own theming.
Two findings from an Omarchy field session, one root cause each.
## The output resolution reset on every theme change
`omarchy-theme-set` ends in `omarchy-restart-hyprctl`, which is `hyprctl reload` — and **a reload drops EVERY runtime `hyprctl keyword`**, our monitor rule for the streamed head included. This crate already documented that fact (it is what `restore_heads` relies on to re-light an `exclusive` session's monitors); nothing put the rule back. So the head silently fell to its default mode mid-stream.
Resizing the client window appeared to fix it only because a resize on Linux re-creates the output, which runs `set_monitor_rule` again.
The Hyprland backend now subscribes to the compositor's event socket and re-applies the rule on `configreloaded`. Any reload gets it — the operator's own `hyprctl reload`, `omarchy-refresh-config`, a theme switch — not just the one that was reported. The watcher is a socket read, not a poll: an idle session costs nothing, and its guard shuts the socket down on teardown so the thread cannot outlive the output (sessions are minted on every mid-stream resize).
**The mode only.** A reload also undoes `topology: exclusive`'s head disables, but re-disabling them from this watcher risks a permanently dark desk — teardown's own `restore_heads` runs a reload to re-light them, and nothing orders the two today. Noted in the code where the fix belongs.
## The console did not follow the theme, and only half wore it
Two separate causes:
* `useUiConfig` was `staleTime: Infinity`. The ports on that payload genuinely cannot change without a restart, but the theme can, so the console sat in the old palette until someone reloaded it. Now polled at 2 s (and refetched on focus). Polled and not pushed because the console's SSE stream is a proxy of the **host's**, and the host reads nothing about themes.
* Only `accent` and `mode` were carried. The template has rendered `background` and `foreground` all along and they were dropped on the floor, which is why a themed button sat on the console's own violet chrome.
All three colours now reach the page, and a `[data-omarchy]` block in `styles.css` expands them into every surface with `color-mix(in oklab, …)` — cards, hovers, borders, the accent surface — plus the three tints of the lens mark, whose fills were hard-coded violet and now follow the brand tokens. One block covers light and dark: `:root[data-omarchy]` outranks both `:root` and `.dark`, and the mixes take their direction from the theme's own background/foreground pair.
`--success` / `--warning` / `--destructive` deliberately do not follow: they encode meaning, and a theme whose accent is red must not leave "delete" and "save" the same colour.
## The palette gate
Every ratio was measured, not chosen. `web/tools/check-omarchy-palette.mjs` reads them back out of the stylesheet, redoes the mixes and asserts WCAG contrast across six shipped themes (Tokyo Night, Gruvbox, Nord, Catppuccin Latte, Rose Pine Dawn, Everforest Light). It runs in `postbuild` beside `check-i18n`, because nothing else in the repo can catch an unreadable palette — the mixes resolve in the browser, so the typecheck, the tests and the build all pass on one.
Tuning against that table caught three real defects in the first draft: muted text at 2.96:1 on Catppuccin Latte, button text at 3.92:1 on Rose Pine Dawn, and the lens mark collapsing to 1.05:1 internal separation on Tokyo Night.
Everforest Light is in the table on purpose: its own foreground is 5.2:1 against its own background, which is what caps a *derived* muted colour at 3.8:1 there. That ceiling is recorded in the stylesheet rather than papered over.
## Verification
* **pf-vdisplay** — 273 tests pass on `linux-gnu` (docker), `xcheck.sh linux clippy` clean, `cargo fmt --check` clean. The new `is_config_reload` test proved non-vacuous by relaxing the matcher to a `contains` and watching it fail.
* **web** — build + `tsc --noEmit` + 37 tests + biome all clean. The theme-file validator test proved non-vacuous by dropping the background check; the palette gate proved non-vacuous by re-tuning a ratio.
* **`check-docs-drift.sh`** — exit 0.
* **On glass** — the trigger chain confirmed on the Omarchy box (`omarchy-theme-set` → `omarchy-restart-hyprctl` → `hyprctl reload`), `configreloaded` present in the Hyprland 0.56.2 binary, and the event socket found and connectable at exactly the path the new code computes. The built console served the box's live theme end to end over `/_auth/ui-config`.
### Not verified
* **The re-apply itself.** A stream was live on the box and a `hyprctl reload` would have disrupted it. One `hyprctl reload` during a stream is the whole check.
* **Screenshots.** Playwright cannot launch on the dev Mac under Node 25 (heap OOM on `chromium.launch()`), so the palette was validated numerically rather than visually.
### Known, left alone
* The console still renders its own violet server-side and flips to the theme after hydration, so a light Omarchy theme flashes dark→light. Pre-existing, not introduced here; closing it needs SSR'd theming.
* `theme-color` meta stays `#6c5bf3` — the Omarchy webapp window already inherits Chromium's own theming.
Two findings from an Omarchy field session, one root cause each.
## The output resolution reset on every theme change
`omarchy-theme-set` ends in `omarchy-restart-hyprctl`, which is `hyprctl reload`
— and a reload drops EVERY runtime `hyprctl keyword`, our monitor rule for the
streamed head included. This crate already documented that fact (it is what
`restore_heads` relies on to re-light an `exclusive` session's monitors); nothing
put the rule back. So the head silently fell to its default mode mid-stream.
Resizing the client window appeared to fix it only because a resize on Linux
re-creates the output, which runs `set_monitor_rule` again.
The Hyprland backend now subscribes to the compositor's event socket and
re-applies the rule on `configreloaded`. Any reload gets it — the operator's own
`hyprctl reload`, `omarchy-refresh-config`, a theme switch — not just the one
that was reported. The watcher is a socket read, not a poll: an idle session
costs nothing, and its guard shuts the socket down on teardown so the thread
cannot outlive the output (sessions are minted on every mid-stream resize).
The mode only. A reload also undoes `topology: exclusive`'s head disables, but
re-disabling them from this watcher risks a permanently dark desk — teardown's
own `restore_heads` runs a reload to re-light them, and nothing orders the two
today. Noted in the code where the fix belongs.
## The console did not follow the theme, and only half wore it
Two separate causes:
* `useUiConfig` was `staleTime: Infinity`. The ports on that payload genuinely
cannot change without a restart, but the theme can, so the console sat in the
old palette until someone reloaded it. Now polled at 2 s (and refetched on
focus). Polled and not pushed because the console's SSE stream is a proxy of
the HOST's, and the host reads nothing about themes.
* Only `accent` and `mode` were carried. The template has rendered `background`
and `foreground` all along and they were dropped on the floor, which is why a
themed button sat on the console's own violet chrome.
All three colours now reach the page, and a `[data-omarchy]` block in styles.css
expands them into every surface with `color-mix(in oklab, …)` — cards, hovers,
borders, the accent surface — plus the three tints of the lens mark, whose fills
were hard-coded violet and now follow the brand tokens. One block covers light
and dark: `:root[data-omarchy]` outranks both `:root` and `.dark`, and the mixes
take their direction from the theme's own background/foreground pair.
`--success` / `--warning` / `--destructive` deliberately do not follow: they
encode meaning, and a theme whose accent is red must not leave "delete" and
"save" the same colour.
Every ratio was measured, not chosen: `web/tools/check-omarchy-palette.mjs`
reads them back out of the stylesheet, redoes the mixes and asserts WCAG
contrast across six shipped themes (Tokyo Night, Gruvbox, Nord, Catppuccin
Latte, Rose Pine Dawn, Everforest Light). It runs in `postbuild` beside
check-i18n, because nothing else in the repo can catch an unreadable palette —
the mixes resolve in the browser, so the typecheck, the tests and the build all
pass on one. Everforest Light is in that table on purpose: its own foreground is
5.2:1 against its own background, which is what caps a derived muted colour at
3.8:1 there. That ceiling is recorded in the stylesheet.
## Verification
* pf-vdisplay: 273 tests pass on linux-gnu (docker), `xcheck.sh linux clippy`
clean. The new `is_config_reload` test proved non-vacuous by relaxing the
matcher to a `contains` and watching it fail.
* web: build + `tsc --noEmit` + 37 tests + biome all clean. The theme-file
validator test proved non-vacuous by dropping the background check; the
palette gate proved non-vacuous by re-tuning a ratio.
* On glass: the trigger chain confirmed on the Omarchy box (`omarchy-theme-set`
→ `omarchy-restart-hyprctl` → `hyprctl reload`), `configreloaded` present in
the Hyprland 0.56.2 binary, and the event socket found and connectable at
exactly the path the new code computes. The built console served the box's
live theme end to end over `/_auth/ui-config`.
* NOT yet verified on glass: the re-apply itself. A stream was live on the box
and a `hyprctl reload` would have disrupted it.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two findings from an Omarchy field session, one root cause each.
The output resolution reset on every theme change
omarchy-theme-setends inomarchy-restart-hyprctl, which ishyprctl reload— and a reload drops EVERY runtimehyprctl keyword, our monitor rule for the streamed head included. This crate already documented that fact (it is whatrestore_headsrelies on to re-light anexclusivesession's monitors); nothing put the rule back. So the head silently fell to its default mode mid-stream.Resizing the client window appeared to fix it only because a resize on Linux re-creates the output, which runs
set_monitor_ruleagain.The Hyprland backend now subscribes to the compositor's event socket and re-applies the rule on
configreloaded. Any reload gets it — the operator's ownhyprctl reload,omarchy-refresh-config, a theme switch — not just the one that was reported. The watcher is a socket read, not a poll: an idle session costs nothing, and its guard shuts the socket down on teardown so the thread cannot outlive the output (sessions are minted on every mid-stream resize).The mode only. A reload also undoes
topology: exclusive's head disables, but re-disabling them from this watcher risks a permanently dark desk — teardown's ownrestore_headsruns a reload to re-light them, and nothing orders the two today. Noted in the code where the fix belongs.The console did not follow the theme, and only half wore it
Two separate causes:
useUiConfigwasstaleTime: Infinity. The ports on that payload genuinely cannot change without a restart, but the theme can, so the console sat in the old palette until someone reloaded it. Now polled at 2 s (and refetched on focus). Polled and not pushed because the console's SSE stream is a proxy of the host's, and the host reads nothing about themes.accentandmodewere carried. The template has renderedbackgroundandforegroundall along and they were dropped on the floor, which is why a themed button sat on the console's own violet chrome.All three colours now reach the page, and a
[data-omarchy]block instyles.cssexpands them into every surface withcolor-mix(in oklab, …)— cards, hovers, borders, the accent surface — plus the three tints of the lens mark, whose fills were hard-coded violet and now follow the brand tokens. One block covers light and dark::root[data-omarchy]outranks both:rootand.dark, and the mixes take their direction from the theme's own background/foreground pair.--success/--warning/--destructivedeliberately do not follow: they encode meaning, and a theme whose accent is red must not leave "delete" and "save" the same colour.The palette gate
Every ratio was measured, not chosen.
web/tools/check-omarchy-palette.mjsreads them back out of the stylesheet, redoes the mixes and asserts WCAG contrast across six shipped themes (Tokyo Night, Gruvbox, Nord, Catppuccin Latte, Rose Pine Dawn, Everforest Light). It runs inpostbuildbesidecheck-i18n, because nothing else in the repo can catch an unreadable palette — the mixes resolve in the browser, so the typecheck, the tests and the build all pass on one.Tuning against that table caught three real defects in the first draft: muted text at 2.96:1 on Catppuccin Latte, button text at 3.92:1 on Rose Pine Dawn, and the lens mark collapsing to 1.05:1 internal separation on Tokyo Night.
Everforest Light is in the table on purpose: its own foreground is 5.2:1 against its own background, which is what caps a derived muted colour at 3.8:1 there. That ceiling is recorded in the stylesheet rather than papered over.
Verification
linux-gnu(docker),xcheck.sh linux clippyclean,cargo fmt --checkclean. The newis_config_reloadtest proved non-vacuous by relaxing the matcher to acontainsand watching it fail.tsc --noEmit+ 37 tests + biome all clean. The theme-file validator test proved non-vacuous by dropping the background check; the palette gate proved non-vacuous by re-tuning a ratio.check-docs-drift.sh— exit 0.omarchy-theme-set→omarchy-restart-hyprctl→hyprctl reload),configreloadedpresent in the Hyprland 0.56.2 binary, and the event socket found and connectable at exactly the path the new code computes. The built console served the box's live theme end to end over/_auth/ui-config.Not verified
hyprctl reloadwould have disrupted it. Onehyprctl reloadduring a stream is the whole check.chromium.launch()), so the palette was validated numerically rather than visually.Known, left alone
theme-colormeta stays#6c5bf3— the Omarchy webapp window already inherits Chromium's own theming.