Add auto theme toggle

This commit is contained in:
2026-02-14 23:11:40 -05:00
parent ea871e3fb4
commit c13bd92c2b
2 changed files with 53 additions and 15 deletions

View File

@@ -17,7 +17,7 @@ export default {
sortColumn: this.sortColumn, sortColumn: this.sortColumn,
topics: [], topics: [],
isMobile: false, isMobile: false,
currentTheme: 'dark', currentTheme: 'auto',
mediaQueryListener: null, mediaQueryListener: null,
vuetifyTheme: null, vuetifyTheme: null,
darkModeQuery: null, darkModeQuery: null,
@@ -43,12 +43,11 @@ export default {
}, },
methods: { methods: {
initializeTheme() { initializeTheme() {
// If no saved preference, apply system preference now // If no saved preference, default to auto
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
if (!savedTheme) { if (!savedTheme) {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; this.currentTheme = 'auto';
const theme = prefersDark ? 'dark' : 'light'; this.applyTheme('auto');
this.applyTheme(theme);
} else { } else {
this.currentTheme = savedTheme; this.currentTheme = savedTheme;
// Apply saved theme // Apply saved theme
@@ -63,12 +62,12 @@ export default {
// Use arrow function to preserve 'this' context // Use arrow function to preserve 'this' context
const themeChangeHandler = (e) => { const themeChangeHandler = (e) => {
// Only auto-update theme if user hasn't set a preference manually // Only auto-update theme if set to 'auto'
const savedTheme = localStorage.getItem('theme'); const savedTheme = localStorage.getItem('theme');
if (!savedTheme) { if (savedTheme === 'auto' || !savedTheme) {
const newTheme = e.matches ? 'dark' : 'light'; const newTheme = e.matches ? 'dark' : 'light';
console.log('System theme changed to:', newTheme); console.log('System theme changed to:', newTheme);
this.applyTheme(newTheme); this.applyThemeActual(newTheme);
} }
}; };
@@ -77,15 +76,27 @@ export default {
this.themeChangeHandler = themeChangeHandler; this.themeChangeHandler = themeChangeHandler;
this.darkModeQuery = darkModeQuery; this.darkModeQuery = darkModeQuery;
}, },
applyTheme(theme) { applyTheme(theme, skipSave = false) {
this.currentTheme = theme; this.currentTheme = theme;
localStorage.setItem('theme', theme); if (!skipSave) {
localStorage.setItem('theme', theme);
}
// Determine actual theme to apply
let actualTheme = theme;
if (theme === 'auto') {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
actualTheme = prefersDark ? 'dark' : 'light';
}
this.applyThemeActual(actualTheme);
},
applyThemeActual(actualTheme) {
// Update data-bs-theme attribute for CSS variables to work // Update data-bs-theme attribute for CSS variables to work
document.documentElement.setAttribute('data-bs-theme', theme === 'dark' ? 'dark' : 'light'); document.documentElement.setAttribute('data-bs-theme', actualTheme === 'dark' ? 'dark' : 'light');
// Update HTML class for theme-based CSS selectors // Update HTML class for theme-based CSS selectors
if (theme === 'dark') { if (actualTheme === 'dark') {
document.documentElement.classList.add('dark-theme'); document.documentElement.classList.add('dark-theme');
document.documentElement.classList.remove('light-theme'); document.documentElement.classList.remove('light-theme');
} else { } else {
@@ -94,7 +105,15 @@ export default {
} }
}, },
toggleTheme() { toggleTheme() {
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark'; // Cycle through: auto -> light -> dark -> auto
let newTheme;
if (this.currentTheme === 'auto') {
newTheme = 'light';
} else if (this.currentTheme === 'light') {
newTheme = 'dark';
} else {
newTheme = 'auto';
}
this.applyTheme(newTheme); this.applyTheme(newTheme);
}, },
detectMobile() { detectMobile() {
@@ -186,6 +205,24 @@ export default {
return dealerName.replace(re, (matchedText) => `<mark>${matchedText}</mark>`); return dealerName.replace(re, (matchedText) => `<mark>${matchedText}</mark>`);
}; };
}, },
getThemeIcon() {
if (this.currentTheme === 'auto') {
return 'brightness_auto';
} else if (this.currentTheme === 'dark') {
return 'light_mode';
} else {
return 'dark_mode';
}
},
getThemeTitle() {
if (this.currentTheme === 'auto') {
return 'Theme: Auto (click for Light)';
} else if (this.currentTheme === 'light') {
return 'Theme: Light (click for Dark)';
} else {
return 'Theme: Dark (click for Auto)';
}
},
}, },
}; };
</script> </script>
@@ -205,8 +242,8 @@ export default {
@keyup.esc="$refs.filter.blur()" @keyup.esc="$refs.filter.blur()"
class="search-input" class="search-input"
/> />
<button @click="toggleTheme" class="theme-toggle" :title="'Switch to ' + (currentTheme === 'dark' ? 'light' : 'dark') + ' theme'"> <button @click="toggleTheme" class="theme-toggle" :title="getThemeTitle">
<span class="material-symbols-outlined">{{ currentTheme === 'dark' ? 'light_mode' : 'dark_mode' }}</span> <span class="material-symbols-outlined">{{ getThemeIcon }}</span>
</button> </button>
</div> </div>
</div> </div>

View File

@@ -169,6 +169,7 @@ html.dark-theme .search-input:focus {
transition: all 0.2s ease; transition: all 0.2s ease;
font-size: 18px; font-size: 18px;
padding: 0; padding: 0;
flex-shrink: 0;
} }
.theme-toggle:hover { .theme-toggle:hover {