diff --git a/src/tailwind.config.js b/src/tailwind.config.js index 33ba6746..bc7b40da 100644 --- a/src/tailwind.config.js +++ b/src/tailwind.config.js @@ -3,7 +3,7 @@ 'use strict'; module.exports = { - darkMode: 'media', + darkMode: 'selector', content: ['./www/**/*.{html,js}'], theme: { screens: { diff --git a/src/www/css/app.css b/src/www/css/app.css index 9c6c85fd..2564cdd8 100644 --- a/src/www/css/app.css +++ b/src/www/css/app.css @@ -590,6 +590,18 @@ video { } } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + .visible { visibility: visible; } @@ -692,8 +704,8 @@ video { margin-bottom: 2.5rem; } -.mb-2 { - margin-bottom: 0.5rem; +.mb-4 { + margin-bottom: 1rem; } .mb-5 { @@ -736,6 +748,10 @@ video { margin-top: 1.25rem; } +.mt-6 { + margin-top: 1.5rem; +} + .mt-px { margin-top: 1px; } @@ -804,10 +820,18 @@ video { height: 1rem; } +.h-5 { + height: 1.25rem; +} + .h-6 { height: 1.5rem; } +.h-8 { + height: 2rem; +} + .min-h-screen { min-height: 100vh; } @@ -925,6 +949,10 @@ video { flex-wrap: wrap; } +.items-end { + align-items: flex-end; +} + .items-center { align-items: center; } @@ -1087,6 +1115,14 @@ video { --tw-bg-opacity: 0.5; } +.fill-gray-400 { + fill: #9ca3af; +} + +.fill-gray-600 { + fill: #4b5563; +} + .p-1 { padding: 0.25rem; } @@ -1271,6 +1307,11 @@ video { color: rgb(17 24 39 / var(--tw-text-opacity)); } +.text-neutral-400 { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} + .text-red-600 { --tw-text-opacity: 1; color: rgb(220 38 38 / var(--tw-text-opacity)); @@ -1458,6 +1499,10 @@ video { opacity: 1; } +.peer:checked ~ .peer-checked\:fill-gray-600 { + fill: #4b5563; +} + @media (min-width: 450px) { .xxs\:flex-row { flex-direction: row; @@ -1607,208 +1652,222 @@ video { } } -@media (prefers-color-scheme: dark) { - .dark\:border-neutral-500 { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); - } +.dark\:border-neutral-500:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} - .dark\:border-neutral-600 { - --tw-border-opacity: 1; - border-color: rgb(82 82 82 / var(--tw-border-opacity)); - } +.dark\:border-neutral-600:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(82 82 82 / var(--tw-border-opacity)); +} - .dark\:border-neutral-800 { - --tw-border-opacity: 1; - border-color: rgb(38 38 38 / var(--tw-border-opacity)); - } +.dark\:border-neutral-800:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(38 38 38 / var(--tw-border-opacity)); +} - .dark\:border-red-600 { - --tw-border-opacity: 1; - border-color: rgb(220 38 38 / var(--tw-border-opacity)); - } +.dark\:border-red-600:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(220 38 38 / var(--tw-border-opacity)); +} - .dark\:bg-black { - --tw-bg-opacity: 1; - background-color: rgb(0 0 0 / var(--tw-bg-opacity)); - } +.dark\:bg-black:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-400 { - --tw-bg-opacity: 1; - background-color: rgb(163 163 163 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-400:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(163 163 163 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-500 { - --tw-bg-opacity: 1; - background-color: rgb(115 115 115 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-500:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(115 115 115 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-600 { - --tw-bg-opacity: 1; - background-color: rgb(82 82 82 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-600:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(82 82 82 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-700 { - --tw-bg-opacity: 1; - background-color: rgb(64 64 64 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-700:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(64 64 64 / var(--tw-bg-opacity)); +} - .dark\:bg-neutral-800 { - --tw-bg-opacity: 1; - background-color: rgb(38 38 38 / var(--tw-bg-opacity)); - } +.dark\:bg-neutral-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(38 38 38 / var(--tw-bg-opacity)); +} - .dark\:bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } +.dark\:bg-red-100:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); +} - .dark\:bg-red-600 { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } +.dark\:bg-red-600:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} - .dark\:bg-red-800 { - --tw-bg-opacity: 1; - background-color: rgb(153 27 27 / var(--tw-bg-opacity)); - } +.dark\:bg-red-800:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} - .dark\:text-gray-500 { - --tw-text-opacity: 1; - color: rgb(107 114 128 / var(--tw-text-opacity)); - } +.dark\:fill-neutral-400:where(.dark, .dark *) { + fill: #a3a3a3; +} - .dark\:text-neutral-200 { - --tw-text-opacity: 1; - color: rgb(229 229 229 / var(--tw-text-opacity)); - } +.dark\:fill-neutral-600:where(.dark, .dark *) { + fill: #525252; +} - .dark\:text-neutral-300 { - --tw-text-opacity: 1; - color: rgb(212 212 212 / var(--tw-text-opacity)); - } +.dark\:text-gray-500:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity)); +} - .dark\:text-neutral-400 { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:text-neutral-200:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(229 229 229 / var(--tw-text-opacity)); +} - .dark\:text-neutral-50 { - --tw-text-opacity: 1; - color: rgb(250 250 250 / var(--tw-text-opacity)); - } +.dark\:text-neutral-300:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(212 212 212 / var(--tw-text-opacity)); +} - .dark\:text-neutral-500 { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:text-neutral-400:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:text-neutral-600 { - --tw-text-opacity: 1; - color: rgb(82 82 82 / var(--tw-text-opacity)); - } +.dark\:text-neutral-50:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(250 250 250 / var(--tw-text-opacity)); +} - .dark\:text-red-300 { - --tw-text-opacity: 1; - color: rgb(252 165 165 / var(--tw-text-opacity)); - } +.dark\:text-neutral-500:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:text-red-600 { - --tw-text-opacity: 1; - color: rgb(220 38 38 / var(--tw-text-opacity)); - } +.dark\:text-neutral-600:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(82 82 82 / var(--tw-text-opacity)); +} - .dark\:text-white { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - } +.dark\:text-red-300:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(252 165 165 / var(--tw-text-opacity)); +} - .dark\:opacity-50 { - opacity: 0.5; - } +.dark\:text-red-600:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(220 38 38 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-400::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:text-white:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-400::placeholder { - --tw-text-opacity: 1; - color: rgb(163 163 163 / var(--tw-text-opacity)); - } +.dark\:opacity-50:where(.dark, .dark *) { + opacity: 0.5; +} - .dark\:placeholder\:text-neutral-500::-moz-placeholder { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:placeholder\:text-neutral-500::placeholder { - --tw-text-opacity: 1; - color: rgb(115 115 115 / var(--tw-text-opacity)); - } +.dark\:placeholder\:text-neutral-400:where(.dark, .dark *)::placeholder { + --tw-text-opacity: 1; + color: rgb(163 163 163 / var(--tw-text-opacity)); +} - .dark\:hover\:border-neutral-600:hover { - --tw-border-opacity: 1; - border-color: rgb(82 82 82 / var(--tw-border-opacity)); - } +.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::-moz-placeholder { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:hover\:border-red-600:hover { - --tw-border-opacity: 1; - border-color: rgb(220 38 38 / var(--tw-border-opacity)); - } +.dark\:placeholder\:text-neutral-500:where(.dark, .dark *)::placeholder { + --tw-text-opacity: 1; + color: rgb(115 115 115 / var(--tw-text-opacity)); +} - .dark\:hover\:bg-neutral-500:hover { - --tw-bg-opacity: 1; - background-color: rgb(115 115 115 / var(--tw-bg-opacity)); - } +.dark\:hover\:border-neutral-600:hover:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(82 82 82 / var(--tw-border-opacity)); +} - .dark\:hover\:bg-neutral-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(82 82 82 / var(--tw-bg-opacity)); - } +.dark\:hover\:border-red-600:hover:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(220 38 38 / var(--tw-border-opacity)); +} - .dark\:hover\:bg-red-600:hover { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); - } +.dark\:hover\:bg-neutral-500:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(115 115 115 / var(--tw-bg-opacity)); +} - .dark\:hover\:bg-red-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(185 28 28 / var(--tw-bg-opacity)); - } +.dark\:hover\:bg-neutral-600:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(82 82 82 / var(--tw-bg-opacity)); +} - .dark\:hover\:bg-red-800:hover { - --tw-bg-opacity: 1; - background-color: rgb(153 27 27 / var(--tw-bg-opacity)); - } +.dark\:hover\:bg-red-600:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-neutral-700:hover { - --tw-text-opacity: 1; - color: rgb(64 64 64 / var(--tw-text-opacity)); - } +.dark\:hover\:bg-red-700:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(185 28 28 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-red-100:hover { - --tw-text-opacity: 1; - color: rgb(254 226 226 / var(--tw-text-opacity)); - } +.dark\:hover\:bg-red-800:hover:where(.dark, .dark *) { + --tw-bg-opacity: 1; + background-color: rgb(153 27 27 / var(--tw-bg-opacity)); +} - .dark\:hover\:text-white:hover { - --tw-text-opacity: 1; - color: rgb(255 255 255 / var(--tw-text-opacity)); - } +.dark\:hover\:text-neutral-700:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(64 64 64 / var(--tw-text-opacity)); +} - .dark\:focus\:border-neutral-500:focus { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); - } +.dark\:hover\:text-red-100:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(254 226 226 / var(--tw-text-opacity)); +} - .dark\:focus\:border-red-800:focus { - --tw-border-opacity: 1; - border-color: rgb(153 27 27 / var(--tw-border-opacity)); - } +.dark\:hover\:text-white:hover:where(.dark, .dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); +} - .focus\:dark\:border-neutral-500:focus { - --tw-border-opacity: 1; - border-color: rgb(115 115 115 / var(--tw-border-opacity)); - } +.dark\:focus\:border-neutral-500:focus:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} + +.dark\:focus\:border-red-800:focus:where(.dark, .dark *) { + --tw-border-opacity: 1; + border-color: rgb(153 27 27 / var(--tw-border-opacity)); +} + +.focus\:dark\:border-neutral-500:where(.dark, .dark *):focus { + --tw-border-opacity: 1; + border-color: rgb(115 115 115 / var(--tw-border-opacity)); +} + +.group:hover .group-hover\:dark\:fill-neutral-500:where(.dark, .dark *) { + fill: #737373; +} + +.peer:checked ~ .peer-checked\:dark\:fill-neutral-400:where(.dark, .dark *) { + fill: #a3a3a3; } diff --git a/src/www/index.html b/src/www/index.html index 2b1b7ba8..d5fdd956 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -18,25 +18,54 @@
-
+
- - {{$t("logout")}} - - - - - -

- - WireGuard -

-

- +
+

+ WireGuard +

+ + + + + + {{$t("logout")}} + + + + +
+
diff --git a/src/www/js/app.js b/src/www/js/app.js index a2536ed6..472c6d3d 100644 --- a/src/www/js/app.js +++ b/src/www/js/app.js @@ -23,9 +23,6 @@ function bytes(bytes, decimals, kib, maxunit) { return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; } -const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); -const theme = darkModeMediaQuery.matches ? 'dark' : 'light'; - const i18n = new VueI18n({ locale: localStorage.getItem('lang') || 'en', fallbackLocale: 'en', @@ -40,9 +37,9 @@ const UI_CHART_TYPES = [ ]; const CHART_COLORS = { - rx: { light: 'rgba(0,0,0,0.2)', dark: 'rgba(255,255,255,0.3)' }, - tx: { light: 'rgba(0,0,0,0.3)', dark: 'rgba(255,255,255,0.5)' }, - gradient: { light: ['rgba(0,0,0,0.1)', 'rgba(0,0,0,0)'], dark: ['rgba(255,255,255,0.1)', 'rgba(255,255,255,0)'] }, + rx: { light: 'rgba(128,128,128,0.3)', dark: 'rgba(255,255,255,0.3)' }, + tx: { light: 'rgba(128,128,128,0.4)', dark: 'rgba(255,255,255,0.3)' }, + gradient: { light: ['rgba(0,0,0,1.0)', 'rgba(0,0,0,1.0)'], dark: ['rgba(128,128,128,0)', 'rgba(128,128,128,0)'] }, }; new Vue({ @@ -71,10 +68,12 @@ new Vue({ currentRelease: null, latestRelease: null, - isDark: null, uiTrafficStats: false, uiChartType: 0, + uiShowCharts: localStorage.getItem('uiShowCharts') === "1" ? true : false, + uiTheme: localStorage.theme || 'auto', + prefersDarkScheme: window.matchMedia('(prefers-color-scheme: dark)'), chartOptions: { chart: { @@ -100,11 +99,10 @@ new Vue({ gradient: { shade: 'dark', type: 'vertical', - shadeIntensity: 1, - gradientToColors: CHART_COLORS.gradient[theme], - inverseColors: true, - opacityFrom: 1, - opacityTo: 1, + shadeIntensity: 0, + gradientToColors: CHART_COLORS.gradient[this.theme], + inverseColors: false, + opacityTo: 0, stops: [0, 100], }, }, @@ -301,15 +299,26 @@ new Vue({ .finally(() => this.refresh().catch(console.error)); }, toggleTheme() { - if (this.isDark) { - localStorage.theme = 'light'; - document.documentElement.classList.remove('dark'); - } else { - localStorage.theme = 'dark'; - document.documentElement.classList.add('dark'); + const themes = ['light', 'dark', 'auto']; + const currentIndex = themes.indexOf(this.uiTheme); + const newIndex = (currentIndex + 1) % themes.length; + this.uiTheme = themes[newIndex]; + localStorage.theme = this.uiTheme; + this.setTheme(this.uiTheme); + }, + setTheme(theme) { + const { classList } = document.documentElement; + const shouldAddDarkClass = theme === 'dark' || (theme === 'auto' && this.prefersDarkScheme.matches); + classList.toggle('dark', shouldAddDarkClass); + }, + handlePrefersChange(e) { + if (localStorage.theme === 'auto') { + this.setTheme(e.matches ? 'dark' : 'light'); } - this.isDark = !this.isDark; }, + toggleCharts() { + localStorage.setItem('uiShowCharts', this.uiShowCharts ? 1 : 0); + } }, filters: { bytes, @@ -318,10 +327,8 @@ new Vue({ }, }, mounted() { - this.isDark = false; - if (localStorage.theme === 'dark') { - this.isDark = true; - } + this.prefersDarkScheme.addListener(this.handlePrefersChange); + this.setTheme(this.uiTheme); this.api = new API(); this.api.getSession() @@ -395,7 +402,7 @@ new Vue({ chartOptionsTX() { const opts = { ...this.chartOptions, - colors: [CHART_COLORS.tx[theme]], + colors: [CHART_COLORS.tx[this.theme]], }; opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false; opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth; @@ -404,14 +411,20 @@ new Vue({ chartOptionsRX() { const opts = { ...this.chartOptions, - colors: [CHART_COLORS.rx[theme]], + colors: [CHART_COLORS.rx[this.theme]], }; opts.chart.type = UI_CHART_TYPES[this.uiChartType].type || false; opts.stroke.width = UI_CHART_TYPES[this.uiChartType].strokeWidth; return opts; }, updateCharts() { - return this.uiChartType > 0; + return this.uiChartType > 0 && this.uiShowCharts; + }, + theme() { + if (this.uiTheme === 'auto') { + return this.prefersDarkScheme.matches ? 'dark' : 'light'; + } + return this.uiTheme; }, }, }); diff --git a/src/www/js/i18n.js b/src/www/js/i18n.js index a1962708..82fe64c8 100644 --- a/src/www/js/i18n.js +++ b/src/www/js/i18n.js @@ -28,6 +28,8 @@ const messages = { // eslint-disable-line no-unused-vars downloadConfig: 'Download Configuration', madeBy: 'Made by', donate: 'Donate', + toggleCharts: 'Show/hide Charts', + theme: { dark: 'Dark theme', light: 'Light theme', auto: 'Auto theme' } }, ua: { name: 'Ім`я',