Is there an existing issue for this?
Describe the bug
The ThemeProvider component has a logic issue where the 'auto' theme setting is being converted to its actual value ('dark' or 'light') and persisted to localStorage. This causes the theme preference to lose the automatic/responsive behavior on subsequent page loads.
Current Behavior
- User selects "Auto" theme mode
- getTheme() retrieves the stored 'auto' value
- getTheme() then converts 'auto' to getAutoThemeValue() (returns 'dark' or 'light')
- The actual value ('dark' or 'light') gets saved to localStorage instead of 'auto'
- On next page load, theme is locked to that specific value rather than following system preferences
Expected Behavior
• When user selects "Auto" theme, the string 'auto' should be persisted to localStorage
• On page load, if theme is 'auto', it should be resolved to the actual value only for DOM rendering
• The system should continue to respond to system dark mode preference changes
Interactive render mode
Interactive Server (Interactive server-side rendering (interactive SSR) using Blazor Server)
Steps To Reproduce
utility.js
Exceptions (if any)
No response
.NET Version
NET9.0
Anything else?
Root Cause
The issue exists in two functions in utility.js:718-724:
In getTheme():
if (theme === null || theme === 'auto') {
theme = getAutoThemeValue(); // Converts 'auto' too early
}
In setTheme(): The function doesn't properly handle the 'auto' case before resolution.
Proposed Solution
- Modify getTheme() in utility.js:718-724
Remove the early conversion of 'auto' to its actual value:
export function getTheme(useLocalstorage = true) {
useLocalstorage = useLocalstorage ?? true;
let theme = null;
if (useLocalstorage) {
theme = localStorage.getItem('theme');
}
else {
theme = document.documentElement.getAttribute('data-bs-theme');
}
// REMOVED: if (theme === null || theme === 'auto') { theme = getAutoThemeValue(); }
return theme;
}
- Modify setTheme() in utility.js:718-724
Update the theme resolution logic to handle 'auto' properly:
export function setTheme(theme, sync) {
if (theme === 'auto') {
document.documentElement.setAttribute('data-bs-theme', getAutoThemeValue())
}
else {
document.documentElement.setAttribute('data-bs-theme', theme);
}
if (sync === true) {
const providers = document.querySelectorAll('.bb-theme-mode');
providers.forEach(p => {
const activeItem = p.querySelector(`.dropdown-item[data-bb-theme-value="${theme}"]`);
setActiveTheme(p, activeItem)
})
saveTheme(theme); // Now saves 'auto' instead of the resolved value
}
EventHandler.trigger(document, 'changed.bb.theme', { theme: theme });
}
Benefits
• ✅ System theme preference changes are now respected
• ✅ 'auto' setting persists and behaves as intended
• ✅ Better user experience when switching between light/dark mode at OS level
• ✅ Aligns with the media query listener already in place
Is there an existing issue for this?
Describe the bug
The ThemeProvider component has a logic issue where the 'auto' theme setting is being converted to its actual value ('dark' or 'light') and persisted to localStorage. This causes the theme preference to lose the automatic/responsive behavior on subsequent page loads.
Current Behavior
Expected Behavior
• When user selects "Auto" theme, the string 'auto' should be persisted to localStorage
• On page load, if theme is 'auto', it should be resolved to the actual value only for DOM rendering
• The system should continue to respond to system dark mode preference changes
Interactive render mode
Interactive Server (Interactive server-side rendering (interactive SSR) using Blazor Server)
Steps To Reproduce
utility.js
Exceptions (if any)
No response
.NET Version
NET9.0
Anything else?
Root Cause
The issue exists in two functions in utility.js:718-724:
In getTheme():
In setTheme(): The function doesn't properly handle the 'auto' case before resolution.
Proposed Solution
Remove the early conversion of 'auto' to its actual value:
Update the theme resolution logic to handle 'auto' properly:
Benefits
• ✅ System theme preference changes are now respected
• ✅ 'auto' setting persists and behaves as intended
• ✅ Better user experience when switching between light/dark mode at OS level
• ✅ Aligns with the media query listener already in place