Point "system" at the themes actually named light and dark

4a65e8c · Gabriel Morell · 2026-09-14 02:57

10 files +51 -20
Message
{commit_body(@commit)}

Files changed

modified assets/css/app.css
+22 −14
@@ -30,12 +30,20 @@
30 30 --color-base-200: oklch(25.26% 0.014 253.1);
31 31 --color-base-300: oklch(20.15% 0.012 254.09);
32 32 --color-base-content: oklch(97.807% 0.029 256.847);
33 − --color-primary: oklch(58% 0.233 277.117);
34 − --color-primary-content: oklch(96% 0.018 272.314);
35 − --color-secondary: oklch(58% 0.233 277.117);
36 − --color-secondary-content: oklch(96% 0.018 272.314);
37 − --color-accent: oklch(60% 0.25 292.717);
38 − --color-accent-content: oklch(96% 0.016 293.756);
33 + --color-primary: oklch(64% 0.076 19);
34 + --color-primary-content: oklch(10% 0.010 19);
35 + --color-secondary: oklch(58% 0.090 19);
36 + --color-secondary-content: oklch(98% 0.010 19);
37 + /* Dark rose, in the same hue family as primary/secondary (~15-19) rather
38 + than the violet daisyUI ships. This is what `text-accent` paints: the
39 + eyebrow over the headline, the feature icons and the step numbers. It
40 + has to stay light enough to read against base-100 at 30% lightness, so
41 + "dark rose" here is depth of hue and restraint of chroma, not a low
42 + lightness -- a genuinely dark rose would be illegible on this ground.
43 + Chroma sits above primary's 0.076 so an accent still reads as one.
44 + The content colour follows primary, which is the same lightness. */
45 + --color-accent: oklch(64% 0.135 15);
46 + --color-accent-content: oklch(10% 0.012 15);
39 47 --color-neutral: oklch(37% 0.044 257.287);
40 48 --color-neutral-content: oklch(98% 0.003 247.858);
41 49 --color-info: oklch(58% 0.158 241.966);
@@ -61,14 +69,14 @@
61 69 default: true;
62 70 prefersdark: false;
63 71 color-scheme: "light";
64 − --color-base-100: oklch(98% 0 0);
65 − --color-base-200: oklch(96% 0.001 286.375);
66 − --color-base-300: oklch(92% 0.004 286.32);
67 − --color-base-content: oklch(21% 0.006 285.885);
68 − --color-primary: oklch(70% 0.213 47.604);
69 − --color-primary-content: oklch(98% 0.016 73.684);
70 − --color-secondary: oklch(55% 0.027 264.364);
71 − --color-secondary-content: oklch(98% 0.002 247.839);
72 + --color-base-100: oklch(98% 0.008 24);
73 + --color-base-200: oklch(96% 0.014 24);
74 + --color-base-300: oklch(92% 0.022 24);
75 + --color-base-content: oklch(25% 0.050 24);
76 + --color-primary: oklch(64% 0.076 19);
77 + --color-primary-content: oklch(98% 0.010 19);
78 + --color-secondary: oklch(55% 0.060 19);
79 + --color-secondary-content: oklch(98% 0.010 19);
72 80 --color-accent: oklch(0% 0 0);
73 81 --color-accent-content: oklch(100% 0 0);
74 82 --color-neutral: oklch(44% 0.017 285.786);
modified lib/feed_pug/themes.ex
+15 −0
@@ -22,6 +22,21 @@ defmodule FeedPug.Themes do
22 22 @spec default() :: theme()
23 23 def default, do: "blueprint"
24 24
25 + @doc """
26 + The pair "system" resolves to, following the OS preference.
27 +
28 + These are the two themes actually named `light` and `dark` — the ones
29 + daisyUI declares with a matching `color-scheme`, so form controls,
30 + scrollbars and the like are rendered by the browser to match. Any of the
31 + other themes can be chosen explicitly; only these two are picked
32 + automatically.
33 + """
34 + @spec system_light() :: theme()
35 + def system_light, do: "light"
36 +
37 + @spec system_dark() :: theme()
38 + def system_dark, do: "dark"
39 +
25 40 @spec valid?(theme()) :: boolean()
26 41 def valid?(theme) when is_binary(theme), do: theme in all() or theme == "system"
27 42 def valid?(_), do: false
modified lib/feed_pug_web/components/layouts/root.html.heex
+14 −6
@@ -1,5 +1,9 @@
1 1 <!DOCTYPE html>
2 −<html lang="en">
2 +<html
3 + lang="en"
4 + data-system-light={FeedPug.Themes.system_light()}
5 + data-system-dark={FeedPug.Themes.system_dark()}
6 +>
3 7 <head>
4 8 <meta charset="utf-8" />
5 9 <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -12,20 +16,24 @@
12 16 </script>
13 17 <script>
14 18 (() => {
15 − const SYSTEM_DARK = "blueprint";
16 − const SYSTEM_LIGHT = "blueprint";
19 + // Read off <html> rather than written here: HEEx does not
20 + // interpolate inside a <script>, it emits the expression verbatim.
21 + // FeedPug.Themes is the one place the pair is named.
22 + const root = document.documentElement;
23 + const SYSTEM_DARK = root.dataset.systemDark;
24 + const SYSTEM_LIGHT = root.dataset.systemLight;
17 25 const mq = window.matchMedia("(prefers-color-scheme: dark)");
18 26 const systemTheme = () => mq.matches ? SYSTEM_DARK : SYSTEM_LIGHT;
19 27 const setTheme = (theme) => {
20 28 if (theme === "system") {
21 29 localStorage.removeItem("phx:theme");
22 − document.documentElement.setAttribute("data-theme", systemTheme());
30 + root.setAttribute("data-theme", systemTheme());
23 31 } else {
24 32 localStorage.setItem("phx:theme", theme);
25 − document.documentElement.setAttribute("data-theme", theme);
33 + root.setAttribute("data-theme", theme);
26 34 }
27 35 };
28 − if (!document.documentElement.hasAttribute("data-theme")) {
36 + if (!root.hasAttribute("data-theme")) {
29 37 setTheme(localStorage.getItem("phx:theme") || "system");
30 38 }
31 39 mq.addEventListener("change", () => {

Parents: e44a7c9