Files
Tim Bond 22369731e3 UI - Follow the device's light/dark setting by default (#4507)
Currently dark mode is only available to users that have opted-in by
selecting a cookie.  Users that have set their device preference to
dark still see light mode until they've specifically clicked the button
to set the cookie.

Without a css_dark_mode cookie the page now renders data-darkmode="auto",
and the dark styles apply under @media (prefers-color-scheme: dark), so a
device in dark mode gets the dark theme without opting in from the menu.
Pure CSS - no flash of the wrong theme and no JS needed.

The cookie set by the menu toggle still wins either way, so light-mode
devices can choose dark and vice versa. In "auto" the toggle reads the
current effective theme via matchMedia so the first click always flips it.

A dark-mode SCSS mixin emits each dark block for both
html[data-darkmode="true"] and the auto+media-query case; the processor
badge and tag colour rules generated outside SCSS get the same pair.
2026-10-16 11:36:52 +02:00

41 lines
1.8 KiB
Python

#!/usr/bin/env python3
import re
from urllib.parse import urlsplit
from flask import url_for
def get_html_darkmode_state(res):
# Read it from the <html> tag - the inline badge/tag CSS also contains html[data-darkmode="..."]
m = re.search(rb'<html[^>]*\sdata-darkmode="([^"]*)"', res.data)
return m.group(1).decode() if m else None
def test_darkmode_follows_device_unless_cookie_set(client, live_server, measure_memory_usage, datastore_path):
# The test client defaults cookies to domain="localhost", which isn't sent to the
# live_server's SERVER_NAME (localhost.localdomain), so set them for the real host
cookie_domain = urlsplit(url_for("watchlist.index", _external=True)).hostname
# No cookie - "auto", the CSS follows prefers-color-scheme
client.delete_cookie('css_dark_mode', domain=cookie_domain)
res = client.get(url_for("watchlist.index"), follow_redirects=True)
assert get_html_darkmode_state(res) == 'auto'
# The cookie from the menu toggle always wins over the device setting
client.set_cookie('css_dark_mode', 'true', domain=cookie_domain)
res = client.get(url_for("watchlist.index"), follow_redirects=True)
assert get_html_darkmode_state(res) == 'true'
client.set_cookie('css_dark_mode', 'false', domain=cookie_domain)
res = client.get(url_for("watchlist.index"), follow_redirects=True)
assert get_html_darkmode_state(res) == 'false'
# Garbage cookie value falls back to the device setting instead of erroring
client.set_cookie('css_dark_mode', 'banana', domain=cookie_domain)
res = client.get(url_for("watchlist.index"), follow_redirects=True)
assert res.status_code == 200
assert get_html_darkmode_state(res) == 'auto'
client.delete_cookie('css_dark_mode', domain=cookie_domain)