Implements a dark mode override system for DevExpress Blazor themes lacking native dark support. Adds a JS function to toggle a dx-dark class on <html>, updates ThemeState to detect native dark themes, and applies targeted CSS variable overrides for consistent dark styling. Disables prerendering to ensure JS interop, and improves theme switching logic and documentation.
290 lines
8.1 KiB
CSS
290 lines
8.1 KiB
CSS
:root {
|
||
--global-size: 16px;
|
||
}
|
||
|
||
html, body {
|
||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||
font-size: var(--global-size);
|
||
}
|
||
|
||
/* Theme-Variablen */
|
||
.app-light {
|
||
--band-editor-bg: #f8f9fa;
|
||
--band-editor-border: #dee2e6;
|
||
--band-toggle-hover-bg: #e9ecef;
|
||
--grid-stripe-bg: rgba(0, 0, 0, 0.03);
|
||
}
|
||
|
||
.app-dark {
|
||
background-color: #1b1b1b;
|
||
color: #f1f1f1;
|
||
--band-editor-bg: #2d2d2d;
|
||
--band-editor-border: #444444;
|
||
--band-toggle-hover-bg: #3a3a3a;
|
||
--grid-stripe-bg: rgba(255, 255, 255, 0.04);
|
||
}
|
||
|
||
a, .btn-link {
|
||
color: #006bb7;
|
||
}
|
||
|
||
.app-dark a, .app-dark .btn-link {
|
||
color: #6cb6ff;
|
||
}
|
||
|
||
.btn-primary {
|
||
color: #fff;
|
||
background-color: #1b6ec2;
|
||
border-color: #1861ac;
|
||
}
|
||
|
||
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
||
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
||
}
|
||
|
||
.content {
|
||
padding-top: 1.1rem;
|
||
}
|
||
|
||
h1:focus {
|
||
outline: none;
|
||
}
|
||
|
||
.valid.modified:not([type=checkbox]) {
|
||
outline: 1px solid #26b050;
|
||
}
|
||
|
||
.invalid {
|
||
outline: 1px solid #e50000;
|
||
}
|
||
|
||
.validation-message {
|
||
color: #e50000;
|
||
}
|
||
|
||
.blazor-error-boundary {
|
||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDg2IDY2LjAxODMgMjYzLjU4NiA2Ni4wMTgzWk0yNjMuNTc2IDg2LjA1NDdDMjYxLjA0OSA4Ni4wNTQ3IDI1OS43ODUgODcuMzAwNSAyNTkuNzg2IDg5Ljc5MjEgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||
padding: 1rem 1rem 1rem 3.7rem;
|
||
color: white;
|
||
}
|
||
|
||
.blazor-error-boundary::after {
|
||
content: "An error has occurred."
|
||
}
|
||
|
||
.darker-border-checkbox.form-check-input {
|
||
border-color: #929292;
|
||
}
|
||
|
||
/* Grid Band-Editor */
|
||
.band-editor {
|
||
margin-top: 4px;
|
||
margin-bottom: 16px;
|
||
border: 1px solid var(--band-editor-border);
|
||
border-radius: 4px;
|
||
background-color: var(--band-editor-bg);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.band-editor-toggle {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: inherit;
|
||
color: inherit;
|
||
text-align: left;
|
||
}
|
||
|
||
.band-editor-toggle:hover {
|
||
background-color: var(--band-toggle-hover-bg);
|
||
}
|
||
|
||
.band-editor-toggle-icon {
|
||
font-size: 0.7rem;
|
||
transition: transform 0.2s ease;
|
||
display: inline-block;
|
||
}
|
||
|
||
.band-editor-toggle-icon.expanded {
|
||
transform: rotate(90deg);
|
||
}
|
||
|
||
.band-editor-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
padding: 0 12px 12px 12px;
|
||
}
|
||
|
||
.band-controls {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
|
||
.band-row {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
|
||
.band-columns {
|
||
max-width: 720px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.grid-section {
|
||
margin-top: 4px;
|
||
}
|
||
|
||
/* Grid Zebra-Striping */
|
||
dxbl-grid tbody tr:nth-child(even) td {
|
||
background-color: var(--grid-stripe-bg) !important;
|
||
}
|
||
|
||
/* ?? Dark-Mode-Overrides für nicht-native Themes ?????????????????????????????
|
||
Strategie: CSS-Custom-Properties werden von DevExpress DIREKT auf den
|
||
Komponenten-Elementen definiert (z. B. --dxbl-popup-bg:#fff auf .dxbl-modal).
|
||
Eine geerbte Variable aus html.dx-dark würde durch die direkte Zuweisung
|
||
überschrieben. Deshalb targeten wir exakt dieselben Elemente, aber mit einem
|
||
zusätzlichen Vorfahren-Selektor (html.dx-dark) für höhere Spezifizität:
|
||
html.dx-dark .dxbl-modal = (0,2,1) > .dxbl-modal = (0,1,0) ?
|
||
html.dx-dark wird per JS gesetzt, wenn IsDarkMode && !IsNativeDarkTheme.
|
||
?? */
|
||
|
||
/* Popup / Modal (CRUD-Dialoge) – Variablen-Quelle: .dxbl-modal */
|
||
html.dx-dark .dxbl-modal {
|
||
--dxbl-popup-bg: #2d2d2d;
|
||
--dxbl-popup-color: #e8e8e8;
|
||
--dxbl-popup-border-color: #555;
|
||
--dxbl-popup-header-bg: #333;
|
||
--dxbl-popup-header-color: #e8e8e8;
|
||
--dxbl-popup-footer-bg: #333;
|
||
--dxbl-popup-footer-color: #e8e8e8;
|
||
}
|
||
|
||
/* Flyout (Column Chooser, Filter-Panel) – Variablen-Quelle: .dxbl-flyout */
|
||
html.dx-dark .dxbl-flyout {
|
||
--dxbl-flyout-bg: #2d2d2d;
|
||
--dxbl-flyout-color: #e8e8e8;
|
||
--dxbl-flyout-border-color: #555;
|
||
--dxbl-flyout-header-bg: #333;
|
||
--dxbl-flyout-header-color: #e8e8e8;
|
||
--dxbl-flyout-footer-bg: #333;
|
||
}
|
||
|
||
/* Dropdown (ComboBox-Klappliste, Band-Dropdowns) – Quelle: .dxbl-dropdown */
|
||
html.dx-dark .dxbl-dropdown,
|
||
html.dx-dark .dxbl-itemlist-dropdown {
|
||
--dxbl-dropdown-bg: #2d2d2d;
|
||
--dxbl-dropdown-color: #e8e8e8;
|
||
--dxbl-dropdown-border-color: #555;
|
||
--dxbl-dropdown-header-bg: #333;
|
||
--dxbl-dropdown-footer-bg: #333;
|
||
}
|
||
|
||
/* Edit-Dropdown (ComboBox-Popup wenn als Modal gerendert) – Quelle: .dxbl-edit-dropdown */
|
||
html.dx-dark .dxbl-edit-dropdown {
|
||
--dxbl-edit-dropdown-bg: #2d2d2d;
|
||
--dxbl-edit-dropdown-color: #e8e8e8;
|
||
--dxbl-edit-dropdown-border-color: #555;
|
||
}
|
||
|
||
/* ListBox (Einträge in Dropdowns) – Quelle: .dxbl-list-box */
|
||
html.dx-dark .dxbl-list-box,
|
||
html.dx-dark .dxbl-list-box-render-container {
|
||
--dxbl-list-box-bg: #2d2d2d;
|
||
--dxbl-list-box-color: #e8e8e8;
|
||
--dxbl-list-box-border-color: #555;
|
||
--dxbl-list-box-item-hover-bg: #3a3a3a;
|
||
--dxbl-list-box-item-hover-color: #e8e8e8;
|
||
}
|
||
|
||
/* TextEdit / ComboBox – Eingabefeld – Quelle: .dxbl-text-edit */
|
||
html.dx-dark .dxbl-text-edit {
|
||
--dxbl-text-edit-bg: #2d2d2d;
|
||
--dxbl-text-edit-color: #e8e8e8;
|
||
--dxbl-text-edit-border-color: #555;
|
||
--dxbl-text-edit-btn-bg: #3a3a3a;
|
||
--dxbl-text-edit-btn-color: #e8e8e8;
|
||
--dxbl-text-edit-btn-hover-bg: #444;
|
||
--dxbl-text-edit-btn-hover-color: #e8e8e8;
|
||
}
|
||
|
||
/* Buttons */
|
||
html.dx-dark .dxbl-btn {
|
||
--dxbl-btn-color: #e8e8e8;
|
||
--dxbl-btn-bg: #3a3a3a;
|
||
--dxbl-btn-border-color: #555;
|
||
--dxbl-btn-hover-bg: #444;
|
||
--dxbl-btn-hover-color: #e8e8e8;
|
||
--dxbl-btn-hover-border-color: #666;
|
||
}
|
||
|
||
/* FormLayout */
|
||
html.dx-dark .dxbl-fl {
|
||
--dxbl-fl-caption-color: #bbb;
|
||
--dxbl-fl-group-bg: #242424;
|
||
--dxbl-fl-group-color: #e8e8e8;
|
||
}
|
||
|
||
/* Grid */
|
||
html.dx-dark .dxbl-grid {
|
||
background-color: #242424;
|
||
color: #e8e8e8;
|
||
border-color: #444;
|
||
}
|
||
|
||
html.dx-dark .dxbl-grid > .dxbl-scroll-viewer,
|
||
html.dx-dark .dxbl-grid > .dxbl-grid-top-panel {
|
||
background-color: #242424;
|
||
color: #e8e8e8;
|
||
}
|
||
|
||
/* MassData-spezifisch */
|
||
.page-size-selector {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-wrap: nowrap;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.page-size-label {
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.page-size-combo {
|
||
width: 13ch;
|
||
min-width: 13ch;
|
||
max-width: 13ch;
|
||
}
|
||
|
||
.page-size-combo input {
|
||
text-align: left;
|
||
}
|
||
|
||
.pager-container {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: 12px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
/* Lade-Spinner */
|
||
.loading-container {
|
||
min-height: 160px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.top-row .btn-gap {
|
||
margin-left: 8px;
|
||
} |