From 75ccc25a16be96e38043915dd72196997bdee807 Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:16:30 +0000 Subject: [PATCH 1/7] Expose darkmode configure to template --- .../laika/helium/internal/generate/ConfigGenerator.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/io/src/main/scala/laika/helium/internal/generate/ConfigGenerator.scala b/io/src/main/scala/laika/helium/internal/generate/ConfigGenerator.scala index 2a769794e..e1c831fa5 100644 --- a/io/src/main/scala/laika/helium/internal/generate/ConfigGenerator.scala +++ b/io/src/main/scala/laika/helium/internal/generate/ConfigGenerator.scala @@ -206,6 +206,10 @@ private[laika] object ConfigGenerator { "helium.site.includePDF", helium.siteSettings.content.downloadPage.fold(false)(_.includePDF) ) + .withValue( + "helium.site.darkModeEnabled", + helium.siteSettings.darkMode.map(_ => "enabled") + ) .withValue("helium.site.fontFamilies", helium.siteSettings.themeFonts) .withValue("helium.epub.fontFamilies", helium.epubSettings.themeFonts) .withValue("helium.pdf.fontFamilies", helium.pdfSettings.themeFonts) From 1961b1c46df5425846e28e9cac8f4a16fbb2329f Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:17:37 +0000 Subject: [PATCH 2/7] Made darkmode css overrideable by manual --- .../internal/generate/CSSVarGenerator.scala | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala b/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala index 4959c7b0f..357cdf8a5 100644 --- a/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala +++ b/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala @@ -39,6 +39,24 @@ private[helium] object CSSVarGenerator { | src: url("$path"); |}""".stripMargin + private def renderManualOverride( + mode: String, + vars: Seq[(String, String)] + ): String = { + val rendered = + vars.map { case (name, value) => + s"$name: $value;" + }.mkString("\n ") + + s""" + :root[data-color-mode="$mode"] { + $rendered + color-scheme: $mode; + } + + """ + } + def generate(settings: SiteSettings): String = { import settings.layout.* val layoutStyles = Seq( @@ -183,15 +201,22 @@ private[helium] object CSSVarGenerator { val (colorScheme, darkModeStyles) = common.darkMode match { case Some(darkModeColors) => - ( - Seq(("color-scheme", "light dark")), + val darkVars = toVars(colorSet(darkModeColors, darkMode = true)) + val lightVars = toVars(colorSet(common.colors, darkMode = false)) + + val darkMedia = renderStyles( - toVars(colorSet(darkModeColors, darkMode = true)), + darkVars, includeInverted, darkMode = true ) + val manualLight = renderManualOverride("light", lightVars) + val manualDark = renderManualOverride("dark", darkVars) + ( + Seq(("color-scheme", "light dark")), + darkMedia + manualLight + manualDark ) - case None => (Nil, "") + case None => (Nil, "") } renderStyles(toVars(vars) ++ colorScheme, includeInverted, darkMode = false) + darkModeStyles From 4c354488004ea714fe03ddc270b6b5388058e926 Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:18:25 +0000 Subject: [PATCH 3/7] Add toggle button to topNav --- .../laika/helium/templates/includes/topNav.template.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/io/src/main/resources/laika/helium/templates/includes/topNav.template.html b/io/src/main/resources/laika/helium/templates/includes/topNav.template.html index cc68f21d1..8e7848112 100644 --- a/io/src/main/resources/laika/helium/templates/includes/topNav.template.html +++ b/io/src/main/resources/laika/helium/templates/includes/topNav.template.html @@ -6,6 +6,11 @@ ${helium.site.topNavigation.versionMenu} + @:for(helium.site.darkModeEnabled) + + @:@ ${?helium.site.topNavigation.home} From b1c56e061765b93ab930ecd7051f1ce212d959cf Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:19:23 +0000 Subject: [PATCH 4/7] Add style to toggle button --- .../main/resources/laika/helium/css/nav.css | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/io/src/main/resources/laika/helium/css/nav.css b/io/src/main/resources/laika/helium/css/nav.css index 91adad5ab..13c0c3558 100644 --- a/io/src/main/resources/laika/helium/css/nav.css +++ b/io/src/main/resources/laika/helium/css/nav.css @@ -25,6 +25,29 @@ header a:hover, nav .row a:hover { color: var(--component-hover) } +header .row button.theme-toggle { + margin: 0 0 0 20px; + padding: 0; + border: none; + background: none; + color: var(--component-color); + cursor: pointer; + font: inherit; + font-size: 0.9rem; + opacity: 0.8; +} + +header .row button.theme-toggle:hover { + color: var(--component-hover); + opacity: 1; +} + +header .row button.theme-toggle:focus-visible { + outline: 2px solid var(--component-border); + outline-offset: 3px; + border-radius: 4px; +} + header .image-link { height: var(--top-bar-height); display: flex; From 4b07006be72190952c0459c3e99359e6cd007210 Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:20:11 +0000 Subject: [PATCH 5/7] Add different states of toggle --- .../main/resources/laika/helium/js/theme.js | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/io/src/main/resources/laika/helium/js/theme.js b/io/src/main/resources/laika/helium/js/theme.js index efb09e944..2df6dc436 100644 --- a/io/src/main/resources/laika/helium/js/theme.js +++ b/io/src/main/resources/laika/helium/js/theme.js @@ -61,8 +61,54 @@ function initMenuToggles () { }); } +function initColorModeToggle () { + const btn = document.getElementById("theme-toggle"); + if (!btn) return; + + function currentMode () { + const attr = document.documentElement.getAttribute("data-color-mode"); + return (attr === "light" || attr === "dark") ? attr : "auto"; + } + + function applyMode (mode) { + if (mode === "light" || mode === "dark") { + document.documentElement.setAttribute("data-color-mode", mode); + try { localStorage.setItem("laika-color-mode", mode); } catch (e) {} + } else { + document.documentElement.removeAttribute("data-color-mode"); + try { localStorage.removeItem("laika-color-mode"); } catch (e) {} + } + updateUi(mode); + } + + function updateUi (mode) { + if (mode === "light") { + btn.textContent = "Light"; + btn.title = "Switch to dark mode"; + btn.setAttribute("aria-label", "Switch to dark mode"); + } else if (mode === "dark") { + btn.textContent = "Dark"; + btn.title = "Switch to system mode"; + btn.setAttribute("aria-label", "Switch to system mode"); + } else { + btn.textContent = "Auto"; + btn.title = "Switch to light mode"; + btn.setAttribute("aria-label", "Switch to light mode"); + } + } + + updateUi(currentMode()); + + btn.addEventListener("click", () => { + const mode = currentMode(); + const next = (mode === "auto") ? "light" : (mode === "light") ? "dark" : "auto"; + applyMode(next); + }); +} + document.addEventListener('DOMContentLoaded', () => { initNavToggle(); initMenuToggles(); initTabs(); + initColorModeToggle(); }); From af255f90eadbdbd2a1243d0c5bf9e23bb54e0238 Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 01:20:48 +0000 Subject: [PATCH 6/7] Set chosen mode before css loading --- .../helium/templates/includes/head.template.html | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/io/src/main/resources/laika/helium/templates/includes/head.template.html b/io/src/main/resources/laika/helium/templates/includes/head.template.html index 0d7aeb26f..d098bb7e2 100644 --- a/io/src/main/resources/laika/helium/templates/includes/head.template.html +++ b/io/src/main/resources/laika/helium/templates/includes/head.template.html @@ -19,6 +19,20 @@ @:for(helium.webFonts) @:@ + @:includeCSS @:includeJS @:heliumInitVersions From 586d500a24cd7c8d797079dd0e1b6fb9836d285f Mon Sep 17 00:00:00 2001 From: abby-ql Date: Sun, 8 Mar 2026 19:27:17 +0000 Subject: [PATCH 7/7] Formatting --- .../laika/helium/internal/generate/CSSVarGenerator.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala b/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala index 357cdf8a5..199b3f083 100644 --- a/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala +++ b/io/src/main/scala/laika/helium/internal/generate/CSSVarGenerator.scala @@ -201,10 +201,10 @@ private[helium] object CSSVarGenerator { val (colorScheme, darkModeStyles) = common.darkMode match { case Some(darkModeColors) => - val darkVars = toVars(colorSet(darkModeColors, darkMode = true)) + val darkVars = toVars(colorSet(darkModeColors, darkMode = true)) val lightVars = toVars(colorSet(common.colors, darkMode = false)) - val darkMedia = + val darkMedia = renderStyles( darkVars, includeInverted, @@ -216,7 +216,7 @@ private[helium] object CSSVarGenerator { Seq(("color-scheme", "light dark")), darkMedia + manualLight + manualDark ) - case None => (Nil, "") + case None => (Nil, "") } renderStyles(toVars(vars) ++ colorScheme, includeInverted, darkMode = false) + darkModeStyles