feat: move dark/light mode toggle to user profile dropdown

Replaced the dark mode checkbox in the My Account modal with a
sun/moon toggle directly in the profile dropdown menu. Sun on the
left, moon on the right, with a colored slider (amber for light,
indigo for dark).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-04 10:12:42 -08:00
parent 7a11936483
commit f5260e0817
1 changed files with 45 additions and 23 deletions

View File

@ -342,6 +342,15 @@ export class RStackIdentity extends HTMLElement {
<button class="dropdown-item" data-action="my-account">👤 My Account</button> <button class="dropdown-item" data-action="my-account">👤 My Account</button>
<button class="dropdown-item" data-action="my-spaces">🌐 My Spaces</button> <button class="dropdown-item" data-action="my-spaces">🌐 My Spaces</button>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<div class="dropdown-theme-row">
<span class="theme-icon"></span>
<label class="theme-toggle">
<input type="checkbox" id="dropdown-theme-toggle" ${(localStorage.getItem("canvas-theme") || "dark") === "dark" ? "checked" : ""} />
<span class="theme-slider"></span>
</label>
<span class="theme-icon">🌙</span>
</div>
<div class="dropdown-divider"></div>
<button class="dropdown-item dropdown-item--danger" data-action="signout">🚪 Sign Out</button> <button class="dropdown-item dropdown-item--danger" data-action="signout">🚪 Sign Out</button>
</div> </div>
</div> </div>
@ -374,6 +383,17 @@ export class RStackIdentity extends HTMLElement {
} }
}); });
}); });
const dropdownTheme = this.#shadow.getElementById("dropdown-theme-toggle") as HTMLInputElement;
if (dropdownTheme) {
dropdownTheme.addEventListener("click", (e) => e.stopPropagation());
dropdownTheme.addEventListener("change", () => {
const newTheme = dropdownTheme.checked ? "dark" : "light";
localStorage.setItem("canvas-theme", newTheme);
document.documentElement.setAttribute("data-theme", newTheme);
this.dispatchEvent(new CustomEvent("theme-change", { bubbles: true, composed: true, detail: { theme: newTheme } }));
});
}
} else { } else {
this.#shadow.innerHTML = ` this.#shadow.innerHTML = `
<style>${STYLES}</style> <style>${STYLES}</style>
@ -676,17 +696,7 @@ export class RStackIdentity extends HTMLElement {
}</div> }</div>
</div> </div>
<div class="account-section account-section--inline"> <div class="error" id="acct-error"></div>
<div class="account-section-header">
<span>🌙 Dark Mode</span>
<label class="toggle-switch">
<input type="checkbox" id="acct-theme-toggle" ${isDark ? "checked" : ""} />
<span class="toggle-slider"></span>
</label>
</div>
</div>
<div class="error" id="acct-error"></div>
</div> </div>
`; `;
attachListeners(); attachListeners();
@ -1150,18 +1160,6 @@ export class RStackIdentity extends HTMLElement {
}); });
} }
// Dark Mode toggle
const themeToggle = overlay.querySelector("#acct-theme-toggle") as HTMLInputElement;
if (themeToggle) {
themeToggle.addEventListener("change", (e) => {
e.stopPropagation();
const newTheme = themeToggle.checked ? "dark" : "light";
localStorage.setItem("canvas-theme", newTheme);
document.documentElement.setAttribute("data-theme", newTheme);
this.dispatchEvent(new CustomEvent("theme-change", { bubbles: true, composed: true, detail: { theme: newTheme } }));
this.#render();
});
}
}; };
document.body.appendChild(overlay); document.body.appendChild(overlay);
@ -1368,6 +1366,30 @@ const STYLES = `
.dropdown-divider { height: 1px; margin: 4px 0; } .dropdown-divider { height: 1px; margin: 4px 0; }
.dropdown-divider { background: var(--rs-border-subtle); } .dropdown-divider { background: var(--rs-border-subtle); }
/* Theme toggle in dropdown */
.dropdown-theme-row {
display: flex; align-items: center; justify-content: center;
gap: 10px; padding: 8px 16px;
}
.theme-icon { font-size: 0.9rem; line-height: 1; }
.theme-toggle {
position: relative; width: 40px; height: 22px;
display: inline-block; flex-shrink: 0;
}
.theme-toggle input { opacity: 0; width: 0; height: 0; }
.theme-slider {
position: absolute; inset: 0; border-radius: 11px;
background: #fbbf24; cursor: pointer; transition: background 0.25s;
}
.theme-slider::before {
content: ""; position: absolute;
width: 18px; height: 18px; border-radius: 50%;
left: 2px; bottom: 2px; background: white;
transition: transform 0.25s;
}
.theme-toggle input:checked + .theme-slider { background: #6366f1; }
.theme-toggle input:checked + .theme-slider::before { transform: translateX(18px); }
/* Avatar wrapper + notification badge */ /* Avatar wrapper + notification badge */
.avatar-wrap { position: relative; } .avatar-wrap { position: relative; }
.notif-badge { .notif-badge {