fix(themes): light themes — readable inactive workspaces + notification hover
All checks were successful
Check / eval (push) Successful in 4m9s

Two hardware-reported contrast bugs, both the same family: surfaces
that fell back to something designed on dark.

1. Inactive workspace numbers unreadable on every light theme.
   - Generated bar: the @muted dim role is floor-gated at only 2.0:1 —
     fine as "dim" on dark, washed out at number size on light. Light
     mode now promotes inactive buttons to @subtext (3.0 floor,
     typically 4-7:1); dark themes keep @muted.
   - boreal-dawn + kiln-clay whole-swaps: same @muted -> @subtext
     (6.4:1 / 6.6:1).
   - summer-day whole-swap: had NO button color rule at all — the
     container's @fg does not inherit into GTK buttons, so the numbers
     took the GTK default over the cream chip. Explicit @fg_dim
     (= the palette's subtext, 4.0:1) added.
   - executive-ivory already used @subtext; untouched.

2. Notification popup unreadable on hover (bg turns dark, text stays
   dark). Root cause found by probing, not guessing: the popup body is
   one big GTK button (.notification-default-action) and swaync's
   DEFAULT stylesheet — still loaded under our style.css — hovers it to
   rgb(56,56,56). Our sheet never overrode that node (only the
   control-center row), so the default bled through; on light themes
   dark @text sat on it. Now the body + action buttons + close button
   are re-pinned to the palette's alpha(@text) tint construction —
   mode-proof by construction. The first fix attempt (row-level hover)
   was probed, SHOWN insufficient, and replaced.

Verification: V2. nix flake check --no-build, theme-wholeswap,
theme-contrast green. §3 evidence viewed: a purpose-built VM hover
probe under summer-day (notify-send -u critical, cursor parked ON the
popup, screenshot) — before: dark card, drowned text (reproduces the
report exactly, /nix/store/q1qf67fl...notify-shot-summer-day); after:
cream card + faint tint, fully readable (shot-notify2). Bar renders
verified under catppuccin-latte (generated light path incl. the mode
branch) and summer-day (edited whole-swap). An inactive-number visual
needs two live workspaces — VM shows one — so the on-hardware
confirmation is queued (HARDWARE-QUEUE light-theme refits entry);
the ratio change itself is machine-checked.

Bookkeeping riding along: BACKLOG #152 deleted (shipped in d79ad35).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 13:27:43 +01:00
parent d79ad3517e
commit 8e8a142465
7 changed files with 67 additions and 27 deletions

View File

@@ -33,24 +33,6 @@ flake pin are different root causes even when they show up on the same machine.
## NEXT ## NEXT
### 152. Generated waybar polish: bar height vs workspace highlighter, right-edge icon spacing
Bernardo, 2026-07-17. Two visual fixes to the **generated** bar (the one
every theme without a `waybar.jsonc`/`waybar.css` whole-swap gets — the
4 whole-swap themes are out of scope unless the parity rule drags them in):
- **Bar height:** make the bar a bit taller so the selected-workspace
highlighter no longer renders taller than the bar itself (today it
visually overflows/clips the bar edge).
- **Right-module spacing:** the icon distances on the right cluster look
uneven — e.g. the power button sits too close to the screen edge; give
the cluster consistent inter-icon spacing and sane end padding.
This is §3 visual-protocol work (agent/THEME-DESIGN.md + VERIFICATION §3):
before/after screenshots, two themes (one generated palette + one
whole-swap for the no-regression side), scripted checks first, actually
view the captures. Waybar CSS ↔ tray-menu gotchas in agent/MEMORY.md
apply (never scope `*`).
### 151. `[human]` v1 launch plan — devise with Bernardo (includes the GitHub move) ### 151. `[human]` v1 launch plan — devise with Bernardo (includes the GitHub move)
Bernardo, 2026-07-17: before tagging v1, sit down and devise the launch plan. Bernardo, 2026-07-17: before tagging v1, sit down and devise the launch plan.

View File

@@ -42,6 +42,14 @@ Everything else below stays open; order is convenience, not a gate.
four (the `*`-reset counter-rules). If a VM-rendered preview looks four (the `*`-reset counter-rules). If a VM-rendered preview looks
off in the theme-picker grid next to the hardware-captured ones, off in the theme-picker grid next to the hardware-captured ones,
recapture that preview on hardware into themes/<slug>/preview.png. recapture that preview on hardware into themes/<slug>/preview.png.
- [ ] **Light-theme contrast refits (2026-07-18 report)** — under any
light theme: (1) with two workspaces open, the inactive number is
readable on the bar (subtext, not the washed-out grey); (2) hover a
notification popup — background stays the theme's light card with a
faint tint, text readable (the swaync default's dark
.notification-default-action hover is now overridden); close
button matches the palette. VM-verified with a hover probe under
summer-day; this is the on-hardware confirmation of the report.
- [ ] **Auto-theme pair flip on hardware** — set - [ ] **Auto-theme pair flip on hardware** — set
`settings.autoTheme.{day,night}` to one of the new pairs (e.g. `settings.autoTheme.{day,night}` to one of the new pairs (e.g.
kiln-clay / kiln), force sunrise/sunset around the current time, kiln-clay / kiln), force sunrise/sunset around the current time,

View File

@@ -71,12 +71,47 @@ in
font-size: ${toString t.fonts.size}pt; font-size: ${toString t.fonts.size}pt;
} }
.control-center .notification-row:focus, /* Unscoped on purpose: the floating popup wraps .notification in
.control-center .notification-row:hover { a .notification-row too, and swaync's DEFAULT stylesheet (still
loaded underneath this one) paints that row dark on hover on
light themes the theme's dark @text then sat on the default's
dark hover, unreadable. The same alpha(@text) tint as
everywhere else overrides it in both surfaces. */
.notification-row:focus,
.notification-row:hover {
background: alpha(@text, 0.1); background: alpha(@text, 0.1);
border-radius: ${r}px; border-radius: ${r}px;
} }
/* The REAL popup-hover culprit: the notification body is one big
GTK button (.notification-default-action), and the default
sheet hovers IT to rgb(56,56,56) on light themes that put
dark @text on a dark chip (unreadable, hardware report
2026-07-18). Re-pin body + action buttons to the palette's
tint construction; hover stays a tint of @text, so it works
in both modes by construction. */
.notification-default-action,
.notification-action {
background: transparent;
color: @text;
}
.notification-default-action:hover,
.notification-action:hover {
background: alpha(@text, 0.08);
}
/* Same default-stylesheet leak: swaync ships a dark close-button
chip; re-pin both ends to the palette's tint construction. */
.close-button {
background: alpha(@text, 0.1);
color: @text;
border-radius: ${r}px;
}
.close-button:hover {
background: alpha(@text, 0.2);
color: @text;
}
.widget-title { .widget-title {
color: @text; color: @text;
font-weight: bold; font-weight: bold;

View File

@@ -426,10 +426,12 @@ let
border-radius: ${toString t.ui.rounding}px; border-radius: ${toString t.ui.rounding}px;
} }
/* Dim states use the palette's @muted role: since item 28b it is /* Dim states use the palette's @muted role on DARK themes: since
floor-guaranteed legible on @base in every theme (muted/base >= item 28b it is floor-guaranteed legible on @base (muted/base >=
2.0, gated by tools/check-theme-contrast.py) the palettes that 2.0, gated by tools/check-theme-contrast.py). On LIGHT themes that
once made it vanish (gruvbox mutedbase, item 27) were retuned. 2.0-floor grey washes out at number size inactive workspace
numbers were unreadable on every light palette so light mode
promotes them to @subtext (>= 3.0 floor, typically 4-7:1).
Secondary-but-not-dim stays alpha(@text, 0.85). */ Secondary-but-not-dim stays alpha(@text, 0.85). */
/* 3px vertical margin insets the active pill INSIDE the bar without /* 3px vertical margin insets the active pill INSIDE the bar without
it the filled button spans the bar's full inner height and visually it the filled button spans the bar's full inner height and visually
@@ -437,7 +439,7 @@ let
#workspaces button { #workspaces button {
padding: 0 8px; padding: 0 8px;
margin: 3px 0; margin: 3px 0;
color: @muted; color: ${if t.mode == "light" then "@subtext" else "@muted"};
border-radius: ${toString t.ui.rounding}px; border-radius: ${toString t.ui.rounding}px;
} }

View File

@@ -120,7 +120,9 @@ window#waybar {
#workspaces { padding: 0 2px; } #workspaces { padding: 0 2px; }
#workspaces button { #workspaces button {
color: @muted; /* @subtext, not @muted: on a light pill the 2.0-floor grey washes out
at number size — inactive workspaces must stay readable (6.4:1). */
color: @subtext;
padding: 0 9px; padding: 0 9px;
margin: 3px 1px; margin: 3px 1px;
border-radius: 9px; border-radius: 9px;

View File

@@ -117,7 +117,9 @@ window#waybar {
#workspaces { padding: 0 2px; } #workspaces { padding: 0 2px; }
#workspaces button { #workspaces button {
color: @muted; /* @subtext, not @muted: on the cream plank the 2.0-floor grey washes
out at number size — inactive workspaces must stay readable (6.6:1). */
color: @subtext;
padding: 0 9px; padding: 0 9px;
margin: 3px 1px; margin: 3px 1px;
border-radius: 6px; border-radius: 6px;

View File

@@ -18,6 +18,7 @@
@define-color grey0 #a6b0a0; @define-color grey0 #a6b0a0;
@define-color grey1 #939f91; /* overlay */ @define-color grey1 #939f91; /* overlay */
@define-color grey2 #829181; /* muted */ @define-color grey2 #829181; /* muted */
@define-color fg_dim #6e828a; /* subtext */
/* margin: top right bottom left */ /* margin: top right bottom left */
/* Spacing outside the element */ /* Spacing outside the element */
@@ -79,6 +80,14 @@ window#waybar {
padding: 0px; padding: 0px;
} }
/* Inactive workspace numbers: without an explicit rule the GTK default
button color leaks in (the container's @fg does NOT inherit into
buttons) and the numbers vanish on the cream chip. @fg_dim = the
palette's subtext (4.0:1) — dim but readable. */
#workspaces button {
color: @fg_dim;
}
/* The power-profile speedometer glyph renders small in its em box. */ /* The power-profile speedometer glyph renders small in its em box. */
#custom-powerprofile { #custom-powerprofile {
font-size: 20px; font-size: 20px;