mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-01 03:49:24 +08:00 
			
		
		
		
	FEAT: darkmode (#155)
* darkmode fixes * fix: darkmode: empty beats in active/ hovered state * fix: color for empty beats * fix: navbar background color * Update src/assets/vars.scss Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> * Update src/assets/app.scss Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> * wip, split dark theme style by .dark and store light theme to normal * add back missing css * working switch theme button and tuning dark theme * finish dark theme Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> Co-authored-by: LouisLam <louislam@users.noreply.github.com>
This commit is contained in:
		| @@ -122,7 +122,9 @@ class Monitor extends BeanModel { | ||||
|                         try { | ||||
|                             await this.updateTlsInfo(checkCertificate(res)); | ||||
|                         } catch (e) { | ||||
|                             console.error(e.message) | ||||
|                             if (e.message !== "No TLS certificate in response") { | ||||
|                                 console.error(e.message) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,26 @@ | ||||
|     font-family: ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol,noto color emoji; | ||||
| } | ||||
|  | ||||
| .modal { | ||||
|     backdrop-filter: blur(3px); | ||||
| } | ||||
|  | ||||
| .modal-content { | ||||
|     border-radius: 1rem; | ||||
|     box-shadow: 0 15px 70px rgba(0, 0, 0, .1); | ||||
|  | ||||
|     .dark & { | ||||
|         box-shadow: 0 15px 70px rgb(0 0 0); | ||||
|         background-color: $dark-bg; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .VuePagination__count { | ||||
|     font-size: 13px; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
|  | ||||
| .shadow-box { | ||||
|     overflow: hidden; | ||||
|     box-shadow: 0 15px 70px rgba(0, 0, 0, .1); | ||||
| @@ -29,10 +49,87 @@ | ||||
|         background-color: $highlight; | ||||
|         border-color: $highlight; | ||||
|     } | ||||
|  | ||||
|     .dark & { | ||||
|         color: $dark-font-color2; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .modal-content { | ||||
|     border-radius: 1rem; | ||||
|     backdrop-filter: blur(3px); | ||||
| } | ||||
|  | ||||
|  | ||||
| // Dark Theme override here | ||||
| .dark { | ||||
|     background-color: #090C10; | ||||
|     color: $dark-font-color; | ||||
|  | ||||
|     .shadow-box { | ||||
|         background-color: $dark-bg; | ||||
|     } | ||||
|  | ||||
|     .form-check-input { | ||||
|         background-color: $dark-bg2; | ||||
|     } | ||||
|  | ||||
|     .form-switch .form-check-input { | ||||
|         background-color: #131a21; | ||||
|     } | ||||
|  | ||||
|     a, | ||||
|     .table, | ||||
|     .nav-link { | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
|  | ||||
|     .form-control, | ||||
|     .form-control:focus, | ||||
|     .form-select, | ||||
|     .form-select:focus { | ||||
|         color: $dark-font-color; | ||||
|         background-color: $dark-bg2; | ||||
|     } | ||||
|  | ||||
|     .form-control, .form-select { | ||||
|         border-color: $dark-border-color; | ||||
|     } | ||||
|  | ||||
|     .table-hover > tbody > tr:hover { | ||||
|         --bs-table-accent-bg: #070A10; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
|  | ||||
|     .nav-pills .nav-link.active, .nav-pills .show > .nav-link { | ||||
|         color: $dark-font-color2; | ||||
|     } | ||||
|  | ||||
|     .bg-primary { | ||||
|         color: $dark-font-color2; | ||||
|     } | ||||
|  | ||||
|     .btn-secondary { | ||||
|         color: white; | ||||
|     } | ||||
|  | ||||
|     .btn-close { | ||||
|         opacity: 1; | ||||
|     } | ||||
|  | ||||
|     .modal-header { | ||||
|         border-color: $dark-bg; | ||||
|     } | ||||
|  | ||||
|     .modal-footer { | ||||
|         border-color: $dark-bg; | ||||
|     } | ||||
|  | ||||
|     // Pagination | ||||
|     .page-item.disabled .page-link { | ||||
|         background-color: $dark-bg; | ||||
|         border-color: $dark-border-color; | ||||
|     } | ||||
|  | ||||
|     .page-link { | ||||
|         background-color: $dark-bg; | ||||
|         border-color: $dark-border-color; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,4 +5,10 @@ $link-color: #111; | ||||
| $border-radius: 50rem; | ||||
|  | ||||
| $highlight: #7ce8a4; | ||||
| $highlight-white: #e7faec; | ||||
| $highlight-white: #e7faec; | ||||
|  | ||||
| $dark-font-color: #b1b8c0; | ||||
| $dark-font-color2: #020b05; | ||||
| $dark-bg: #0D1117; | ||||
| $dark-bg2: #070A10; | ||||
| $dark-border-color: #1d2634; | ||||
|   | ||||
| @@ -133,7 +133,7 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars.scss"; | ||||
|  | ||||
| .wrap { | ||||
| @@ -150,6 +150,10 @@ export default { | ||||
|  | ||||
|         &.empty { | ||||
|             background-color: aliceblue; | ||||
|  | ||||
|             .dark & { | ||||
|                 background-color: #d0d3d5; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         &.down { | ||||
| @@ -168,4 +172,10 @@ export default { | ||||
|     } | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     .hp-bar-big .beat.empty{ | ||||
|         background-color: #848484; | ||||
|     } | ||||
| } | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -325,7 +325,7 @@ | ||||
|                                 <p> | ||||
|                                     Status: | ||||
|                                     <span v-if="appriseInstalled" class="text-primary">Apprise is installed</span> | ||||
|                                     <span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise">Read more</a></span> | ||||
|                                     <span v-else class="text-danger">Apprise is not installed. <a href="https://github.com/caronc/apprise" target="_blank">Read more</a></span> | ||||
|                                 </p> | ||||
|                             </div> | ||||
|                         </template> | ||||
| @@ -512,3 +512,13 @@ export default { | ||||
|     }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars.scss"; | ||||
|  | ||||
| .dark { | ||||
|     .modal-dialog .form-text, .modal-dialog p { | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -1,76 +1,78 @@ | ||||
| <template> | ||||
|     <div v-if="! $root.socket.connected && ! $root.socket.firstConnect" class="lost-connection"> | ||||
|         <div class="container-fluid"> | ||||
|             {{ $root.connectionErrorMsg }} | ||||
|     <div :class="$root.theme"> | ||||
|         <div v-if="! $root.socket.connected && ! $root.socket.firstConnect" class="lost-connection"> | ||||
|             <div class="container-fluid"> | ||||
|                 {{ $root.connectionErrorMsg }} | ||||
|             </div> | ||||
|         </div> | ||||
|  | ||||
|         <!-- Desktop header --> | ||||
|         <header v-if="! $root.isMobile" class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom"> | ||||
|             <router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none"> | ||||
|                 <object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg" alt="Logo" /> | ||||
|                 <span class="fs-4 title">Uptime Kuma</span> | ||||
|             </router-link> | ||||
|  | ||||
|             <ul class="nav nav-pills"> | ||||
|                 <li class="nav-item"> | ||||
|                     <router-link to="/dashboard" class="nav-link"> | ||||
|                         <font-awesome-icon icon="tachometer-alt" /> Dashboard | ||||
|                     </router-link> | ||||
|                 </li> | ||||
|                 <li class="nav-item"> | ||||
|                     <router-link to="/settings" class="nav-link"> | ||||
|                         <font-awesome-icon icon="cog" /> Settings | ||||
|                     </router-link> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </header> | ||||
|  | ||||
|         <!-- Mobile header --> | ||||
|         <header v-else class="d-flex flex-wrap justify-content-center pt-2 pb-2 mb-3"> | ||||
|             <router-link to="/dashboard" class="d-flex align-items-center text-dark text-decoration-none"> | ||||
|                 <object class="bi" width="40" height="40" data="/icon.svg" /> | ||||
|                 <span class="fs-4 title ms-2">Uptime Kuma</span> | ||||
|             </router-link> | ||||
|         </header> | ||||
|  | ||||
|         <main> | ||||
|             <!-- Add :key to disable vue router re-use the same component --> | ||||
|             <router-view v-if="$root.loggedIn" :key="$route.fullPath" /> | ||||
|             <Login v-if="! $root.loggedIn && $root.allowLoginDialog" /> | ||||
|         </main> | ||||
|  | ||||
|         <footer> | ||||
|             <div class="container-fluid"> | ||||
|                 Uptime Kuma - | ||||
|                 Version: {{ $root.info.version }} - | ||||
|                 <a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">Check Update On GitHub</a> | ||||
|             </div> | ||||
|         </footer> | ||||
|  | ||||
|         <!-- Mobile Only --> | ||||
|         <div v-if="$root.isMobile" style="width: 100%;height: 60px;" /> | ||||
|         <nav v-if="$root.isMobile" class="bottom-nav"> | ||||
|             <router-link to="/dashboard" class="nav-link" @click="$root.cancelActiveList"> | ||||
|                 <div><font-awesome-icon icon="tachometer-alt" /></div> | ||||
|                 Dashboard | ||||
|             </router-link> | ||||
|  | ||||
|             <a href="#" :class=" { 'router-link-exact-active' : $root.showListMobile } " @click="$root.showListMobile = ! $root.showListMobile"> | ||||
|                 <div><font-awesome-icon icon="list" /></div> | ||||
|                 List | ||||
|             </a> | ||||
|  | ||||
|             <router-link to="/add" class="nav-link" @click="$root.cancelActiveList"> | ||||
|                 <div><font-awesome-icon icon="plus" /></div> | ||||
|                 Add | ||||
|             </router-link> | ||||
|  | ||||
|             <router-link to="/settings" class="nav-link" @click="$root.cancelActiveList"> | ||||
|                 <div><font-awesome-icon icon="cog" /></div> | ||||
|                 Settings | ||||
|             </router-link> | ||||
|         </nav> | ||||
|     </div> | ||||
|  | ||||
|     <!-- Desktop header --> | ||||
|     <header v-if="! $root.isMobile" class="d-flex flex-wrap justify-content-center py-3 mb-3 border-bottom"> | ||||
|         <router-link to="/dashboard" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-dark text-decoration-none"> | ||||
|             <object class="bi me-2 ms-4" width="40" height="40" data="/icon.svg" alt="Logo" /> | ||||
|             <span class="fs-4 title">Uptime Kuma</span> | ||||
|         </router-link> | ||||
|  | ||||
|         <ul class="nav nav-pills"> | ||||
|             <li class="nav-item"> | ||||
|                 <router-link to="/dashboard" class="nav-link"> | ||||
|                     <font-awesome-icon icon="tachometer-alt" /> Dashboard | ||||
|                 </router-link> | ||||
|             </li> | ||||
|             <li class="nav-item"> | ||||
|                 <router-link to="/settings" class="nav-link"> | ||||
|                     <font-awesome-icon icon="cog" /> Settings | ||||
|                 </router-link> | ||||
|             </li> | ||||
|         </ul> | ||||
|     </header> | ||||
|  | ||||
|     <!-- Mobile header --> | ||||
|     <header v-else class="d-flex flex-wrap justify-content-center mt-3 mb-3"> | ||||
|         <router-link to="/dashboard" class="d-flex align-items-center  text-dark text-decoration-none"> | ||||
|             <object class="bi" width="40" height="40" data="/icon.svg" /> | ||||
|             <span class="fs-4 title ms-2">Uptime Kuma</span> | ||||
|         </router-link> | ||||
|     </header> | ||||
|  | ||||
|     <main> | ||||
|         <!-- Add :key to disable vue router re-use the same component --> | ||||
|         <router-view v-if="$root.loggedIn" :key="$route.fullPath" /> | ||||
|         <Login v-if="! $root.loggedIn && $root.allowLoginDialog" /> | ||||
|     </main> | ||||
|  | ||||
|     <footer> | ||||
|         <div class="container-fluid"> | ||||
|             Uptime Kuma - | ||||
|             Version: {{ $root.info.version }} - | ||||
|             <a href="https://github.com/louislam/uptime-kuma/releases" target="_blank" rel="noopener">Check Update On GitHub</a> | ||||
|         </div> | ||||
|     </footer> | ||||
|  | ||||
|     <!-- Mobile Only --> | ||||
|     <div v-if="$root.isMobile" style="width: 100%;height: 60px;" /> | ||||
|     <nav v-if="$root.isMobile" class="bottom-nav"> | ||||
|         <router-link to="/dashboard" class="nav-link" @click="$root.cancelActiveList"> | ||||
|             <div><font-awesome-icon icon="tachometer-alt" /></div> | ||||
|             Dashboard | ||||
|         </router-link> | ||||
|  | ||||
|         <a href="#" :class=" { 'router-link-exact-active' : $root.showListMobile } " @click="$root.showListMobile = ! $root.showListMobile"> | ||||
|             <div><font-awesome-icon icon="list" /></div> | ||||
|             List | ||||
|         </a> | ||||
|  | ||||
|         <router-link to="/add" class="nav-link" @click="$root.cancelActiveList"> | ||||
|             <div><font-awesome-icon icon="plus" /></div> | ||||
|             Add | ||||
|         </router-link> | ||||
|  | ||||
|         <router-link to="/settings" class="nav-link" @click="$root.cancelActiveList"> | ||||
|             <div><font-awesome-icon icon="cog" /></div> | ||||
|             Settings | ||||
|         </router-link> | ||||
|     </nav> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| @@ -103,7 +105,7 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars.scss"; | ||||
|  | ||||
| .bottom-nav { | ||||
| @@ -159,9 +161,24 @@ footer { | ||||
|     color: #AAA; | ||||
|     font-size: 13px; | ||||
|     margin-top: 10px; | ||||
|     margin-bottom: 30px; | ||||
|     padding-bottom: 30px; | ||||
|     margin-left: 10px; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     header { | ||||
|         background-color: #161B22; | ||||
|         border-bottom-color: #161B22 !important; | ||||
|  | ||||
|         span { | ||||
|             color: #F0F6FC; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     .bottom-nav { | ||||
|         background-color: $dark-bg; | ||||
|     } | ||||
| } | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import { FontAwesomeIcon } from "./icon.js"; | ||||
| import EmptyLayout from "./layouts/EmptyLayout.vue"; | ||||
| import Layout from "./layouts/Layout.vue"; | ||||
| import socket from "./mixins/socket"; | ||||
| import theme from "./mixins/theme"; | ||||
| import Dashboard from "./pages/Dashboard.vue"; | ||||
| import DashboardHome from "./pages/DashboardHome.vue"; | ||||
| import Details from "./pages/Details.vue"; | ||||
| @@ -77,6 +78,7 @@ const router = createRouter({ | ||||
| const app = createApp({ | ||||
|     mixins: [ | ||||
|         socket, | ||||
|         theme | ||||
|     ], | ||||
|     data() { | ||||
|         return { | ||||
|   | ||||
| @@ -29,7 +29,7 @@ export default { | ||||
|             notificationList: [], | ||||
|             windowWidth: window.innerWidth, | ||||
|             showListMobile: false, | ||||
|             connectionErrorMsg: "Cannot connect to the socket server. Reconnecting..." | ||||
|             connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|   | ||||
							
								
								
									
										39
									
								
								src/mixins/theme.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/mixins/theme.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| export default { | ||||
|  | ||||
|     data() { | ||||
|         return { | ||||
|             system: (window.matchMedia("(prefers-color-scheme: dark)")) ? "dark" : "light", | ||||
|             userTheme: localStorage.theme, | ||||
|         }; | ||||
|     }, | ||||
|  | ||||
|     mounted() { | ||||
|         // Default Light | ||||
|         if (! this.userTheme) { | ||||
|             this.userTheme = "light"; | ||||
|         } | ||||
|  | ||||
|         document.body.classList.add(this.theme); | ||||
|     }, | ||||
|  | ||||
|     computed: { | ||||
|         theme() { | ||||
|             if (this.userTheme === "auto") { | ||||
|                 return this.system; | ||||
|             } | ||||
|             return this.userTheme; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     watch: { | ||||
|         userTheme(to, from) { | ||||
|             localStorage.theme = to; | ||||
|         }, | ||||
|  | ||||
|         theme(to, from) { | ||||
|             document.body.classList.remove(from); | ||||
|             document.body.classList.add(this.theme); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -89,7 +89,7 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars.scss"; | ||||
|  | ||||
| .container-fluid { | ||||
| @@ -133,4 +133,18 @@ export default { | ||||
|     padding-right: 5px !important; | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     .list { | ||||
|         .item { | ||||
|             &:hover { | ||||
|                 background-color: $dark-bg2; | ||||
|             } | ||||
|  | ||||
|             &.active { | ||||
|                 background-color: $dark-bg2; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| </style> | ||||
|   | ||||
| @@ -169,7 +169,7 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars"; | ||||
|  | ||||
| .num { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|         <span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span> | ||||
|         <span v-if="monitor.type === 'keyword'"> | ||||
|             <br> | ||||
|             <span>Keyword:</span> <span style="color: black">{{ monitor.keyword }}</span> | ||||
|             <span>Keyword:</span> <span class="keyword">{{ monitor.keyword }}</span> | ||||
|         </span> | ||||
|     </p> | ||||
|  | ||||
| @@ -352,4 +352,14 @@ table { | ||||
|         margin: 20px 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| .keyword { | ||||
|     color: black; | ||||
| } | ||||
|  | ||||
| .dark  { | ||||
|     .keyword { | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -20,6 +20,19 @@ | ||||
|                         </select> | ||||
|                     </div> | ||||
|  | ||||
|                     <div class="mb-3"> | ||||
|                         <div class="btn-group" role="group" aria-label="Basic checkbox toggle button group"> | ||||
|                             <input id="btncheck1" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="light"> | ||||
|                             <label class="btn btn-outline-primary" for="btncheck1">Light</label> | ||||
|  | ||||
|                             <input id="btncheck2" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="dark"> | ||||
|                             <label class="btn btn-outline-primary" for="btncheck2">Dark</label> | ||||
|  | ||||
|                             <input id="btncheck3" v-model="$root.userTheme" type="radio" class="btn-check" name="theme" autocomplete="off" value="auto"> | ||||
|                             <label class="btn btn-outline-primary" for="btncheck3">Auto</label> | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
|                     <div> | ||||
|                         <button class="btn btn-primary" type="submit"> | ||||
|                             Save | ||||
| @@ -67,7 +80,7 @@ | ||||
|                 </template> | ||||
|             </div> | ||||
|  | ||||
|             <div class="col-md-6"> | ||||
|             <div class="notification-list col-md-6"> | ||||
|                 <div v-if="$root.isMobile" class="mt-3" /> | ||||
|  | ||||
|                 <h2>Notifications</h2> | ||||
| @@ -201,8 +214,17 @@ export default { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
|     .shadow-box { | ||||
|         padding: 20px; | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars.scss"; | ||||
|  | ||||
| .shadow-box { | ||||
|     padding: 20px; | ||||
| } | ||||
|  | ||||
| .dark { | ||||
|     .list-group-item { | ||||
|         background-color: $dark-bg2; | ||||
|         color: $dark-font-color; | ||||
|     } | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -29,7 +29,7 @@ function ucfirst(str) { | ||||
| exports.ucfirst = ucfirst; | ||||
| function debug(msg) { | ||||
|     if (process.env.NODE_ENV === "development") { | ||||
|         console.log(msg); | ||||
|         console.debug(msg); | ||||
|     } | ||||
| } | ||||
| exports.debug = debug; | ||||
|   | ||||
| @@ -39,6 +39,6 @@ export function ucfirst(str) { | ||||
|  | ||||
| export function debug(msg) { | ||||
|     if (process.env.NODE_ENV === "development") { | ||||
|         console.log(msg) | ||||
|         console.debug(msg); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user