Predicate chips and graph are illegible (dark-on-dark) under OS-enforced prefers-color-scheme: dark #72

Closed
opened 2026-06-30 11:51:44 +00:00 by anuna-02 · 0 comments
anuna-02 commented 2026-06-30 11:51:44 +00:00 (Migrated from codeberg.org)

Summary

When the user's OS enforces dark mode (prefers-color-scheme: dark), two zetl UI elements become illegible (dark-on-dark):

  1. Predicate chips (.zetl-edge-predicate) — near-black text/background.
  2. The graph (/_graph and the docked mini-widget) — node labels and surfaces are very low-contrast.

The rest of the page background follows OS dark mode (via the compiled base theme), but these two components are styled with light-only colours, so they end up black-on-dark.

Affects zetl 0.9.2, default theme.

Root cause

zetl's own component styles define light-only colours and have no @media (prefers-color-scheme: dark) override (the page is also pinned to data-theme="default"), while the base theme background does react to OS dark mode — so only these custom components fall out of sync.

Predicate chips

themes/default/static/shell.css (.zetl-edge-predicate, ~line 171) hardcodes black:

.zetl-edge-predicate {
  background: rgba(0,0,0,0.05);
  color: rgba(0,0,0,0.55);          /* near-black text → invisible on a dark bg */
  border: 1px solid rgba(0,0,0,0.10);
}

There is no prefers-color-scheme: dark rule anywhere in shell.css.

Graph

themes/default/static/graph-boot.js nodeReducer/edgeReducer read --zetl-graph-* custom properties via getComputedStyle (refreshed on prefers-color-scheme change — see _graph.html), and the widget chrome uses light fallbacks:

background: var(--zetl-shell-surface, rgba(0,0,0,0.05));
border-top: 1px solid var(--zetl-shell-border, rgba(0,0,0,0.1));
outline: 2px solid var(--zetl-graph-node-active, #f59e0b);

The --zetl-graph-* / --zetl-shell-* tokens are only defined for the light theme, so under OS dark mode the labels/nodes/surfaces keep their light values and lose contrast. (The reducers already re-render on prefers-color-scheme change — there's just nothing dark to read.)

Reproduction

  1. Set the OS to dark mode (or a forced-dark browser setting).
  2. Open any deployed zetl build page (e.g. https://anti-violence-tech.pages.dev/research/datafication-of-metoo/).
  3. The predicate chips (e.g. Studies, Related to) render as dark text on a dark background.
  4. Open /_graph — node labels and the widget surface are very low contrast.

Expected

Predicate chips and graph labels/surfaces should remain legible under prefers-color-scheme: dark, matching the dark page background.

Suggested fix

Add dark-mode values for the custom components — either:

  • a @media (prefers-color-scheme: dark) block in shell.css overriding .zetl-edge-predicate colours and the --zetl-shell-* / --zetl-graph-* token defaults, and/or
  • derive these colours from theme tokens instead of hardcoded black — e.g. color: color-mix(in srgb, currentColor 60%, transparent) / daisyUI base-content variables — so they track whatever theme/color-scheme is active automatically.

A regression check could assert the chip color/background and the --zetl-graph-* tokens resolve to a dark-mode-appropriate value when prefers-color-scheme: dark.

## Summary When the user's OS enforces dark mode (`prefers-color-scheme: dark`), two zetl UI elements become **illegible (dark-on-dark)**: 1. **Predicate chips** (`.zetl-edge-predicate`) — near-black text/background. 2. **The graph** (`/_graph` and the docked mini-widget) — node labels and surfaces are very low-contrast. The rest of the page background follows OS dark mode (via the compiled base theme), but these two components are styled with **light-only colours**, so they end up black-on-dark. Affects **zetl 0.9.2**, default theme. ## Root cause zetl's own component styles define light-only colours and have **no `@media (prefers-color-scheme: dark)` override** (the page is also pinned to `data-theme="default"`), while the base theme background does react to OS dark mode — so only these custom components fall out of sync. ### Predicate chips `themes/default/static/shell.css` (`.zetl-edge-predicate`, ~line 171) hardcodes black: ```css .zetl-edge-predicate { background: rgba(0,0,0,0.05); color: rgba(0,0,0,0.55); /* near-black text → invisible on a dark bg */ border: 1px solid rgba(0,0,0,0.10); } ``` There is no `prefers-color-scheme: dark` rule anywhere in `shell.css`. ### Graph `themes/default/static/graph-boot.js` `nodeReducer`/`edgeReducer` read `--zetl-graph-*` custom properties via `getComputedStyle` (refreshed on `prefers-color-scheme` change — see `_graph.html`), and the widget chrome uses light fallbacks: ```js background: var(--zetl-shell-surface, rgba(0,0,0,0.05)); border-top: 1px solid var(--zetl-shell-border, rgba(0,0,0,0.1)); outline: 2px solid var(--zetl-graph-node-active, #f59e0b); ``` The `--zetl-graph-*` / `--zetl-shell-*` tokens are only defined for the light theme, so under OS dark mode the labels/nodes/surfaces keep their light values and lose contrast. (The reducers already re-render on `prefers-color-scheme` change — there's just nothing dark to read.) ## Reproduction 1. Set the OS to dark mode (or a forced-dark browser setting). 2. Open any deployed `zetl build` page (e.g. https://anti-violence-tech.pages.dev/research/datafication-of-metoo/). 3. The `predicate` chips (e.g. `Studies`, `Related to`) render as dark text on a dark background. 4. Open `/_graph` — node labels and the widget surface are very low contrast. ## Expected Predicate chips and graph labels/surfaces should remain legible under `prefers-color-scheme: dark`, matching the dark page background. ## Suggested fix Add dark-mode values for the custom components — either: - a `@media (prefers-color-scheme: dark)` block in `shell.css` overriding `.zetl-edge-predicate` colours and the `--zetl-shell-*` / `--zetl-graph-*` token defaults, and/or - derive these colours from theme tokens instead of hardcoded black — e.g. `color: color-mix(in srgb, currentColor 60%, transparent)` / daisyUI `base-content` variables — so they track whatever theme/`color-scheme` is active automatically. A regression check could assert the chip `color`/`background` and the `--zetl-graph-*` tokens resolve to a dark-mode-appropriate value when `prefers-color-scheme: dark`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
anuna-research/zetl#72
No description provided.