fix(web_viewer): status and accent text passes contrast in both themes

Bootstrap's text-success, text-warning, text-info and text-danger are fill tones, so used as text they failed contrast (amber on white was 1.6:1, info 2.0:1), and nothing adjusted them for dark mode. base.html now defines per-theme status text tokens that pass 4.5:1 on every surface tier, and the text utilities, text-muted, form-text, code, links and the outline buttons resolve through them. Info and warning badges carry dark text. Ink Muted moves a step darker so it holds on paper-sunk.

The dark message bubbles on Realtime were solid navy, forest green and purple, which overpowered the page and dropped the sender names to 1.7:1. They are now low-alpha tints of the signal and lamp hues over the card, like the dashboard delta chips, and the error bubble is red in both themes instead of purple in dark.

Also: the light log levels and level toggles move to tones that pass on the log well, the dark ERROR toggle uses dark text, Realtime drops its local orange text-warning and its opacity on message times, and the four copies of the dark .table td * rule no longer force body text color onto badges and buttons. DESIGN.md records the tokens and the rule that a fill hue never goes on text directly.
This commit is contained in:
agessaman committed 2026-10-17 17:46:51 -07:00
1 parent b5bf8c26e2
commit ebdc44cd90
8 files changed
+146 -45

No files matched your search

+84 -3
View File
@@ -33,7 +33,7 @@
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-color: #212529;
--text-muted: #6c757d;
--text-muted: #5f666d;
--border-color: #dee2e6;
--card-bg: #ffffff;
--card-header-bg: #f8f9fa;
@@ -41,6 +41,16 @@
--log-entry-bg: #f8f9fa;
--connection-info-bg: #e9ecef;
--shadow-color: rgba(0, 0, 0, 0.1);
/* Status and accent colors used as text. Bootstrap's own text-*
colors are background tones and fail contrast as text; these
pass 4.5:1 on paper, paper-raised and paper-sunk. */
--text-success: #146c43;
--text-warning: #7a5d02;
--text-danger: #b02a37;
--text-info: #0a6c80;
--text-primary: #0d6efd;
--link-color: #0d6efd;
--code-color: #ab296a;
}
/* CSS Variables for Dark Mode */
@@ -57,6 +67,14 @@
--log-entry-bg: #2d2d2d;
--connection-info-bg: #3d3d3d;
--shadow-color: rgba(0, 0, 0, 0.3);
/* Pass 4.5:1 on night, night-raised and night-sunk. */
--text-success: #75b798;
--text-warning: #ffda6a;
--text-danger: #f1aeb5;
--text-info: #6edff6;
--text-primary: #6ea8fe;
--link-color: #6ea8fe;
--code-color: #e685b5;
}
/* Apply theme variables */
@@ -255,11 +273,74 @@
color: var(--text-muted);
}
/* Dark mode text muted */
[data-theme="dark"] .text-muted {
/* Muted and status text follow the theme in both themes */
.text-muted,
.form-text {
color: var(--text-muted) !important;
}
/* On the console-black table headers and navbar, muted text uses the
night tone in either theme. */
.table-dark .text-muted,
.navbar-dark .text-muted {
color: #adb5bd !important;
}
.alert code { color: inherit; }
.text-success { color: var(--text-success) !important; }
.text-warning { color: var(--text-warning) !important; }
.text-danger { color: var(--text-danger) !important; }
.text-info { color: var(--text-info) !important; }
.text-primary { color: var(--text-primary) !important; }
code { color: var(--code-color); }
/* Solid info and warning fills are light, so they carry dark text
(Bootstrap 5.1 gives every badge white text). */
.badge.bg-info,
.badge.bg-warning {
color: #212529 !important;
}
[data-theme="dark"] a:not(.btn):not(.nav-link):not(.navbar-brand):not(.dropdown-item) {
color: var(--link-color);
}
/* Outline buttons: Bootstrap's outline text colors are its fill tones,
which fail as text on raised surfaces and on the dark theme. */
.btn-outline-secondary {
color: var(--text-muted);
}
.btn-outline-secondary:hover,
.btn-outline-secondary.active {
color: #fff;
background-color: #6c757d;
}
.btn-outline-danger {
color: var(--text-danger);
}
.btn-outline-danger:hover,
.btn-outline-danger.active {
color: #fff;
}
[data-theme="dark"] .btn-outline-primary {
color: var(--text-primary);
}
[data-theme="dark"] .btn-outline-primary:hover,
[data-theme="dark"] .btn-outline-primary.active {
color: #fff;
}
.btn-outline-warning {
color: var(--text-warning);
}
.btn-outline-warning:hover,
.btn-outline-warning.active {
color: #212529;
}
[data-theme="dark"] .btn-link {
color: var(--link-color);
}
/* Dark mode borders. The directional utilities need naming too — the
all-sides rule does not cover them, so a .border-start divider drew
Bootstrap's light #dee2e6 straight onto a dark card. */
+3 -3
View File
@@ -2777,9 +2777,9 @@ document.addEventListener('DOMContentLoaded', () => {
color: var(--text-color);
}
/* Ensure all text in table cells is light in dark mode */
[data-theme="dark"] .table td,
[data-theme="dark"] .table td * {
/* Table cells take the light text color in dark mode; children with their
own color (badges, buttons, status text) keep it. */
[data-theme="dark"] .table td {
color: var(--text-color) !important;
}
+3 -3
View File
@@ -1216,9 +1216,9 @@ document.addEventListener('DOMContentLoaded', () => {
color: var(--text-color);
}
/* Ensure all text in table cells is light in dark mode */
[data-theme="dark"] .table td,
[data-theme="dark"] .table td * {
/* Table cells take the light text color in dark mode; children with their
own color (badges, buttons, status text) keep it. */
[data-theme="dark"] .table td {
color: var(--text-color) !important;
}
+3 -3
View File
@@ -574,9 +574,9 @@ document.addEventListener('DOMContentLoaded', () => {
color: var(--text-color);
}
/* Ensure all text in table cells is light in dark mode */
[data-theme="dark"] .table td,
[data-theme="dark"] .table td * {
/* Table cells take the light text color in dark mode; children with their
own color (badges, buttons, status text) keep it. */
[data-theme="dark"] .table td {
color: var(--text-color) !important;
}
+9 -8
View File
@@ -43,10 +43,11 @@
.log-line.level-error { color: #f85149; }
.log-line.level-critical{ color: #ff79c6; font-weight: bold; }
[data-theme="light"] .log-line.level-debug { color: #0d9488; }
[data-theme="light"] .log-line.level-info { color: #0d6efd; }
[data-theme="light"] .log-line.level-warning { color: #fd7e14; }
[data-theme="light"] .log-line.level-error { color: #dc3545; }
/* Light set: each passes 4.5:1 on the light log well (#f8f9fa). */
[data-theme="light"] .log-line.level-debug { color: #0f766e; }
[data-theme="light"] .log-line.level-info { color: #0a58ca; }
[data-theme="light"] .log-line.level-warning { color: #a85300; }
[data-theme="light"] .log-line.level-error { color: #b02a37; }
[data-theme="light"] .log-line.level-critical{ color: #6610f2; font-weight: bold; }
.log-controls {
@@ -97,14 +98,14 @@
.log-level-toggle[data-level="DEBUG"].is-on { background: #2dd4bf; color: #0d1117; border-color: #2dd4bf; }
.log-level-toggle[data-level="INFO"].is-on { background: #58a6ff; color: #0d1117; border-color: #58a6ff; }
.log-level-toggle[data-level="WARNING"].is-on { background: #d29922; color: #0d1117; border-color: #d29922; }
.log-level-toggle[data-level="ERROR"].is-on { background: #f85149; color: #fff; border-color: #f85149; }
.log-level-toggle[data-level="ERROR"].is-on { background: #f85149; color: #0d1117; border-color: #f85149; }
.log-level-toggle[data-level="CRITICAL"].is-on { background: #ff79c6; color: #0d1117; border-color: #ff79c6; }
/* Light theme — on */
[data-theme="light"] .log-level-toggle[data-level="DEBUG"].is-on { background: #0d9488; color: #fff; border-color: #0d9488; }
[data-theme="light"] .log-level-toggle[data-level="INFO"].is-on { background: #0d6efd; color: #fff; border-color: #0d6efd; }
[data-theme="light"] .log-level-toggle[data-level="DEBUG"].is-on { background: #0f766e; color: #fff; border-color: #0f766e; }
[data-theme="light"] .log-level-toggle[data-level="INFO"].is-on { background: #0a58ca; color: #fff; border-color: #0a58ca; }
[data-theme="light"] .log-level-toggle[data-level="WARNING"].is-on { background: #fd7e14; color: #212529; border-color: #fd7e14; }
[data-theme="light"] .log-level-toggle[data-level="ERROR"].is-on { background: #dc3545; color: #fff; border-color: #dc3545; }
[data-theme="light"] .log-level-toggle[data-level="ERROR"].is-on { background: #b02a37; color: #fff; border-color: #b02a37; }
[data-theme="light"] .log-level-toggle[data-level="CRITICAL"].is-on { background: #6610f2; color: #fff; border-color: #6610f2; }
</style>
{% endblock %}
+3 -3
View File
@@ -1968,9 +1968,9 @@ document.addEventListener('DOMContentLoaded', () => {
color: var(--text-color);
}
/* Ensure all text in table cells is light in dark mode */
[data-theme="dark"] .table td,
[data-theme="dark"] .table td * {
/* Table cells take the light text color in dark mode; children with their
own color (badges, buttons, status text) keep it. */
[data-theme="dark"] .table td {
color: var(--text-color) !important;
}
+25 -20
View File
@@ -102,7 +102,6 @@
.live-msg-time {
font-size: 0.58rem;
line-height: 1.2;
opacity: 0.88;
}
.stream-entry:hover {
@@ -162,10 +161,6 @@
color: #fff;
}
.text-warning {
color: #fd7e14 !important;
}
.conversation-entry {
border-left: 3px solid #007bff;
padding-left: 10px;
@@ -179,11 +174,23 @@
color: var(--text-color);
}
/* Dark bubbles are light tints of the lamp and signal hues over the
card, like the dashboard delta chips, so body text and the theme's
status text colors keep their contrast inside them. */
[data-theme="dark"] .user-message {
background-color: #1a237e;
border-color: #283593;
color: #e3f2fd;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
background-color: rgba(13, 110, 253, 0.18);
border-color: rgba(110, 168, 254, 0.35);
color: var(--text-color);
}
/* Sender names on the light blue bubble need a deeper blue than the
page's link blue. */
.user-message .text-primary {
color: #0a58ca !important;
}
[data-theme="dark"] .user-message .text-primary {
color: #8bb9fe !important;
}
.bot-message-success {
@@ -194,10 +201,9 @@
}
[data-theme="dark"] .bot-message-success {
background-color: #1b5e20;
border-color: #2e7d32;
color: #c8e6c9;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
background-color: rgba(25, 135, 84, 0.2);
border-color: rgba(117, 183, 152, 0.35);
color: var(--text-color);
}
.bot-message-danger {
@@ -208,10 +214,9 @@
}
[data-theme="dark"] .bot-message-danger {
background-color: #4a148c;
border-color: #6a1b9a;
color: #f8bbd0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
background-color: rgba(220, 53, 69, 0.18);
border-color: rgba(234, 134, 143, 0.35);
color: var(--text-color);
}
.response-text {
@@ -529,15 +534,15 @@
<small class="text-muted me-1">Show:</small>
<div class="form-check form-check-inline mb-0">
<input class="form-check-input rt-filter-cb" type="checkbox" id="rt-filter-command" data-target="command-card" checked>
<label class="form-check-label" for="rt-filter-command" style="font-size:0.8rem;color:#6c757d">Commands</label>
<label class="form-check-label text-muted" for="rt-filter-command" style="font-size:0.8rem">Commands</label>
</div>
<div class="form-check form-check-inline mb-0">
<input class="form-check-input rt-filter-cb" type="checkbox" id="rt-filter-packet" data-target="packet-card" checked>
<label class="form-check-label" for="rt-filter-packet" style="font-size:0.8rem;color:#0dcaf0">Packets</label>
<label class="form-check-label text-info" for="rt-filter-packet" style="font-size:0.8rem">Packets</label>
</div>
<div class="form-check form-check-inline mb-0">
<input class="form-check-input rt-filter-cb" type="checkbox" id="rt-filter-message" data-target="message-card" checked>
<label class="form-check-label" for="rt-filter-message" style="font-size:0.8rem;color:#0d6efd">Messages</label>
<label class="form-check-label text-primary" for="rt-filter-message" style="font-size:0.8rem">Messages</label>
</div>
</div>
</div>