Skip to content

Commit a4e329d

Browse files
weirdtangentclaude
andauthored
fix(overlay): apply the configured theme on load, and make the clock card legible (#250)
Two bugs and one legibility fix, all in the same path. 1. The theme never applied on initial render. css_block emitted _theme_css() BEFORE OVERLAY_CSS, and both declare :root with identical specificity, so the static defaults won. A kiosk showed its configured accent only until the page reloaded, then silently reverted to the built-in #88C0D0 until some card happened to appear -- because the refresh loop copies the theme onto documentElement, and that runs solely on an overlay CONTENT version change. Swapping the order lets the theme win in the sheet itself, with no JS involved. 2. applyThemeVariables() took the FIRST :root in the document. Once the theme moved to the end (fix 1), the first block became the static defaults -- so every content update copied #88C0D0 onto inline style, and inline beats the sheet. It now reads the last :root, i.e. the theme block. 3. The clock card is the only card rendered without a background plate; it sits directly on the wallpaper. Its title was drawn in the accent colour at 0.95rem and disappeared over a bright photo. Colour alone cannot fix this -- white vanishes over a pale wallpaper just as the accent vanishes over a bright one -- so the title joins the time/date in white and all three get a text-shadow. The accent still marks titles on the backed cards, where it reads fine. Verified on pulse-office over a live camera background: computed --overlay-accent-color is now the configured value on a cold load, the title is legible against bright concrete, and NOW PLAYING renders in the accent. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 45755a9 commit a4e329d

4 files changed

Lines changed: 74 additions & 2 deletions

File tree

assets/overlay/overlay.css

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,29 @@ body {
10181018
color: var(--overlay-accent-color);
10191019
}
10201020

1021+
/* The clock card is the ONLY card rendered without a background plate -- every other
1022+
card carries --ambient or --alert, so its text is read against a known dark scrim.
1023+
The clock sits directly on the wallpaper, which can be any photo at any luminance,
1024+
so colour alone cannot make it legible: the accent disappeared over a bright sunlit
1025+
shot, and white would disappear over a pale one just as badly. The time and date
1026+
only get away with being unshadowed because they are enormous (up to 6.5rem); the
1027+
title is 0.95rem and had no such margin.
1028+
1029+
So: the title joins its siblings in white rather than the accent, and all three get
1030+
a shadow so the card holds up over an arbitrary background. The accent still marks
1031+
titles on the backed cards, where it reads fine. */
1032+
.overlay-card--clock .overlay-card__title {
1033+
color: var(--overlay-text-color, #ffffff);
1034+
}
1035+
1036+
.overlay-card--clock .overlay-card__title,
1037+
.overlay-card--clock .overlay-clock__time,
1038+
.overlay-card--clock .overlay-clock__date {
1039+
text-shadow:
1040+
0 1px 3px rgba(0, 0, 0, 0.85),
1041+
0 0 14px rgba(0, 0, 0, 0.55);
1042+
}
1043+
10211044
/* The clock gets its own family. It is the one thing on screen rendered at 100px+, where a
10221045
face picked for legibility in a 14px badge often reads badly, so the two are chosen
10231046
separately. Falls back to the overlay font when no clock font is set. */

pulse/overlay.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,14 @@ def _add_card(cell: str, markup: str) -> None:
12561256
f'data-info-endpoint="{info_endpoint_attr}"'
12571257
)
12581258

1259-
css_block = f"{_theme_css(theme)}\n{OVERLAY_CSS}"
1259+
# Theme AFTER the static sheet, not before. Both blocks target :root with the same
1260+
# specificity, so whichever comes last wins -- with the theme first, OVERLAY_CSS's
1261+
# :root defaults silently overrode every configured colour and font on initial
1262+
# render. It only ever looked right because the refresh loop copies the theme onto
1263+
# documentElement's inline style, and that runs solely when the overlay CONTENT
1264+
# version changes: so a display showed its configured accent until the next reload,
1265+
# then reverted to the built-in default until some card happened to appear.
1266+
css_block = f"{OVERLAY_CSS}\n{_theme_css(theme)}"
12601267
html_document = f"""<!DOCTYPE html>
12611268
<html lang="en">
12621269
<head>

pulse/overlay_server.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,13 @@ def _render_framed_overlay(self, target_url: str) -> str:
326326
const style = doc.querySelector('style');
327327
if (!style) return;
328328
const text = style.textContent || '';
329-
const rootAt = text.indexOf(':root');
329+
// LAST :root, not the first. The document carries two: the static stylesheet's
330+
// defaults come first, and the theme block is appended after it so it wins the
331+
// cascade (see the css_block ordering in overlay.py). Taking the first one here
332+
// would copy the built-in DEFAULTS onto documentElement's inline style -- and
333+
// inline beats the sheet, so it would defeat the very ordering that makes the
334+
// configured theme apply at all.
335+
const rootAt = text.lastIndexOf(':root');
330336
if (rootAt < 0) return;
331337
const open = text.indexOf('{{', rootAt);
332338
const close = text.indexOf('}}', open);

tests/test_overlay.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,42 @@ def setUp(self) -> None:
3737
show_notification_bar=True,
3838
)
3939

40+
def test_theme_css_wins_over_the_static_stylesheet(self) -> None:
41+
"""The theme block must come AFTER OVERLAY_CSS in the rendered document.
42+
43+
Both declare :root with identical specificity, so source order decides. With
44+
the theme emitted first, OVERLAY_CSS's :root defaults silently overrode every
45+
configured colour on initial render, and the display only picked the real theme
46+
up when the refresh loop next copied it onto documentElement -- which happens
47+
solely on an overlay CONTENT change. Net effect: a kiosk showed its configured
48+
accent until the page reloaded, then reverted to the built-in default until
49+
some card happened to appear.
50+
"""
51+
from pulse.overlay_assets import OVERLAY_CSS
52+
53+
theme = OverlayTheme(
54+
ambient_background="rgba(0,0,0,0.32)",
55+
alert_background="rgba(0,0,0,0.65)",
56+
text_color="#FFFFFF",
57+
accent_color="#ff5c5c",
58+
show_notification_bar=True,
59+
)
60+
html = render_overlay_html(self._snapshot(), theme)
61+
62+
themed = html.rfind("--overlay-accent-color: #ff5c5c")
63+
self.assertGreaterEqual(themed, 0, "themed accent missing from the document")
64+
65+
# The static sheet also defines --overlay-accent-color; the themed one has to
66+
# come later or it loses the cascade.
67+
static_default = OVERLAY_CSS[OVERLAY_CSS.find("--overlay-accent-color") :][:60]
68+
self.assertIn("--overlay-accent-color", static_default)
69+
static_at = html.find("--overlay-accent-color")
70+
self.assertLess(
71+
static_at,
72+
themed,
73+
"static :root default must precede the theme block, otherwise it wins",
74+
)
75+
4076
def _snapshot(self, **overrides) -> OverlaySnapshot:
4177
data = {
4278
"version": 1,

0 commit comments

Comments
 (0)