neiam
/waxx
waxx
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
Make "system" follow the OS, and repaint the light/dark pair
c6db9a8 · Gabriel Morell · 2026-09-14 03:19
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: false; |
| 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/waxx/themes.ex
+15
−0
@@ -17,6 +17,21 @@ defmodule Waxx.Themes do
| 17 | 17 | @spec neiam() :: [theme()] |
| 18 | 18 | def neiam, do: @neiam |
| 19 | 19 | |
| 20 | + @doc """ | |
| 21 | + The pair "system" resolves to, following the OS preference. | |
| 22 | + | |
| 23 | + These are the two themes actually named `light` and `dark` — the ones | |
| 24 | + daisyUI declares with a matching `color-scheme`, so form controls, | |
| 25 | + scrollbars and the like are rendered by the browser to match. Any of the | |
| 26 | + other themes can be chosen explicitly; only these two are picked | |
| 27 | + automatically. | |
| 28 | + """ | |
| 29 | + @spec system_light() :: theme() | |
| 30 | + def system_light, do: "light" | |
| 31 | + | |
| 32 | + @spec system_dark() :: theme() | |
| 33 | + def system_dark, do: "dark" | |
| 34 | + | |
| 20 | 35 | @spec valid?(theme()) :: boolean() |
| 21 | 36 | def valid?(theme) when is_binary(theme), do: theme in all() or theme == "system" |
| 22 | 37 | def valid?(_), do: false |
modified
lib/waxx_web/components/layouts/root.html.heex
+19
−5
@@ -1,5 +1,9 @@
| 1 | 1 | <!DOCTYPE html> |
| 2 | −<html lang="en"> | |
| 2 | +<html | |
| 3 | + lang="en" | |
| 4 | + data-system-light={Waxx.Themes.system_light()} | |
| 5 | + data-system-dark={Waxx.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,30 @@
| 12 | 16 | </script> |
| 13 | 17 | <script> |
| 14 | 18 | (() => { |
| 19 | + // Read off <html> rather than written here: HEEx does not | |
| 20 | + // interpolate inside a <script>, it emits the expression verbatim. | |
| 21 | + // Waxx.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; | |
| 25 | + const mq = window.matchMedia("(prefers-color-scheme: dark)"); | |
| 26 | + const systemTheme = () => mq.matches ? SYSTEM_DARK : SYSTEM_LIGHT; | |
| 15 | 27 | const setTheme = (theme) => { |
| 16 | 28 | if (theme === "system") { |
| 17 | 29 | localStorage.removeItem("phx:theme"); |
| 18 | − document.documentElement.removeAttribute("data-theme"); | |
| 30 | + root.setAttribute("data-theme", systemTheme()); | |
| 19 | 31 | } else { |
| 20 | 32 | localStorage.setItem("phx:theme", theme); |
| 21 | − document.documentElement.setAttribute("data-theme", theme); | |
| 33 | + root.setAttribute("data-theme", theme); | |
| 22 | 34 | } |
| 23 | 35 | }; |
| 24 | − if (!document.documentElement.hasAttribute("data-theme")) { | |
| 36 | + if (!root.hasAttribute("data-theme")) { | |
| 25 | 37 | setTheme(localStorage.getItem("phx:theme") || "system"); |
| 26 | 38 | } |
| 39 | + mq.addEventListener("change", () => { | |
| 40 | + if (!localStorage.getItem("phx:theme")) setTheme("system"); | |
| 41 | + }); | |
| 27 | 42 | window.addEventListener("storage", (e) => e.key === "phx:theme" && setTheme(e.newValue || "system")); |
| 28 | − | |
| 29 | 43 | window.addEventListener("phx:set-theme", (e) => setTheme(e.target.dataset.phxTheme)); |
| 30 | 44 | })(); |
| 31 | 45 | </script> |
Parents: 7a34183