From: Alexander Moisseev Date: Sun, 2 Nov 2025 13:14:21 +0000 (+0300) Subject: [Feature] WebUI: Implement dark mode X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c9e5a8df6f44c1490318c58f351b88d52ac182f1;p=thirdparty%2Frspamd.git [Feature] WebUI: Implement dark mode --- diff --git a/interface/css/rspamd.css b/interface/css/rspamd.css index d23dddac16..a43f1e5e1c 100644 --- a/interface/css/rspamd.css +++ b/interface/css/rspamd.css @@ -31,6 +31,50 @@ THE SOFTWARE. /* Tweak bootstrap 5 colors for better accessibility */ --bs-danger-rgb: 221, 0, 0; --bs-success-rgb: 40, 139, 69; + + /* Light mode (default) colors */ + --rspamd-bg-card-gradient-start: #f9f9f9; + --rspamd-bg-card-gradient-end: #ededed; + --rspamd-text-primary: #000000; + --rspamd-text-secondary: #666666; + --rspamd-nav-active-bg: #e7e7e7; + --rspamd-sidebar-bg: #ffffee; + --rspamd-symbol-description-color: #484848; + --rspamd-symbol-negative-bg: #eef9e7; + --rspamd-symbol-positive-bg: #fbe9e5; + --rspamd-symbol-special-bg: #e2e9fe; + --rspamd-symbol-hover-bg: #e6e6e6; + --rspamd-table-danger-bg: #fbe9e5; + --rspamd-table-success-bg: #eef9e7; + --rspamd-table-warning-bg: #fff8e6; + --rspamd-bs-table-bg: #f8f9fa; + + /* Light mode logo display rules */ + --rspamd-display-logo-light: inline; + --rspamd-display-logo-dark: none; +} + +[data-theme="dark"] { + /* Dark mode colors */ + --rspamd-bg-card-gradient-start: #2a2a2a; + --rspamd-bg-card-gradient-end: #252525; + --rspamd-text-primary: #cccccc; + --rspamd-text-secondary: #b0b0b0; + --rspamd-nav-active-bg: #404040; + --rspamd-sidebar-bg: #444422; + --rspamd-symbol-description-color: #848484; + --rspamd-symbol-negative-bg: #1a3a1a; + --rspamd-symbol-positive-bg: #3a1a1a; + --rspamd-symbol-special-bg: #3a3a5a; + --rspamd-symbol-hover-bg: #000000; + --rspamd-table-danger-bg: #390b00; + --rspamd-table-success-bg: #102d00; + --rspamd-table-warning-bg: #43380b; + --rspamd-bs-table-bg: #272b2f; + + /* Dark mode logo display rules */ + --rspamd-display-logo-light: none; + --rspamd-display-logo-dark: inline; } /* bootstrap 4 overrides */ @@ -45,25 +89,24 @@ small, font-size: 85%; } .text-secondary { - color: #666 !important; + color: var(--rspamd-text-secondary) !important; } .navbar { padding-top: 0; padding-bottom: 0; margin-bottom: 20px; - border-bottom: 1px solid rgb(231 231 231); } .nav-pills .nav-link.active { - background-color: #e7e7e7; + background-color: var(--rspamd-nav-active-bg); } .danger > td { - background-color: #fbe9e5; + background-color: var(--rspamd-table-danger-bg); } .success > td { - background-color: #eef9e7; + background-color: var(--rspamd-table-success-bg); } td.warning { - background-color: #fff8e6; + background-color: var(--rspamd-table-warning-bg); } @media (max-width: 1199px) { .navbar-collapse.order-3 { @@ -140,10 +183,66 @@ textarea { border-bottom-right-radius:4px } +/* FooTable dark mode */ +[data-theme="dark"] .footable .pagination .footable-page-link, +[data-theme="dark"] .footable .form-control { + color: #aaaaaa; + background-color: var(--bs-body-bg); + border-color: var(--bs-border-color) +} +[data-theme="dark"] .footable .btn-primary { + color: #ffffff; + background-color: #0d6efd; + border-color: #0d6efd; +} +[data-theme="dark"] .footable .btn-primary:hover { + background-color: #0b5ed7; + border-color: #0a58ca; +} +[data-theme="dark"] .footable .btn-default { + color: #ffffff; + background-color: #6c757d; + border-color: #6c757d; +} +[data-theme="dark"] .footable .btn-default:hover, +[data-theme="dark"] .footable .open > .dropdown-toggle.btn-default { + background-color: #5c636a; + border-color: #565e64; +} + +[data-theme="dark"] .footable .dropdown-menu { + background-color: var(--bs-body-bg); + border-color: var(--bs-dropdown-border-color); +} +[data-theme="dark"] .footable .dropdown-menu > li > a { + color: #aaaaaa; +} +[data-theme="dark"] .footable .dropdown-menu > li > a:hover { + background-color: var(--bs-dropdown-link-hover-bg); +} + +[data-theme="dark"] .footable .pagination > li a:hover { + background-color: var(--bs-nav-link-disabled-color); + border-color: var(--bs-border-color); +} +[data-theme="dark"] .footable .pagination .footable-page-link:hover { + color: var(--bs-nav-link-hover-color); +} +[data-theme="dark"] .footable .pagination .footable-page.active .footable-page-link { + background-color: var(--rspamd-nav-active-bg); + color: var(--rspamd-text-primary); +} + /* local overrides */ .navbar-brand > img { height: 50px; } +.logo-light { + display: var(--rspamd-display-logo-light); +} +.logo-dark { + display: var(--rspamd-display-logo-dark); +} .btn-group > .btn.radius-right { border-top-right-radius: var(--bs-btn-border-radius) !important; border-bottom-right-radius: var(--bs-btn-border-radius) !important; @@ -173,7 +272,6 @@ table#symbolsTable input[type="number"] { } .alert { margin-bottom: 4px; - color: #c09853; } .alert.alert-modal { top: 0; @@ -182,17 +280,20 @@ table#symbolsTable input[type="number"] { display: inline-block; padding-left: 35px; } -.alert-success { +[data-theme="light"] .alert-success { color: #468847; background: #dff0d8; border-color: #d6e9c6; } -.alert-danger { +[data-theme="light"] .alert-danger { color: #b94a48; background: #f2dede; border-color: #eed3d7; } -.alert-info { +[data-theme="light"] .alert-warning { + color: #c09853; +} +[data-theme="light"] .alert-info { color: #3a87ad; background: #d9edf7; border-color: #bce8f1; @@ -207,16 +308,14 @@ table#symbolsTable input[type="number"] { .card-header, .modal-header { - background-color: #f3f3f3; - background-image: linear-gradient(to bottom, #fdfdfd, #eaeaea); + background-image: linear-gradient(to bottom, var(--rspamd-bg-card-gradient-start), var(--rspamd-bg-card-gradient-end)); } .card-header .h6 { font-size: 0.857rem; } .stat-box { - background-color: #f3f3f3; - background-image: linear-gradient(to bottom, #f9f9f9, #ededed); + background-image: linear-gradient(to bottom, var(--rspamd-bg-card-gradient-start), var(--rspamd-bg-card-gradient-end)); line-height: 1; } .stat-box:not(.float-end) { @@ -236,25 +335,25 @@ table#symbolsTable input[type="number"] { padding-right: 2px; } .symbol-default:hover { - background-color: #e6e6e6; + background-color: var(--rspamd-symbol-hover-bg); } .symbol-negative.symbol-negative { - background-color: #eef9e7; + background-color: var(--rspamd-symbol-negative-bg); } .symbol-positive.symbol-positive { - background-color: #fbe9e5; + background-color: var(--rspamd-symbol-positive-bg); } .symbol-special { - background-color: #e2e9fe; + background-color: var(--rspamd-symbol-special-bg); } .symbol-negative:hover { - background-color: #dcf9d3; + background-color: var(--rspamd-symbol-hover-bg); } .symbol-positive:hover { - background-color: #fbd6d1; + background-color: var(--rspamd-symbol-hover-bg); } .symbol-special:hover { - background-color: #cddbff; + background-color: var(--rspamd-symbol-hover-bg); } /* For symbol description display on hover/focus */ @@ -263,7 +362,7 @@ table#symbolsTable input[type="number"] { } .symbol-description { display: none; - color: #484848; + color: var(--rspamd-symbol-description-color); } .symbol-default:hover .symbol-description, .symbol-default:focus .symbol-description { @@ -358,6 +457,8 @@ table#symbolsTable input[type="number"] { text-align: left; font-size: 12px; z-index: 100; + + --bs-table-bg: var(--rspamd-bs-table-bg); } #rrd-table td { color: inherit; @@ -477,9 +578,13 @@ table#symbolsTable input[type="number"] { transition-property: flex-basis, max-width, width; } +.sidebar, +#sidebar-tab-left > a, +#sidebar-tab-right > a { + background-color: var(--rspamd-sidebar-bg); +} .sidebar { padding: 8px; - background-color: #ffe; transition: margin 0.3s ease; } .collapsed { @@ -509,11 +614,10 @@ table#symbolsTable input[type="number"] { } .sidebar-nav .nav-link, .sidebar-nav .nav-link:hover { - border: 1px solid #ddd; + border: 1px solid var(--bs-card-border-color); } #sidebar-tab-left > a, #sidebar-tab-right > a { - background-color: #ffe; margin-left: 12px; margin-right: 12px; } @@ -535,8 +639,8 @@ table#symbolsTable input[type="number"] { display: block; } #content { - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; + border-left: 1px solid var(--bs-card-border-color); + border-right: 1px solid var(--bs-card-border-color); } #sidebar-tab-left { display: flex; @@ -553,8 +657,8 @@ table#symbolsTable input[type="number"] { border-bottom-right-radius: 3.5px; } #content { - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; + border-top: 1px solid var(--bs-card-border-color); + border-bottom: 1px solid var(--bs-card-border-color); } #sidebar-tab-right { bottom: 0; @@ -640,3 +744,48 @@ table#symbolsTable input[type="number"] { filter: invert(1); } } + +/* Dark mode overrides for Bootstrap tables */ +[data-theme="dark"] .table { + --bs-table-color-state: var(--rspamd-text-primary); +} + +/* Dark mode overrides for D3Evolution */ +[data-theme="dark"] .d3evolution .grid line { + stroke: #404040; +} +[data-theme="dark"] .d3evolution .cursor .background { + stroke: #1a1a1a; +} +[data-theme="dark"] .d3evolution .cursor .x.foreground { + stroke: #4db8ff; +} +[data-theme="dark"] .d3evolution .cursor circle.foreground { + stroke: white; +} +[data-theme="dark"] .d3evolution .chart-title, +[data-theme="dark"] .d3evolution .axis, +[data-theme="dark"] .d3evolution .legend, +[data-theme="dark"] .d3evolution .y.label, +[data-theme="dark"] .d3evolution .cursor-time { + fill: var(--rspamd-text-primary); +} + +/* Dark mode overrides for D3Pie */ +[data-theme="dark"] .d3pie .chart-title, +[data-theme="dark"] .d3pie .outer-label, +[data-theme="dark"] .d3pie .total-text, +[data-theme="dark"] .d3pie .total-value { + fill: var(--rspamd-text-primary); +} +[data-theme="dark"] .d3pie .slice-g.first-slice .inner-label { + fill: #666666; +} +[data-theme="dark"] .d3pie-tooltip { + background-color: rgb(80 80 80 / 80%); +} + +/* Dark mode for NProgress */ +[data-theme="dark"] #nprogress .bar { + background: #4db8ff; +} diff --git a/interface/img/rspamd_logo_navbar_dark.png b/interface/img/rspamd_logo_navbar_dark.png new file mode 100644 index 0000000000..0d62b2da67 Binary files /dev/null and b/interface/img/rspamd_logo_navbar_dark.png differ diff --git a/interface/index.html b/interface/index.html index 1c12badf51..909b16ffa7 100644 --- a/interface/index.html +++ b/interface/index.html @@ -28,10 +28,11 @@ -