System details
Omarchy 4.0.2, Arch, Hyprland. Not hardware-specific — this lives in
default/themed/*.tpl and reproduces from the shipped theme files alone.
Confirmed present at quattro HEAD.
What's wrong?
default/themed/pi.json.tpl derives dimmed text with a fixed blend toward the
background:
34: "mutedText": "{{ mix foreground background 34% }}",
35: "dimText": "{{ mix foreground background 52% }}"
dimText isn't decoration — the same file maps it to text people read:
46: "dim": "dimText",
48: "thinkingText": "dimText",
73: "syntaxComment": "dimText",
A fixed percentage assumes every theme has the same foreground-to-background
headroom, and they don't. Mixing toward the background also drops luminance
non-linearly, so the same 52% costs far more contrast on a low-headroom theme
than a high-headroom one.
Measured across the 22 themes in themes/ that ship a colors.toml,
19 of 22 land below the WCAG AA 4.5:1 minimum for normal text, and 6 fall
below even the 3:1 large-text floor:
| theme |
dimText @ 52% |
@ 25% |
| rose-pine |
2.17:1 |
3.70:1 |
| catppuccin-latte |
2.21:1 |
3.85:1 |
| tokyo-night |
2.89:1 |
5.15:1 |
| everforest |
2.93:1 |
4.91:1 |
| lupine |
2.98:1 |
6.92:1 |
| gruvbox |
3.03:1 |
5.29:1 |
| miasma |
3.14:1 |
5.59:1 |
| matte-black |
3.21:1 |
6.10:1 |
| osaka-jade |
3.22:1 |
5.96:1 |
| flexoki-light |
3.36:1 |
8.59:1 |
| nord |
3.41:1 |
6.00:1 |
| solitude |
3.52:1 |
6.93:1 |
| kanagawa |
3.65:1 |
6.92:1 |
| ristretto |
3.66:1 |
6.83:1 |
| catppuccin |
3.66:1 |
6.95:1 |
| white |
3.69:1 |
10.37:1 |
| ethereal |
3.80:1 |
7.87:1 |
| lumon |
3.86:1 |
7.41:1 |
| retro-82 |
3.92:1 |
7.90:1 |
| hackerman |
4.61:1 |
9.91:1 |
| vantablack |
4.89:1 |
11.42:1 |
| last-horizon |
4.89:1 |
10.77:1 |
The same pattern appears in three other text-bearing tokens:
| template |
token |
mix |
below 4.5:1 |
worst |
pi.json.tpl:34 |
mutedText → mdQuote, syntaxPunctuation |
34% |
5/22 |
3.08:1 |
claude.json.tpl:9 |
inactive |
40% |
10/22 |
2.72:1 |
shell.toml.tpl:216 |
placeholder |
34% |
5/22 |
3.08:1 |
Steps to reproduce
Reads only the shipped theme files; changes nothing:
python3 - <<'PY'
import re, glob, os
def lum(h):
h = h.lstrip("#")[:6]
c = [int(h[i:i+2], 16)/255 for i in (0, 2, 4)]
c = [x/12.92 if x <= 0.04045 else ((x+0.055)/1.055)**2.4 for x in c]
return 0.2126*c[0] + 0.7152*c[1] + 0.0722*c[2]
def ratio(a, b):
la, lb = lum(a), lum(b)
return (max(la, lb) + 0.05) / (min(la, lb) + 0.05)
def mix(a, b, amt):
a, b = a.lstrip("#"), b.lstrip("#")
return "#" + "".join(f"{int(int(a[i:i+2],16)*(1-amt)+int(b[i:i+2],16)*amt+0.5):02x}"
for i in (0, 2, 4))
bad = 0
for d in sorted(glob.glob("/usr/share/omarchy/themes/*")):
f = os.path.join(d, "colors.toml")
if not os.path.exists(f):
continue
t = open(f).read()
m1 = re.search(r'^foreground\s*=\s*"(#\w{6})"', t, re.M)
m2 = re.search(r'^background\s*=\s*"(#\w{6})"', t, re.M)
if not (m1 and m2):
continue
fg, bg = m1.group(1), m2.group(1)
r = ratio(mix(fg, bg, 0.52), bg)
bad += r < 4.5
print(f"{os.path.basename(d):20} dimText {mix(fg,bg,0.52)} = {r:5.2f}:1"
f"{' BELOW AA' if r < 4.5 else ''}")
print(f"\n{bad} themes below 4.5:1")
PY
Suggested fix
Lowering the constant helps but does not solve it. At 25%, dimText clears
4.5:1 on 20 of 22 — rose-pine (3.70:1) and catppuccin-latte (3.85:1) still
fail, because their own undimmed foreground is only 6.66:1 and 7.06:1, so any
meaningful dimming drops under. Going low enough to save those two (~15%) makes
dimText nearly indistinguishable from normal text, which defeats the token.
The robust fix is to solve for a contrast floor instead of a fixed blend:
mix toward the background until the result would drop below a target ratio,
then stop. Something like {{ dim foreground background 52% min 4.5 }}. That
keeps dim genuinely dim on themes with headroom, and stops it becoming
unreadable on themes without — without either constraining theme authors'
palettes or hand-tuning a constant per theme.
Happy to send a PR for whichever direction you'd prefer.
System details
Omarchy 4.0.2, Arch, Hyprland. Not hardware-specific — this lives in
default/themed/*.tpland reproduces from the shipped theme files alone.Confirmed present at
quattroHEAD.What's wrong?
default/themed/pi.json.tplderives dimmed text with a fixed blend toward thebackground:
dimTextisn't decoration — the same file maps it to text people read:A fixed percentage assumes every theme has the same foreground-to-background
headroom, and they don't. Mixing toward the background also drops luminance
non-linearly, so the same 52% costs far more contrast on a low-headroom theme
than a high-headroom one.
Measured across the 22 themes in
themes/that ship acolors.toml,19 of 22 land below the WCAG AA 4.5:1 minimum for normal text, and 6 fall
below even the 3:1 large-text floor:
dimText@ 52%The same pattern appears in three other text-bearing tokens:
pi.json.tpl:34mutedText→mdQuote,syntaxPunctuationclaude.json.tpl:9inactiveshell.toml.tpl:216placeholderSteps to reproduce
Reads only the shipped theme files; changes nothing:
Suggested fix
Lowering the constant helps but does not solve it. At 25%,
dimTextclears4.5:1 on 20 of 22 —
rose-pine(3.70:1) andcatppuccin-latte(3.85:1) stillfail, because their own undimmed foreground is only 6.66:1 and 7.06:1, so any
meaningful dimming drops under. Going low enough to save those two (~15%) makes
dimTextnearly indistinguishable from normal text, which defeats the token.The robust fix is to solve for a contrast floor instead of a fixed blend:
mix toward the background until the result would drop below a target ratio,
then stop. Something like
{{ dim foreground background 52% min 4.5 }}. Thatkeeps dim genuinely dim on themes with headroom, and stops it becoming
unreadable on themes without — without either constraining theme authors'
palettes or hand-tuning a constant per theme.
Happy to send a PR for whichever direction you'd prefer.