AvengeMedia / AvengeMedia/DankMaterialShell
niri: generated urgent-color is isoluminant with active-color and can become perceptually indistinguishable on warm-hued wallpapers
- Dominant language
- QML
- Stars
- 8.1k
- Forks
- 515
- Avg merge
- 1d 8h
- Merged PRs (30d)
- 102
Description
### Compositor
Niri
### Distribution
Fedora
### If Other, please specify
_No response_
### Select your Installation Method
Distro Packaging
### Was this your original Installation method?
Yes
### If no, specify
_No response_
### dms doctor -vC
Output from `dms doctor -v`; `-C` was intentionally omitted because it writes to the clipboard.
Click to expand
```
System Health Check
──────────────────────────────────────
System
● Operating System ····· Fedora Linux 44 (Forty Four)
└─ ID: fedora, Version: 44, Arch: amd64
● Architecture ········· amd64
● Display Server ······· Wayland
└─ WAYLAND_DISPLAY=wayland-1
Versions
● DMS CLI ·············· dms v1.6.1
└─ /usr/bin/dms
● Quickshell ··········· Quickshell 0.3.1 (revision , distributed by Fedora COPR (avengemedia/quickshell))
└─ /usr/bin/qs
● DMS Shell ············ v1.6.1
└─ /run/user/1000/danklinux-shell/d83248ba9119b48a
Installation
● DMS Configuration ···· Found
└─ /run/user/1000/danklinux-shell/d83248ba9119b48a
● shell.qml ············ Present
└─ /run/user/1000/danklinux-shell/d83248ba9119b48a/shell.qml
○ Install Type ········· Unknown
└─ /run/user/1000/danklinux-shell/d83248ba9119b48a
Compositor
● niri ················· 26.04
└─ /usr/bin/niri
○ Active ··············· niri
● Background Blur ······ Supported
└─ Compositor supports ext-background-effect-v1
Quickshell Features
● Polkit ··············· Available
└─ Authentication prompts
● IdleMonitor ·········· Available
└─ Idle detection
● IdleInhibitor ········ Available
└─ Prevent idle/sleep
● ShortcutInhibitor ···· Available
└─ Allow shortcut management (niri)
● BackgroundBlur ······· Available
└─ Background blur API support in Quickshell
Optional Features
● accountsservice ······ Available
└─ User accounts
● power-profiles-daemon Available
└─ Power profile management
● logind ··············· Available
└─ Session management
● cups-pk-helper ······· Available
└─ Printer management
● I2C/DDC ·············· 3 monitor(s) detected
└─ External monitor brightness control
● qt6-imageformats ····· Installed (5 formats)
└─ Formats: WebP, TIFF, GIF, JP2, ICNS (/usr/lib64/qt6/plugins/imageformats)
● kimageformats ········ Installed (4 formats)
└─ Formats: AVIF, HEIF, JXL, EXR (/usr/lib64/qt6/plugins/imageformats)
● Terminal ············· kitty
● Network ·············· NetworkManager
└─ NetworkManager present. Using NM API.
● matugen ·············· Installed
└─ Dynamic theming
● cava ················· Installed
└─ Audio visualizer
○ khal ················· Not installed
└─ Calendar events
● danksearch ··········· Installed
└─ File search
○ dankcalendar ········· Not installed
└─ Calendar app
○ fprintd ·············· Not installed
└─ Fingerprint auth
● adw-gtk3 ············· Installed
└─ GTK3 dynamic theming (user copy)
Config Files
● settings.json ········ Present
└─ ~/.config/DankMaterialShell/settings.json
○ clsettings.json ······ Not yet created
└─ ~/.config/DankMaterialShell/clsettings.json
○ plugin_settings.json · Not yet created
└─ ~/.config/DankMaterialShell/plugin_settings.json
● session.json ········· Present
└─ ~/.local/state/DankMaterialShell/session.json
● dms-colors.json ······ Present
└─ ~/.cache/DankMaterialShell/dms-colors.json
Services
● dms.service ·········· enabled, active
● greetd ··············· enabled
Environment
○ QT_QPA_PLATFORMTHEME · qt6ct
● qt6ct plugin (Qt6) ··· Installed
└─ /usr/lib64/qt6/plugins/platformthemes/libqt6ct.so
○ QT_QPA_PLATFORMTHEME… Not set
○ QS_ICON_THEME ········ Not set
● XDG_MENU_PREFIX ······ plasma- (menu file not found)
└─ Dolphin 'Open with…' dialog may be empty. Ensure /etc/xdg/menus/plasma-applications.menu exists.
→ https://danklinux.com/docs/dankmaterialshell/cli-doctor#xdg-menu-prefix
Fonts
● Normal Font ·········· Rubik
└─ Available via fontconfig
● Monospace Font ······· JetBrainsMono Nerd Font Mono
└─ Available via fontconfig
──────────────────────────────────────
1 warning(s), 36 ok
```
### Description
`quickshell/matugen/templates/niri-colors.kdl` derives all three niri window-state colours from two Material 3 roles:
```kdl
focus-ring {
active-color "{{colors.primary.default.hex}}"
inactive-color "{{colors.outline.default.hex}}"
urgent-color "{{colors.error.default.hex}}"
}
// border { … } and tab-indicator { … } use the same three roles
```
`primary` and `error` come from different tonal palettes but sit at the **same tone** — 80 in dark, 40 in light. That has two consequences for the urgent state.
**1. `active-color` and `urgent-color` are always isoluminant.** Across every source colour and both modes I measured, the WCAG contrast ratio between them is **1.00:1** and ΔL\* < 0.2. Hue is the only channel carrying the urgency signal, on a 2 px border/ring.
**2. When the wallpaper's source hue is near the M3 error hue (~25°), the hue separation collapses too**, and the two states become the same colour. Measured with `matugen 4.2.0`, `scheme-tonal-spot` (the DMS default):
| source colour | mode | `active` (primary) | `urgent` (error) | ΔE2000 | contrast |
|---|---|---|---|---|---|
| `#c62828` red | dark | `#ffb4ac` | `#ffb4ab` | **0.4** | 1.00:1 |
| `#e05a4a` coral | dark | `#ffb4a9` | `#ffb4ab` | **0.7** | 1.00:1 |
| `#8d4a3a` warm brown | dark | `#ffb4a3` | `#ffb4ab` | **2.9** | 1.00:1 |
| `#b3512f` rust | dark | `#ffb59d` | `#ffb4ab` | **5.5** | 1.00:1 |
| `#7d5260` mauve | dark | `#ffb0ca` | `#ffb4ab` | 11.9 | 1.00:1 |
| `#182b3a` blue | dark | `#98ccf9` | `#ffb4ab` | 36.7 | 1.00:1 |
| `#c62828` red | light | `#904a44` | `#ba1a1a` | 12.2 | 1.00:1 |
| `#8d4a3a` warm brown | light | `#904b3b` | `#ba1a1a` | 11.3 | 1.00:1 |
| `#182b3a` blue | light | `#2a638a` | `#ba1a1a` | 45.9 | 1.00:1 |
`#ffb4ac` vs `#ffb4ab` is one step in one channel. On a red or coral wallpaper an urgent window is, in practice, not marked at all: the ring looks identical to the focused window's.
**Changing the matugen scheme in Settings does not rescue it.** Same source colour `#c62828`, dark mode:
| scheme | active | urgent | ΔE2000 | contrast |
|---|---|---|---|---|
| tonal-spot, vibrant, content, fidelity, rainbow | `#ffb4ac` | `#ffb4ab` | 0.4 | 1.00:1 |
| neutral | `#dfbfbc` | `#ffb4ab` | 10.0 | 1.00:1 |
| fruit-salad | `#feabf4` | `#ffb4ab` | 22.6 | 1.00:1 |
| expressive | `#abc7ff` | `#ffb4ab` | 32.0 | 1.00:1 |
| monochrome | `#ffffff` | `#ffb4ab` | 24.5 | 1.70:1 |
Only `monochrome` breaks the 1.00:1 tie, and no scheme is a general fix.
**This is not the light-mode class of bug from #3298.** The template correctly uses `colors.*.default`, so it follows the active mode: rendering it in both modes gives `urgent-color "#ffb4ab"` (dark) and `"#ba1a1a"` (light), with urgent-vs-surface contrast of 10.90:1 and 6.14:1 respectively. Light mode does not reproduce the urgent-vs-background contrast failure, but urgent and active remain isoluminant there as well. The defect here is the *relationship between active and urgent*, in both modes.
**There is no narrow way to change just this colour from DMS.** `Common/settings/SettingsSpec.js` exposes `niriLayoutBorderSize`, `niriLayoutGapsOverride` and `niriLayoutRadiusOverride` — geometry only, no colour keys. DMS already has exactly the kind of setting that would fix this, but only for its own bar widget: `workspaceUrgentColorMode` / `workspaceUrgentCustomColor` (Settings → workspace appearance). The compositor config DMS generates has no equivalent.
The header of the generated file also points at a heavier workaround than niri needs:
```kdl
// Remove `include "dms/colors.kdl"` from your config to override.
```
Since niri 25.11 includes are positional and later settings win, so a user can keep dynamic theming and override only `urgent-color` — but because niri rejects two `layout` nodes in one file, it has to go in a *separate* file included after the DMS one:
```kdl
include "dms/colors.kdl"
include "dms/urgent-override.kdl" // layout { focus-ring { urgent-color "…" } border { … } tab-indicator { … } }
```
Verified with `niri validate` on niri 26.04. It works, but the colour is then a static literal that no longer tracks the wallpaper or the light/dark mode — which is the thing dynamic theming is for.
`matugen/templates/mango-colors.conf` has the same mapping (`focuscolor` = primary, `urgentcolor` = error), so MangoWC inherits the identical collapse.
### Expected Behavior
An urgent window should be **perceptually distinguishable** from the focused window — not merely a different hue at matched luminance, and not the same colour when the wallpaper happens to be warm. Two directions, either would work, without prescribing an implementation:
1. **Derive the urgent colour with a distinguishability constraint** instead of taking `error` verbatim. When the generated `error` is too close to `primary` by some lightness/ΔE criterion, pick a different tone from the error palette (`error_container`, `on_error_container`, or a fixed tone) so the two states separate in luminance as well as hue. This keeps dynamic theming intact and fixes it for everyone with no configuration.
2. **Or expose a narrow urgent-colour setting** for the generated compositor colours, mirroring the existing `workspaceUrgentCustomColor` for workspace pills, so a user can override just this role without giving up the generated `colors.kdl`.
Independently, and much smaller: the generated header's advice could point at the positional-include override rather than removing the include entirely.
### Steps to Reproduce
No wallpaper needed — the source colour is enough:
1. Render the shipped template against a warm source colour in dark mode:
```console
$ matugen --dry-run --json hex color hex '#c62828' | jq -r '.colors.primary.dark.color, .colors.error.dark.color'
#ffb4ac
#ffb4ab
```
Those two values are what the template writes to `active-color` and `urgent-color`.
2. In DMS, set the theme to **dynamic** with a red/coral wallpaper (matugen source colour around hue 25°), so `~/.config/niri/dms/colors.kdl` is regenerated with those values.
3. Open two windows in niri, focus one, and make the other urgent (e.g. an app requesting attention).
4. Compare the urgent window's border/focus-ring against the focused window's.
Observed: both rings are the same colour; the urgent state is not visible.
Expected: the urgent window is clearly distinguishable from the focused one.
With a cool-hued wallpaper (my own case, source `#182b3a`) the hues do differ, but the contrast ratio between the two states is still 1.00:1 — so the state is carried entirely by hue there too.
### Error Messages/Logs
None — the failure is silent. The generated `colors.kdl` is valid and niri accepts it.
### Screenshots/Recordings
_No response_
### Still present on master
Yes. `quickshell/matugen/templates/niri-colors.kdl` on master is unchanged and still maps `urgent-color` to `{{colors.error.default.hex}}` for `focus-ring`, `border` and `tab-indicator`. Installed version is v1.6.1, the current release.
### Ruled out
- Not local config: `~/.config/niri/dms/colors.kdl` is DMS-generated and byte-identical to the shipped template's output.
- Not packaging: the Fedora COPR is upstream's own (`avengemedia`), and the template on master matches the installed one.
- Not an outdated build: v1.6.1 is the latest release, and master is unchanged.
- Not a niri defect: niri applies whatever `urgent-color` it is given, and its positional-include override works as documented.
Contributor guide
Research direction
Start with quickshell/matugen/templates/niri-colors.kdl and reproduce the warm-source output using the supplied matugen command, then inspect Common/settings/SettingsSpec.js and the corresponding MangoWC template if comparing implementation paths. Validate generated configuration with niri validate; done means urgent and active states remain perceptually distinguishable while dynamic light/dark theming continues to work.
Written by the indexing model from the issue text.
Assessment
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100