feat: UI/UX overhaul — CSS cleanup, accessibility, error handling, inline style extraction

Phase 0 — CSS-only fixes:
- Fix --font-mono to use real monospace stack (JetBrains Mono, Fira Code, etc.)
- Replace hardcoded hex colors with CSS variables across 16+ files
- Merge global-nav.css (507 lines) into layout.css, delete original
- Reduce !important in responsive.css from 71 to 8 via .app-shell specificity
- Standardize breakpoints to 480/768/1024/1280px

Phase 1 — Loading states & SSE connection feedback:
- Add centralized SSEManager (sse-manager.js) with exponential backoff
- Add SSE status indicator dot in nav bar
- Add withLoadingButton() + .btn-loading CSS spinner
- Add mode section crossfade transitions

Phase 2 — Accessibility:
- Add aria-labels to icon-only buttons across mode partials
- Add for/id associations to 42 form labels in 5 mode partials
- Add aria-live on toast stack, enableListKeyNav() utility

Phase 3 — Destructive action guards & list overflow:
- Add confirmAction() styled modal, replace all 25 native confirm() calls
- Add toast cap at 5 simultaneous toasts
- Add list overflow indicator CSS

Phase 4 — Inline style extraction:
- Refactor switchMode() in app.js and index.html to use classList.toggle()
- Add CSS toggle rules for all switchMode-controlled elements
- Remove inline style="display:none" from 7+ HTML elements
- Add utility classes (.hidden, .d-flex, .d-grid, etc.)

Phase 5 — Mobile UX polish:
- pre/code overflow handling already in place
- Touch target sizing via --touch-min variable

Phase 6 — Error handling consistency:
- Add reportActionableError() to user-facing catch blocks in 5 mode JS files
- 28 error toast additions alongside existing console.error calls

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-03-12 13:04:36 +00:00
parent 05412fbfc3
commit e687862043
56 changed files with 2660 additions and 2238 deletions

View File

@@ -80,8 +80,8 @@
}
.btn-danger:hover:not(:disabled) {
background: #dc2626;
border-color: #dc2626;
background: var(--accent-red-hover);
border-color: var(--accent-red-hover);
}
.btn-success {
@@ -91,8 +91,8 @@
}
.btn-success:hover:not(:disabled) {
background: #16a34a;
border-color: #16a34a;
background: var(--accent-green-hover);
border-color: var(--accent-green-hover);
}
/* Button sizes */
@@ -415,6 +415,28 @@
to { transform: rotate(360deg); }
}
/* Button loading state */
.btn-loading {
position: relative;
color: transparent;
pointer-events: none;
}
.btn-loading::after {
content: '';
position: absolute;
width: 14px;
height: 14px;
top: 50%;
left: 50%;
margin-top: -7px;
margin-left: -7px;
border: 2px solid var(--border-color);
border-top-color: var(--accent-cyan);
border-radius: var(--radius-full);
animation: spin 0.8s linear infinite;
}
/* Loading overlay */
.loading-overlay {
position: absolute;
@@ -855,3 +877,205 @@ textarea:focus {
cursor: not-allowed;
filter: grayscale(30%);
}
/* ============================================
CONFIRMATION MODAL
============================================ */
.confirm-modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
display: flex;
align-items: center;
justify-content: center;
z-index: var(--z-modal);
animation: fadeIn 0.15s ease-out;
}
.confirm-modal {
background: var(--surface-panel-gradient);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: var(--space-6);
min-width: 320px;
max-width: 440px;
box-shadow: var(--shadow-lg);
}
.confirm-modal-title {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-primary);
margin-bottom: var(--space-3);
}
.confirm-modal-message {
font-size: var(--text-sm);
color: var(--text-secondary);
line-height: var(--leading-normal);
margin-bottom: var(--space-6);
}
.confirm-modal-actions {
display: flex;
justify-content: flex-end;
gap: var(--space-3);
}
/* ============================================
MODE SECTION TRANSITIONS
============================================ */
.mode-section {
display: none;
opacity: 0;
transition: opacity var(--transition-fast);
}
.mode-section.active {
display: block;
opacity: 1;
}
/* ============================================
SWITCHMODE TOGGLE CLASSES
Elements hidden by default, shown via .active
============================================ */
/* Stats sections in header (pager, sensor, wifi, satellite, aircraft) */
#pagerStats,
#sensorStats,
#aircraftStats,
#wifiStats,
#satelliteStats {
display: none;
}
#pagerStats.active,
#sensorStats.active,
#aircraftStats.active,
#wifiStats.active,
#satelliteStats.active {
display: flex;
}
/* Signal meter */
#signalMeter {
display: none;
}
#signalMeter.active {
display: block;
}
/* Dashboard buttons in nav */
#adsbDashboardBtn,
#satelliteDashboardBtn {
display: none;
}
#adsbDashboardBtn.active,
#satelliteDashboardBtn.active {
display: inline-flex;
}
/* Layout containers (wifi, bluetooth) */
.wifi-layout-container,
.bt-layout-container {
display: none;
}
.wifi-layout-container.active {
display: flex;
}
.bt-layout-container.active {
display: flex;
}
/* Visuals containers */
#aircraftVisuals {
display: none;
}
#aircraftVisuals.active {
display: grid;
}
#satelliteVisuals {
display: none;
}
#satelliteVisuals.active {
display: block;
}
/* RTL-SDR device section */
#rtlDeviceSection {
display: none;
}
#rtlDeviceSection.active {
display: block;
}
/* Tool status sections */
.tool-status-section {
display: none;
grid-template-columns: auto auto;
gap: 4px 8px;
align-items: center;
}
.tool-status-section.active {
display: grid;
}
/* Output console and status bar — visible when mode is active, hidden for full-visual modes.
switchMode() adds/removes .active; hidden by default until a mode is selected. */
.app-shell #output:not(.active) {
display: none;
}
.app-shell .status-bar:not(.active) {
display: none;
}
/* Recon panel — controlled by switchMode */
.app-shell #reconPanel:not(.active) {
display: none;
}
/* ============================================
UTILITY CLASSES
============================================ */
.hidden { display: none; }
.d-flex { display: flex; }
.d-grid { display: grid; }
.gap-2 { gap: var(--space-2); }
.gap-4 { gap: var(--space-4); }
.text-center { text-align: center; }
.w-full { width: 100%; }
/* Keyboard focus indicator for list items */
.keyboard-focused {
outline: 2px solid var(--accent-cyan);
outline-offset: -2px;
}
/* Overflow indicator */
.list-overflow-indicator {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-2) var(--space-3);
font-size: var(--text-xs);
color: var(--text-dim);
border-top: 1px solid var(--border-color);
background: var(--bg-secondary);
}
.list-overflow-indicator .btn {
font-size: var(--text-xs);
padding: var(--space-1) var(--space-2);
}