From 2a7c173f7bc30c768cc7520816621b4c7fcf4e0b Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 14 Apr 2026 21:43:28 -0500 Subject: [PATCH] Replace light/dark switch with sun/moon icon button - Remove toggle switch, Light/Dark labels, knob/track CSS - Single button with sun (light) and moon (dark) FA icons - Icons cross-fade with rotation transition on theme change - Compact circular button matches FAB aesthetic - Removed duplicate old switch CSS rules Co-Authored-By: Claude Opus 4.6 (1M context) --- src/media/css/template.css | 110 ++++++++++++++----------------------- src/media/js/template.js | 49 +++++++++-------- 2 files changed, 65 insertions(+), 94 deletions(-) diff --git a/src/media/css/template.css b/src/media/css/template.css index 5a5daa9..b144a33 100644 --- a/src/media/css/template.css +++ b/src/media/css/template.css @@ -17064,7 +17064,7 @@ form .form-select { display: flex; align-items: center; gap: .5rem; - padding: calc(var(--padding-x, 0.25rem) * 2) calc(var(--padding-y, 0.25rem) * 3) calc(var(--padding-x, 0.25rem) * 2) calc(var(--padding-y, 0.25rem) * 8); + padding: .4rem .6rem; border-radius: 999px; border: none; background: var(--muted-color, #6d757e); @@ -17094,46 +17094,47 @@ form .form-select { top: 1rem; } -#mokoThemeFab .switch { - display: inline-flex; +/* Sun/Moon theme toggle button */ +.theme-icon-btn { + display: flex; align-items: center; - position: relative; - width: 44px; - height: 24px; - background: var(--secondary-color, #e6ebf1bf); - transition: background .2s, border-color .2s; - border-radius: var(--border-radius-xxl, 2rem); -} - -#mokoThemeFab .knob { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - border-radius: var(--border-radius-xxl, 2rem); - background: var(--bs-body-bg, #fff); - box-shadow: var(--box-shadow, 0 .5rem 1rem #00000066); - transition: transform .2s ease; -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .knob { - transform: translateX(20px); -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .switch { - background: rgba(var(--secondary-color, #e6ebf1bf), .15); -} - -button#mokoThemeSwitch { - border: unset; - background-color: unset; -} - -#mokoThemeFab .label { - user-select: none; - font-size: .875rem; + justify-content: center; + width: 32px; + height: 32px; + border: none; + border-radius: 50%; + background: rgba(255,255,255,.15); color: #fff; + font-size: 1.1rem; + cursor: pointer; + padding: 0; + position: relative; +} + +.theme-icon-btn .fa-sun, +.theme-icon-btn .fa-moon { + position: absolute; + transition: opacity .2s, transform .2s; +} + +/* Light mode: show sun, hide moon */ +.theme-icon-btn.is-light .fa-sun { + opacity: 1; + transform: rotate(0deg); +} +.theme-icon-btn.is-light .fa-moon { + opacity: 0; + transform: rotate(-90deg); +} + +/* Dark mode: show moon, hide sun */ +.theme-icon-btn.is-dark .fa-moon { + opacity: 1; + transform: rotate(0deg); +} +.theme-icon-btn.is-dark .fa-sun { + opacity: 0; + transform: rotate(90deg); } /* Auto toggle switch (on/off style) */ @@ -17357,37 +17358,6 @@ body.site.error-page { text-decoration: none; } -#mokoThemeFab .knob { - position: absolute; - top: 2px; - left: 2px; - width: 20px; - height: 20px; - border-radius: var(--border-radius-xxl, 2rem); - background: var(--bs-body-bg, #fff); - box-shadow: var(--box-shadow, 0 .5rem 1rem #00000066); - transition: transform .2s ease; -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .knob { - transform: translateX(20px); -} - -#mokoThemeFab [role="switch"][aria-checked="true"] .switch { - background: rgba(var(--secondary-color, #e6ebf1bf), .15); -} - -button#mokoThemeSwitch { - border: unset; - background-color: unset; -} - -#mokoThemeFab .label { - user-select: none; - font-size: .875rem; - color: #fff; -} - #mokoThemeFab.debug-outline { outline: 2px dashed var(--pink, #ff8fc0); outline-offset: 2px; diff --git a/src/media/js/template.js b/src/media/js/template.js index 49ae3ae..94a6909 100644 --- a/src/media/js/template.js +++ b/src/media/js/template.js @@ -62,30 +62,33 @@ wrap.id = 'mokoThemeFab'; wrap.className = posClassFromBody(); - // Light label - var lblL = doc.createElement('span'); - lblL.className = 'label'; - lblL.textContent = 'Light'; - - // Switch + // Sun/Moon toggle button var switchWrap = doc.createElement('button'); switchWrap.id = 'mokoThemeSwitch'; switchWrap.type = 'button'; - switchWrap.setAttribute('role', 'switch'); + switchWrap.className = 'theme-icon-btn'; switchWrap.setAttribute('aria-label', 'Toggle dark mode'); - switchWrap.setAttribute('aria-checked', 'false'); - var track = doc.createElement('span'); - track.className = 'switch'; - var knob = doc.createElement('span'); - knob.className = 'knob'; - track.appendChild(knob); - switchWrap.appendChild(track); + var sunIcon = doc.createElement('i'); + sunIcon.className = 'fa-solid fa-sun'; + sunIcon.setAttribute('aria-hidden', 'true'); - // Dark label - var lblD = doc.createElement('span'); - lblD.className = 'label'; - lblD.textContent = 'Dark'; + var moonIcon = doc.createElement('i'); + moonIcon.className = 'fa-solid fa-moon'; + moonIcon.setAttribute('aria-hidden', 'true'); + + switchWrap.appendChild(sunIcon); + switchWrap.appendChild(moonIcon); + + function updateThemeIcon(theme) { + if (theme === 'dark') { + switchWrap.classList.add('is-dark'); + switchWrap.classList.remove('is-light'); + } else { + switchWrap.classList.add('is-light'); + switchWrap.classList.remove('is-dark'); + } + } // Auto toggle (on/off switch style) var autoWrap = doc.createElement('div'); @@ -127,7 +130,7 @@ var current = (root.getAttribute('data-bs-theme') || 'light').toLowerCase(); var next = current === 'dark' ? 'light' : 'dark'; applyTheme(next); - switchWrap.setAttribute('aria-checked', next === 'dark' ? 'true' : 'false'); + updateThemeIcon(next); // Turn off auto when manually switching auto.classList.remove('on'); auto.setAttribute('aria-checked', 'false'); @@ -145,7 +148,7 @@ clearStored(); var sys = systemTheme(); applyTheme(sys); - switchWrap.setAttribute('aria-checked', sys === 'dark' ? 'true' : 'false'); + updateThemeIcon(sys); } }); @@ -154,7 +157,7 @@ if (!getStored()) { var sys = systemTheme(); applyTheme(sys); - switchWrap.setAttribute('aria-checked', sys === 'dark' ? 'true' : 'false'); + updateThemeIcon(sys); } }; if (typeof mql.addEventListener === 'function') mql.addEventListener('change', onMql); @@ -162,12 +165,10 @@ // Initial state var initial = getStored() || systemTheme(); - switchWrap.setAttribute('aria-checked', initial === 'dark' ? 'true' : 'false'); + updateThemeIcon(initial); // Mount - wrap.appendChild(lblL); wrap.appendChild(switchWrap); - wrap.appendChild(lblD); wrap.appendChild(autoWrap); wrap.appendChild(divider); wrap.appendChild(a11ySlot);