Files
Blog/src/frontend/clientJS/theme-picker.ts
Caleb Braaten f46f4667a1 Complete WIP: Architecture refactor.
Mount JSX server side templating for blog posts. Send AppShell conditionally. Maintain support for HMR via HTMLbundles using Bun's native fullstack dev server under an /hmr path. This is only mounted in development and is supported by the onImport Bun plugin. Add DB creation on startup and load pages based on those records.
2026-01-08 05:35:59 -08:00

166 lines
5.2 KiB
TypeScript

// Theme switching functionality
const LIGHT_THEMES = ['latte', 'solarized-light', 'gruvbox-light'];
const DARK_THEMES = ['frappe', 'macchiato', 'mocha', 'solarized-dark', 'gruvbox-dark', 'nord', 'dracula', 'one-dark', 'tokyo-night'];
const THEME_NAMES = {
latte: 'Catppuccin Latte',
frappe: 'Catppuccin Frappe',
macchiato: 'Catppuccin Macchiato',
mocha: 'Catppuccin Mocha',
'solarized-light': 'Solarized Light',
'solarized-dark': 'Solarized Dark',
'gruvbox-light': 'Gruvbox Light',
'gruvbox-dark': 'Gruvbox Dark',
nord: 'Nord',
dracula: 'Dracula',
'one-dark': 'One Dark',
'tokyo-night': 'Tokyo Night'
};
let currentMode = '';
let THEMES = { light: 'latte', dark: 'mocha' };
function initializeTheme() {
const savedLightTheme = localStorage.getItem('theme-light') || 'latte';
const savedDarkTheme = localStorage.getItem('theme-dark') || 'mocha';
const savedMode = localStorage.getItem('theme-mode');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
THEMES.light = savedLightTheme;
THEMES.dark = savedDarkTheme;
currentMode = savedMode || (prefersDark ? 'dark' : 'light');
updateModeToggle();
updateThemeDropdown();
applyTheme();
}
function applyTheme() {
const theme = THEMES[currentMode];
document.documentElement.setAttribute('data-theme', theme);
updateCurrentThemeDisplay();
}
function updateThemeDropdown() {
const menu = document.getElementById('themeDropdownMenu');
const themes = currentMode === 'light' ? LIGHT_THEMES : DARK_THEMES;
if (menu) {
menu.innerHTML = themes.map(theme =>
'<div class="theme-option" data-theme="' + theme + '">' +
' <div class="theme-name">' + THEME_NAMES[theme] + '</div>' +
' <button class="theme-selector-btn" data-theme="' + theme + '"></button>' +
'</div>'
).join('');
attachThemeOptionListeners();
updateUI();
}
}
function attachThemeOptionListeners() {
document.querySelectorAll('.theme-option').forEach(option => {
option.addEventListener('click', function(e) {
const theme = this.getAttribute('data-theme');
if (theme) selectTheme(theme);
});
});
}
function selectTheme(theme: string) {
THEMES[currentMode] = theme;
localStorage.setItem('theme-' + currentMode, theme);
updateUI();
applyTheme();
}
function updateCurrentThemeDisplay() {
const theme = THEMES[currentMode];
const themeName = THEME_NAMES[theme];
const display = document.getElementById('currentThemeDisplay');
if (display) {
display.textContent = themeName;
}
}
function updateUI() {
const currentTheme = THEMES[currentMode];
document.querySelectorAll('.theme-selector-btn').forEach(function(btn) {
const theme = btn.getAttribute('data-theme');
if (theme) {
btn.classList.toggle('selected', currentTheme === theme);
}
});
}
function updateModeToggle() {
const lightBtn = document.getElementById('lightModeBtn');
const darkBtn = document.getElementById('darkModeBtn');
if (lightBtn) lightBtn.classList.toggle('active', currentMode === 'light');
if (darkBtn) darkBtn.classList.toggle('active', currentMode === 'dark');
}
// Event listeners
function setupEventListeners() {
const lightBtn = document.getElementById('lightModeBtn');
const darkBtn = document.getElementById('darkModeBtn');
const trigger = document.getElementById('themeDropdownTrigger');
const menu = document.getElementById('themeDropdownMenu');
if (lightBtn) {
lightBtn.addEventListener('click', function() {
currentMode = 'light';
localStorage.setItem('theme-mode', 'light');
updateModeToggle();
updateThemeDropdown();
applyTheme();
});
}
if (darkBtn) {
darkBtn.addEventListener('click', function() {
currentMode = 'dark';
localStorage.setItem('theme-mode', 'dark');
updateModeToggle();
updateThemeDropdown();
applyTheme();
});
}
if (trigger && menu) {
trigger.addEventListener('click', function() {
trigger.classList.toggle('open');
menu.classList.toggle('open');
});
}
document.addEventListener('click', function(e) {
const target = e.target as Element;
if (!target.closest('.theme-dropdown-wrapper')) {
if (trigger) trigger.classList.remove('open');
if (menu) menu.classList.remove('open');
}
});
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
if (localStorage.getItem('theme-mode') !== 'light' && localStorage.getItem('theme-mode') !== 'dark') {
currentMode = e.matches ? 'dark' : 'light';
updateModeToggle();
updateThemeDropdown();
applyTheme();
}
});
}
// Initialize on page load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
initializeTheme();
setupEventListeners();
});
} else {
initializeTheme();
setupEventListeners();
}