diff --git a/src_assets/common/assets/web/Navbar.vue b/src_assets/common/assets/web/Navbar.vue
index 9e4e1be64f5..838c630f45a 100644
--- a/src_assets/common/assets/web/Navbar.vue
+++ b/src_assets/common/assets/web/Navbar.vue
@@ -1,60 +1,89 @@
-
+
diff --git a/src_assets/common/assets/web/ThemeToggle.vue b/src_assets/common/assets/web/ThemeToggle.vue
new file mode 100644
index 00000000000..7c34916adc9
--- /dev/null
+++ b/src_assets/common/assets/web/ThemeToggle.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html
index 0fd0651aa13..63d2be9e700 100644
--- a/src_assets/common/assets/web/apps.html
+++ b/src_assets/common/assets/web/apps.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html
index 722d55c7600..7df3880ceb4 100644
--- a/src_assets/common/assets/web/config.html
+++ b/src_assets/common/assets/web/config.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
@@ -13,12 +13,6 @@
.buttons {
padding: 1em 0;
}
-
- .ms-item {
- background-color: #ccc;
- font-size: 12px;
- font-weight: bold;
- }
diff --git a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue
index 851e1e03a7e..58695ba1d2a 100644
--- a/src_assets/common/assets/web/configs/tabs/AudioVideo.vue
+++ b/src_assets/common/assets/web/configs/tabs/AudioVideo.vue
@@ -87,3 +87,6 @@ const config = ref(props.config)
+
+
diff --git a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue
index 74bd5d9f87b..7fb5ca3b0f9 100644
--- a/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue
+++ b/src_assets/common/assets/web/configs/tabs/audiovideo/DisplayModesSettings.vue
@@ -65,3 +65,11 @@ const fpsIn = ref("")
{{ $t('config.res_fps_desc') }}
+
+
diff --git a/src_assets/common/assets/web/index.html b/src_assets/common/assets/web/index.html
index a5b5a6f1199..0dac538f313 100644
--- a/src_assets/common/assets/web/index.html
+++ b/src_assets/common/assets/web/index.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
diff --git a/src_assets/common/assets/web/password.html b/src_assets/common/assets/web/password.html
index 9a47cc565c8..639c82b7401 100644
--- a/src_assets/common/assets/web/password.html
+++ b/src_assets/common/assets/web/password.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
diff --git a/src_assets/common/assets/web/pin.html b/src_assets/common/assets/web/pin.html
index 359c5e527ba..f3139e123cc 100644
--- a/src_assets/common/assets/web/pin.html
+++ b/src_assets/common/assets/web/pin.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
diff --git a/src_assets/common/assets/web/public/assets/css/sunshine.css b/src_assets/common/assets/web/public/assets/css/sunshine.css
index de2acffee46..843600feebd 100644
--- a/src_assets/common/assets/web/public/assets/css/sunshine.css
+++ b/src_assets/common/assets/web/public/assets/css/sunshine.css
@@ -2,3 +2,15 @@
[v-cloak] {
display: none;
}
+
+[data-bs-theme=dark] .element {
+ color: var(--bs-primary-text-emphasis);
+ background-color: var(--bs-primary-bg-subtle);
+}
+
+@media (prefers-color-scheme: dark) {
+ .element {
+ color: var(--bs-primary-text-emphasis);
+ background-color: var(--bs-primary-bg-subtle);
+ }
+}
diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json
index ad5133cdc6f..a743d6de428 100644
--- a/src_assets/common/assets/web/public/assets/locale/en.json
+++ b/src_assets/common/assets/web/public/assets/locale/en.json
@@ -336,6 +336,10 @@
"home": "Home",
"password": "Change Password",
"pin": "Pin",
+ "theme_auto": "Auto",
+ "theme_dark": "Dark",
+ "theme_light": "Light",
+ "toggle_theme": "Theme",
"troubleshoot": "Troubleshooting"
},
"password": {
diff --git a/src_assets/common/assets/web/theme.js b/src_assets/common/assets/web/theme.js
new file mode 100644
index 00000000000..a1f497802fa
--- /dev/null
+++ b/src_assets/common/assets/web/theme.js
@@ -0,0 +1,84 @@
+const getStoredTheme = () => localStorage.getItem('theme')
+const setStoredTheme = theme => localStorage.setItem('theme', theme)
+
+export const getPreferredTheme = () => {
+ const storedTheme = getStoredTheme()
+ if (storedTheme) {
+ return storedTheme
+ }
+
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
+}
+
+const setTheme = theme => {
+ if (theme === 'auto') {
+ document.documentElement.setAttribute(
+ 'data-bs-theme',
+ (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
+ )
+ } else {
+ document.documentElement.setAttribute('data-bs-theme', theme)
+ }
+}
+
+export const showActiveTheme = (theme, focus = false) => {
+ const themeSwitcher = document.querySelector('#bd-theme')
+
+ if (!themeSwitcher) {
+ return
+ }
+
+ const themeSwitcherText = document.querySelector('#bd-theme-text')
+ const activeThemeIcon = document.querySelector('.theme-icon-active i')
+ const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
+ const classListOfActiveBtn = btnToActive.querySelector('i').classList
+
+ document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
+ element.classList.remove('active')
+ element.setAttribute('aria-pressed', 'false')
+ })
+
+ btnToActive.classList.add('active')
+ btnToActive.setAttribute('aria-pressed', 'true')
+ activeThemeIcon.classList.remove(...activeThemeIcon.classList.values())
+ activeThemeIcon.classList.add(...classListOfActiveBtn)
+ const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.textContent.trim()})`
+ themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)
+
+ if (focus) {
+ themeSwitcher.focus()
+ }
+}
+
+export function setupThemeToggleListener() {
+ document.querySelectorAll('[data-bs-theme-value]')
+ .forEach(toggle => {
+ toggle.addEventListener('click', () => {
+ const theme = toggle.getAttribute('data-bs-theme-value')
+ setStoredTheme(theme)
+ setTheme(theme)
+ showActiveTheme(theme, true)
+ })
+ })
+
+ showActiveTheme(getPreferredTheme(), false)
+}
+
+export function loadAutoTheme() {
+ (() => {
+ 'use strict'
+
+ setTheme(getPreferredTheme())
+
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
+ const storedTheme = getStoredTheme()
+ if (storedTheme !== 'light' && storedTheme !== 'dark') {
+ setTheme(getPreferredTheme())
+ }
+ })
+
+ window.addEventListener('DOMContentLoaded', () => {
+ showActiveTheme(getPreferredTheme())
+ })
+ })()
+}
diff --git a/src_assets/common/assets/web/troubleshooting.html b/src_assets/common/assets/web/troubleshooting.html
index 00497741368..0adc16542af 100644
--- a/src_assets/common/assets/web/troubleshooting.html
+++ b/src_assets/common/assets/web/troubleshooting.html
@@ -1,5 +1,5 @@
-
+
<%- header %>
diff --git a/src_assets/common/assets/web/welcome.html b/src_assets/common/assets/web/welcome.html
index cf1e74ba8e6..18c67b2ee79 100644
--- a/src_assets/common/assets/web/welcome.html
+++ b/src_assets/common/assets/web/welcome.html
@@ -1,5 +1,5 @@
-
+
<%- header %>