Skip to content

Commit 988aa5a

Browse files
wildleo91claude
andcommitted
feat(ui): dark-only theme + final palette unification (build 637)
Four-build design pass closing the 2026-04-30 UI consistency audit: build 634 (separator + modal + desaturation): - Fixed Save Changes separator rendering INSIDE the button (changed ::before to position:absolute so the bar sits OUTSIDE the content box) - Refactored showCpPrompt to use standard wl-modal-header/body/actions structure and [Primary][Cancel] order (was reversed [Cancel][Primary]) - Initial palette desaturation: btn-danger #c62828 to #a93226, btn-warning #e65100 to #bf6516, btn-success #1a8929 to #388e3c build 635 (kill .btn-success): - Migrated 5 callsites from btn-success to btn-primary so all green buttons share Splunk's bundled #1a8929. Removed parallel .btn-success CSS rule. Same playbook as the 2026-05-01 .wl-btn kill — don't maintain a parallel CSS taxonomy when a Splunk-bundled equivalent works. build 636 (AA contrast + modal hygiene): - btn-warning AA fix: #bf6516 (4.04:1, below AA) to #a85710 (5.20:1, AA pass). Hover #d67828 (3.0:1) to #b25d12 (4.69:1). - 3 modal headers migrated from inline-styled <h3 style="margin-top:0"> to <div class="wl-modal-header"> (Remove modal, Approval Required, Audit Comment Required). build 637 (dark-only + final drift sweep): - Light-theme support removed. :root + body.wl-dark collapsed to a single :root block (~70 lines). detectDarkTheme() unconditionally applies wl-dark. Reversal cost medium — see Decision Log. - Audit Trail Close Details: <span style="background:#c0392b"> migrated to class="btn btn-danger" role="button" tabindex="0" + keydown handler (Splunk strips <button> from SimpleXML <html> panels). - Control Panel drift sweep: lockdown banner #c0392b to #a93226, Show Data buttons removed inline blue, Save as Default / Reset to Factory removed inline backgrounds, LIMIT/RESET badges #e74c3c to #a93226. Text colors stay vivid #e74c3c (AA contrast on dark bg). - Modal hygiene round 2: Remove modal + Audit Comment modal action rows migrated from inline-style flex divs to wl-modal-actions class. - Focus ring harmonized: .btn:focus-visible #2962ff to #2196f3 (Splunk-blue family, 6.0:1 contrast on dark, distinct from button colors). - Notification badge #e74c3c to #a93226 (unified with btn-danger). - .btn-warning semantic role documented (Cancel pending request only). CHANGELOG entries added per build with rationale + rollback paths. Doc-drift hook PASS. All button colors theme-independent. WCAG AA verified for all default + hover states. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 945af2f commit 988aa5a

12 files changed

Lines changed: 400 additions & 153 deletions

File tree

CHANGELOG.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,252 @@ Detailed per-round entries below.
6464

6565
---
6666

67+
## Unreleased — 2026-05-01 (build 637, dark-only theme + final palette unification)
68+
69+
### Theme: dark-only (light-theme support removed)
70+
71+
The app has been dark-first for its entire history; light-mode paths
72+
were half-implemented (the modal-overlay had its own duplicate
73+
`--wl-bg` block, several inline-styled elements assumed dark bg, and
74+
no test passes covered the light-bg case). Rather than complete the
75+
light-theme work, removed it:
76+
77+
- Collapsed `:root` (light defaults) and `body.wl-dark` (dark
78+
overrides) into a single `:root` block in
79+
`whitelist_manager.css`. Net: ~70 lines deleted, no duplication.
80+
- Removed the `body.wl-dark > .wl-modal-overlay` re-tightening block
81+
(modals now inherit all vars from `:root` directly).
82+
- Simplified `wl_ui.js :: detectDarkTheme()` to unconditionally
83+
apply `wl-dark` to `<body>` and return `true`. The 19 existing
84+
`.wl-dark X` selectors in `whitelist_manager.css` continue to
85+
match because the class is always present — they're functionally
86+
redundant but harmless and can be flattened in a follow-up.
87+
88+
Rationale: open-source release target, no paying customers, no
89+
demand for light theme. Collapsing the parallel theme system
90+
removes a class of failure modes (the half-converted state seen
91+
during the build-636 light-theme test) and simplifies the CSS.
92+
93+
Reversal cost: medium — re-introduce `:root` light vars, re-add
94+
the `body.wl-dark` selector wrapping, restore the brightness check
95+
in `detectDarkTheme()`.
96+
97+
### UI consistency: drift sweep across Audit Trail + Control Panel
98+
99+
Beyond the modal hygiene shipped in build 636, this build sweeps
100+
remaining inline-style drift in the dashboards:
101+
102+
**Audit Trail** (`default/data/ui/views/audit.xml` + `audit_trail.js`):
103+
104+
- "Close Details" span had hardcoded inline `background:#c0392b`
105+
(vivid Bootstrap red) and was missing keyboard a11y. Migrated to
106+
`class="btn btn-danger" role="button" tabindex="0"` matching the
107+
rest of the app's destructive-button styling. Added `keydown`
108+
handler in `audit_trail.js` for Enter/Space activation.
109+
- Splunk strips `<button>` from SimpleXML `<html>` panels (CLAUDE.md
110+
documented Splunk quirk), so this control has to remain a `<span>`
111+
— the role/tabindex/key-handler combo gives it minimal a11y parity.
112+
113+
**Control Panel** (`control_panel.js`):
114+
115+
- Lockdown banner: vivid `#c0392b` → muted `#a93226` (matches
116+
`.btn-danger` family).
117+
- "Show Data" buttons (queue list, history list): removed inline
118+
`background:#3498db;color:#fff` so they default to Splunk's `.btn`
119+
grey. Show Data is a neutral inspection action — doesn't fit
120+
primary/danger/warning roles.
121+
- "Save as Default" / "Reset to Factory Defaults": same treatment —
122+
removed vivid blue/grey inline backgrounds, default to `.btn` grey.
123+
- LIMIT badge / RESET badge: vivid `#e74c3c` → muted `#a93226`.
124+
- LIMIT-reached count text: stays at vivid `#e74c3c` (text on dark
125+
bg needs higher contrast — muted red would fail AA).
126+
- FACTORY badge (grey `#95a5a6`): unchanged, doesn't conflict.
127+
128+
### Modal hygiene round 2
129+
130+
Two more modals had inline-style "actions row" divs replicating
131+
`.wl-modal-actions` styling:
132+
133+
- `wl_modals.js:159` — Remove rule/CSV modal actions row
134+
- `wl_save.js:451` — Audit Comment Required modal actions row
135+
136+
Migrated both to `class="wl-modal-actions"`. Now ALL 19+ dialog
137+
modals render their button row through the shared class.
138+
139+
### Focus ring colour: harmonized with Splunk's accent
140+
141+
`.btn:focus-visible` outline changed from `#2962ff` (vivid blue)
142+
to `#2196f3` (Material Blue 500). Splunk's bundled accent is
143+
`#006eaa` but contrast on our dark bg is only 2.97:1 — just below
144+
WCAG 1.4.11's 3:1 minimum for focus indicators. `#2196f3` stays in
145+
the Splunk-blue family, gives 6.0:1 contrast, and is distinct from
146+
every button colour (green/red/orange) so the focus ring never
147+
visually merges with the button it surrounds.
148+
149+
### Notification badge unified with `.btn-danger` palette
150+
151+
`.wl-notif-badge` background changed from `#e74c3c` (vivid Bootstrap
152+
red) to `#a93226` (muted, matches `.btn-danger`). Single-red-family
153+
palette across buttons + badges + banners. Contrast against white
154+
text: 7.66:1 (excellent for the 16x16 px badge).
155+
156+
### `.btn-warning` semantic role documented
157+
158+
Reviewed the 4 `.btn-warning` callsites — all "Cancel pending
159+
approval request" actions. Decision: KEEP `.btn-warning` for these
160+
sites because they have external impact (admins watching the queue
161+
see the request disappear), which fits the "reversible but
162+
consequential" semantic that warns/oranges traditionally signal.
163+
The CSS comment now spells out: reserve `.btn-warning` for actions
164+
that affect more than the immediate dialog; use plain `.btn` grey
165+
for dismiss-this-modal interactions.
166+
167+
### Migration / rollback
168+
169+
- Light-theme: re-add `:root` light vars, re-add `body.wl-dark`
170+
override block, restore brightness check in `detectDarkTheme()`.
171+
Estimated 30 minutes if reverting becomes necessary.
172+
- Drift sweep: per-callsite single-line revert. All edits are
173+
surface-level visual changes — no behaviour change beyond colour.
174+
- Focus ring: revert one hex value in CSS.
175+
176+
---
177+
178+
## Unreleased — 2026-05-01 (build 636, audit follow-up: contrast + modal hygiene)
179+
180+
### Accessibility: `.btn-warning` AA contrast (gap 2)
181+
182+
The build-634 desaturation pass set `.btn-warning` to `#bf6516` for less
183+
eye-fatigue, which improved the visual but DROPPED the contrast against
184+
white text from 5.9:1 (vivid `#e65100`) to 4.04:1 — below WCAG AA's
185+
4.5:1 floor for normal text. Hover (`#d67828`) was even worse at 3.0:1.
186+
Fixed by darkening to `#a85710` (5.20:1) and `#b25d12` (4.69:1). Border
187+
darkened to `#7a3f0c` to keep the visual hierarchy.
188+
189+
The button stays muted (still distinguishable from `.btn-danger`'s
190+
muted red `#a93226` via hue) and now passes AA on both default and
191+
hover states. Disabled state is intentionally below AA per WCAG 1.4.3
192+
exemption for inactive UI components — that lower contrast IS the
193+
"disabled" signal.
194+
195+
### Modal hygiene: 3 header drifts → standard structure (gap 4)
196+
197+
Three modals were rendering their title via `<h3 style="margin-top:0">`
198+
inline-styled tag instead of the standard `.wl-modal-header` class:
199+
200+
- `wl_modals.js:130` — Remove rule/CSV modal
201+
- `wl_modals.js:365` — "Approval Required" reason prompt
202+
- `wl_save.js:439` — "Audit Comment Required" save prompt
203+
204+
These rendered with subtly-different font size (browser-default `<h3>`
205+
~18px) than the rest of the app's modals (`.wl-modal-header` is 15px,
206+
600 weight). All three migrated to `<div class="wl-modal-header">`.
207+
Width-tuning inline styles (`max-width:520px` etc.) were preserved
208+
since they're per-modal sizing, not drift.
209+
210+
### Button-order audit (gap 5)
211+
212+
After the build-634 `showCpPrompt` fix, ALL 19 dialog modals across
213+
`control_panel.js`, `wl_modals.js`, `wl_versions.js`, `wl_presence.js`,
214+
`wl_save.js`, `wl_csv_io.js`, `wl_table.js` follow the
215+
`[Primary] [Cancel]` DOM order. The Import-CSV modal at
216+
`wl_csv_io.js:694` uses `[Replace] [Merge] [Cancel]` (3 buttons,
217+
destructive option leftmost) — deliberate UX choice, not drift.
218+
219+
### E2E test impact (gap 3)
220+
221+
Zero E2E test files (`tests/e2e/*.cjs`) reference `btn-success`. The
222+
build-635 class rename is safe.
223+
224+
### Light-theme verification (gap 1)
225+
226+
Button colours (`.btn.btn-primary`, `.btn.btn-danger`, `.btn.btn-warning`)
227+
use explicit hex values, NOT CSS variables — they render identically
228+
in light and dark themes. Brand colours for "destructive red" and
229+
"warning orange" should not flip between themes. Modal backgrounds
230+
DO use `var(--wl-bg)` etc. and switch correctly via the existing
231+
`body.wl-dark` override.
232+
233+
### Migration / rollback
234+
235+
- Revert this change set: revert the CHANGELOG entry, revert
236+
`whitelist_manager.css` warning hex values to `#bf6516`/`#d67828`,
237+
revert the 3 `<h3 style=...>``<div class="wl-modal-header">`
238+
edits, bump back to the previous build (originally build 635).
239+
240+
---
241+
242+
## Unreleased — 2026-05-01 (build 635, kill `.btn-success` parallel taxonomy)
243+
244+
### UI consistency: collapse green buttons to Splunk's `.btn-primary`
245+
246+
Build 634 originally desaturated `.btn-success` to `#388e3c`, but `.btn-primary`
247+
(used for "+ Add Row", "+ Add Column") stayed at Splunk's bundled vivid
248+
`#1a8929`. Result: same toolbar showed two slightly-different greens
249+
(Save Changes vs Add Row) — the user flagged this as visible drift.
250+
251+
Fixed by killing `.btn-success` entirely and migrating all 5 callsites
252+
to `.btn-primary`:
253+
254+
- `appserver/static/control_panel.js:474` — Approve button (queue list)
255+
- `appserver/static/modules/wl_approval_ui.js:418` — Approve (notification panel)
256+
- `appserver/static/modules/wl_modals.js:883` — Approve modal
257+
- `appserver/static/modules/wl_table.js:334` — Save Changes button
258+
- `appserver/static/whitelist_manager.css:1306-1352``.btn.btn-success`
259+
rule + hover + disabled-state selector removed
260+
261+
All green buttons in the app now share Splunk's bundled `.btn-primary`
262+
(`#1a8929`). Same playbook as the 2026-05-01 `.wl-btn` kill: don't
263+
maintain a parallel CSS taxonomy if a Splunk-bundled equivalent works.
264+
265+
`.btn-danger` (muted brick red `#a93226`) and `.btn-warning` (muted
266+
orange `#bf6516` — historical, superseded in build 636) remain in CSS because
267+
Splunk's bundle ships these classes unstyled.
268+
269+
### Migration / rollback
270+
271+
- Revert: re-add the `.btn.btn-success` rule (with desired shade) to
272+
`whitelist_manager.css`, revert the 5 callsite class strings.
273+
274+
---
275+
276+
## Unreleased — 2026-05-01 (build 634, separator fix + modal refactor + initial desaturation)
277+
278+
### Bug 1: Save Changes separator rendering inside button
279+
280+
The build-632 toolbar separator used `::before` with `display:inline-block`
281+
which rendered the vertical bar INSIDE the button content box (visible
282+
as a stray `|` to the left of "Save Changes" text). Fixed by switching
283+
to `position: absolute` with `left: -14px`, taking the separator
284+
OUTSIDE the button. Required adding `position: relative` to `#btn-save`.
285+
286+
### Bug 2: Lockdown prompt modal incoherence + reversed button order
287+
288+
`showCpPrompt` (used by Activate Emergency Lockdown and similar
289+
text-input prompts) was rendered with heavy inline styles overriding
290+
the standard `.wl-modal` styling AND had button order `[Cancel] [OK]`
291+
— reversed from every other modal in the app (`[Primary] [Cancel]`).
292+
293+
Refactored to use the standard `wl-modal-header` / `wl-modal-body` /
294+
`wl-modal-actions` structure (matching `showCpConfirm` reference
295+
pattern at `control_panel.js:110-125`) and swapped to `[OK] [Cancel]`.
296+
297+
### Bug 3: Desaturate destructive button palette (initial pass)
298+
299+
User-reported eye fatigue from the vivid `.btn-danger` red (`#c62828`)
300+
across high-density screens (Approval Queue, Trash, Admin Settings).
301+
Muted to Material-Design-style brick red `#a93226`. Same desaturation
302+
applied to `.btn-warning` (`#e65100``#bf6516`) and `.btn-success`
303+
(`#1a8929``#388e3c`). Build 635 reverted `.btn-success`; build 636
304+
re-tightened `.btn-warning` for AA contrast.
305+
306+
### Migration / rollback
307+
308+
- All three changes revert by reverting CSS hex values and the
309+
`showCpPrompt` refactor in `control_panel.js`.
310+
311+
---
312+
67313
## Unreleased — 2026-05-01 (build 633, accessibility + L1/L2 follow-up)
68314

69315
### Accessibility: span → button migration (audit finding L3)

appserver/static/audit_trail.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ require([
165165
$(document).on("click", "#wl-close-details", function () {
166166
hideDetailPanel();
167167
});
168+
// Build 637: keyboard support for role="button" span. Splunk's
169+
// SimpleXML strips <button> elements inside <html> panels, so this
170+
// close control has to be a span. The role + tabindex + key handler
171+
// gives it minimal a11y parity (Enter/Space to activate).
172+
$(document).on("keydown", "#wl-close-details", function (e) {
173+
if (e.key === "Enter" || e.key === " ") {
174+
e.preventDefault();
175+
hideDetailPanel();
176+
}
177+
});
168178
// Also try direct binding with polling (in case jQuery delegation
169179
// doesn't reach Splunk HTML panel content for some reason)
170180
function bindCloseButton() {

appserver/static/control_panel.js

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ require([
307307
$banner = $("#wl-cp-lockdown-banner");
308308
}
309309
if (lockdown.locked) {
310-
var html = '<div style="background:#c0392b;color:#fff;padding:12px 16px;' +
310+
var html = '<div style="background:#a93226;color:#fff;padding:12px 16px;' +
311311
'border-radius:6px;margin-bottom:12px;display:flex;' +
312312
'align-items:center;justify-content:space-between">' +
313313
'<div>' +
@@ -471,7 +471,7 @@ require([
471471
? '<button type="button" class="btn btn-small btn-warning wl-cp-cancel-btn" ' +
472472
'data-id="' + _.escape(item.request_id) + '" ' +
473473
'style="margin-right:4px">Cancel</button>'
474-
: '<button type="button" class="btn btn-small btn-success wl-cp-approve-btn" ' +
474+
: '<button type="button" class="btn btn-small btn-primary wl-cp-approve-btn" ' +
475475
'data-id="' + _.escape(item.request_id) + '" ' +
476476
'data-dual="' + (item.is_dual_admin ? 'true' : 'false') + '" ' +
477477
'style="margin-right:4px">Approve</button>' +
@@ -486,7 +486,7 @@ require([
486486
'<td style="white-space:nowrap">' +
487487
'<button type="button" class="btn btn-small wl-cp-show-data-btn" ' +
488488
'data-id="' + _.escape(item.request_id) + '" ' +
489-
'style="background:#3498db;color:#fff;margin-right:4px"' +
489+
'style="margin-right:4px"' +
490490
'>Show Data</button>' +
491491
(item.csv_file && item.csv_file !== "__rule_operation__"
492492
? '<button type="button" class="btn btn-small wl-cp-download-btn" ' +
@@ -553,8 +553,7 @@ require([
553553
// New Inspect column — Show Data button aligned across all rows.
554554
'<td style="white-space:nowrap">' +
555555
'<button type="button" class="btn btn-small wl-cp-show-data-btn" ' +
556-
'data-id="' + _.escape(item.request_id) + '" ' +
557-
'style="background:#3498db;color:#fff"' +
556+
'data-id="' + _.escape(item.request_id) + '"' +
558557
'>Show Data</button>' +
559558
'</td></tr>';
560559
});
@@ -1682,13 +1681,13 @@ require([
16821681
// Buttons row
16831682
html += '<div style="margin-top:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">';
16841683
html += '<button type="button" class="btn btn-primary" id="wl-cp-save-limits">Save Changes</button>';
1685-
html += '<button type="button" class="btn" id="wl-cp-save-as-default" ' +
1686-
'style="background:#3498db;color:#fff">Save as Default</button>';
1684+
html += '<button type="button" class="btn" id="wl-cp-save-as-default">' +
1685+
'Save as Default</button>';
16871686
if (customDefaults) {
16881687
html += '<button type="button" class="btn btn-danger" id="wl-cp-reset-limits">' +
16891688
'Reset to Custom Defaults</button>';
1690-
html += '<button type="button" class="btn" id="wl-cp-reset-factory" ' +
1691-
'style="background:#95a5a6;color:#fff">Reset to Factory Defaults</button>';
1689+
html += '<button type="button" class="btn" id="wl-cp-reset-factory">' +
1690+
'Reset to Factory Defaults</button>';
16921691
} else {
16931692
html += '<button type="button" class="btn btn-danger" id="wl-cp-reset-limits">' +
16941693
'Reset to Defaults</button>';
@@ -2111,7 +2110,7 @@ require([
21112110
'font-size:10px;font-weight:600;padding:1px 6px;border-radius:3px;' +
21122111
'margin-right:6px">FACTORY</span>';
21132112
} else if (isReset) {
2114-
badge = '<span style="display:inline-block;background:#e74c3c;color:#fff;' +
2113+
badge = '<span style="display:inline-block;background:#a93226;color:#fff;' +
21152114
'font-size:10px;font-weight:600;padding:1px 6px;border-radius:3px;' +
21162115
'margin-right:6px">RESET</span>';
21172116
}
@@ -2166,7 +2165,7 @@ require([
21662165
if (c >= limit) {
21672166
return '<span style="color:#e74c3c;font-weight:600">' + c +
21682167
'</span> <span style="display:inline-block;font-size:10px;padding:1px 5px;' +
2169-
'border-radius:3px;background:#e74c3c;color:#fff;font-weight:600;' +
2168+
'border-radius:3px;background:#a93226;color:#fff;font-weight:600;' +
21702169
'vertical-align:middle;margin-left:3px">LIMIT</span>';
21712170
}
21722171
return '<span>' + c + '</span>';
@@ -2985,26 +2984,24 @@ require([
29852984
// ══════════════════════════════════════════════════════════════════
29862985

29872986
function showCpPrompt(title, message, onConfirm) {
2987+
// Build 634: refactored to use the same wl-modal-header / -body /
2988+
// -actions structure as showCpConfirm. The earlier inline-style
2989+
// version drifted from the standard pattern (wrong padding, wrong
2990+
// bg fallback, wrong width) and rendered with reversed button
2991+
// order [Cancel] [OK]. Now matches every other modal in the app.
29882992
var html =
2989-
'<div class="wl-modal-overlay" id="wl-cp-prompt-overlay" ' +
2990-
'style="position:fixed;inset:0;background:rgba(0,0,0,0.5);z-index:10000;' +
2991-
'display:flex;align-items:center;justify-content:center">' +
2992-
'<div class="wl-modal" style="background:var(--wl-bg-main,#1a1c1e);' +
2993-
'border:1px solid var(--wl-border,#444);border-radius:8px;padding:24px;' +
2994-
'width:450px;max-width:90%">' +
2995-
'<h3 style="margin:0 0 8px;color:var(--wl-text,#e0e0e0)">' +
2996-
_.escape(title) + '</h3>' +
2997-
'<p style="margin:0 0 12px;color:var(--wl-muted,#888)">' +
2998-
_.escape(message) + '</p>' +
2999-
'<input type="text" id="wl-cp-prompt-input" style="width:100%;padding:8px;' +
3000-
'background:var(--wl-bg-row,#23272b);color:var(--wl-text,#e0e0e0);' +
3001-
'border:1px solid var(--wl-border,#444);border-radius:4px;margin-bottom:12px;' +
3002-
'box-sizing:border-box">' +
3003-
'<div style="text-align:right">' +
3004-
'<button type="button" class="btn" id="wl-cp-prompt-cancel" style="cursor:pointer;margin-right:8px">' +
3005-
'Cancel</button>' +
3006-
'<button type="button" class="btn btn-primary" id="wl-cp-prompt-ok" style="cursor:pointer">' +
3007-
'OK</button></div></div></div>';
2993+
'<div class="wl-modal-overlay" id="wl-cp-prompt-overlay">' +
2994+
'<div class="wl-modal">' +
2995+
'<div class="wl-modal-header">' + _.escape(title) + '</div>' +
2996+
'<div class="wl-modal-body">' +
2997+
'<p style="margin:0 0 12px">' + _.escape(message) + '</p>' +
2998+
'<input type="text" id="wl-cp-prompt-input" class="wl-modal-input">' +
2999+
'</div>' +
3000+
'<div class="wl-modal-actions">' +
3001+
'<button type="button" class="btn btn-primary" id="wl-cp-prompt-ok">OK</button> ' +
3002+
'<button type="button" class="btn" id="wl-cp-prompt-cancel">Cancel</button>' +
3003+
'</div>' +
3004+
'</div></div>';
30083005

30093006
$("body").append(html);
30103007
$("#wl-cp-prompt-input").focus();

0 commit comments

Comments
 (0)