fix: accessibility, theming, and focus-visibility improvements

Addresses critical and high-severity findings from UI audit:

- C1: Define missing CSS tokens (--hover, --bg-primary/secondary/tertiary)
  fixing broken hover states and Slides Builder backgrounds
- C2: Chart colors now read CSS custom properties at render-time via
  getChartTheme(), adapting tooltip, ticks, and grid to dark mode
- C3: Multi-select ARIA fixed — label elements now carry role="option"
  and aria-selected for valid listbox semantics
- H1/M1: Remove unused --gold and duplicate --primary tokens;
  replace all var(--primary) with var(--accent) throughout App.css
- H3/H4: Focus-visible outlines added to all custom interactive elements
  (chips, controls, year buttons, hero button, multi-select trigger)
- H5: access-badge--full hardcoded colors replaced with design tokens
- H7: aria-pressed added to all chart toggle buttons
- L1: Hardcoded #fff/white replaced with var(--text-inverse)
- M4: index.html now preloads DM Serif Display, Outfit, and IBM Plex
  Sans Arabic — all fonts actually used in the app

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
fahed
2026-04-26 15:46:54 +03:00
parent d3f9a6cd43
commit 9138ac1098
5 changed files with 136 additions and 79 deletions
+35 -18
View File
@@ -23,13 +23,11 @@
--text-secondary: #334155;
--text-muted: #64748b;
--accent: #2563eb;
--primary: #2563eb;
--accent-light: #dbeafe;
--success: #059669;
--success-light: #d1fae5;
--danger: #dc2626;
--danger-light: #fee2e2;
--gold: #b8860b;
--brand-icon: #3b82f6;
--brand-text: #1e3a5f;
--text-inverse: #ffffff;
@@ -44,6 +42,10 @@
--shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--shadow: 0 4px 12px rgba(0,0,0,0.08);
--radius: 12px;
--hover: var(--muted-light);
--bg-primary: var(--surface);
--bg-secondary: var(--bg);
--bg-tertiary: var(--border);
}
/* Dark mode */
@@ -56,7 +58,6 @@
--text-secondary: #cbd5e1;
--text-muted: #94a3b8;
--accent: #3b82f6;
--primary: #3b82f6;
--accent-light: #1e3a5f;
--success: #34d399;
--success-light: #064e3b;
@@ -88,7 +89,6 @@
--text-secondary: #cbd5e1;
--text-muted: #94a3b8;
--accent: #3b82f6;
--primary: #3b82f6;
--accent-light: #1e3a5f;
--success: #34d399;
--success-light: #064e3b;
@@ -226,7 +226,7 @@ html[dir="rtl"] {
.empty-state-action {
padding: 10px 20px;
background: var(--primary);
background: var(--accent);
color: var(--text-inverse);
border: none;
border-radius: 8px;
@@ -1089,7 +1089,7 @@ table tbody tr:hover {
.drp-custom-field input[type="date"]:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.12);
box-shadow: 0 0 0 2px var(--accent);
}
.drp-custom-sep {
@@ -1127,7 +1127,7 @@ table tbody tr:hover {
.drp-apply {
background: var(--accent);
border: 1px solid transparent;
color: #fff;
color: var(--text-inverse);
}
.drp-apply:hover {
@@ -1301,7 +1301,7 @@ table tbody tr:hover {
font-size: 1rem;
font-weight: 600;
background: var(--accent);
color: white;
color: var(--text-inverse);
cursor: pointer;
transition: opacity 150ms ease;
}
@@ -1398,8 +1398,8 @@ table tbody tr:hover {
}
.access-badge--full {
background: #d1fae5;
color: #065f46;
background: var(--success-light);
color: var(--success);
}
.btn-small {
@@ -1418,7 +1418,7 @@ table tbody tr:hover {
.btn-small.btn-primary {
background: var(--accent);
color: white;
color: var(--text-inverse);
border-color: var(--accent);
}
@@ -1758,7 +1758,7 @@ tr.editing td {
.chart-metric-selector button.active {
background: var(--surface);
color: var(--primary);
color: var(--accent);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
font-weight: 600;
}
@@ -1792,7 +1792,7 @@ tr.editing td {
}
.carousel:focus-visible {
outline: 2px solid var(--primary);
outline: 2px solid var(--accent);
outline-offset: 4px;
border-radius: 8px;
}
@@ -1877,7 +1877,7 @@ tr.editing td {
}
.carousel-dot.active {
background: var(--primary);
background: var(--accent);
width: 24px;
}
@@ -1903,14 +1903,14 @@ tr.editing td {
}
.carousel-dots.labeled .carousel-dot:hover {
border-color: var(--primary);
border-color: var(--accent);
background: var(--bg);
}
.carousel-dots.labeled .carousel-dot.active {
width: auto;
background: var(--primary);
border-color: var(--primary);
background: var(--accent);
border-color: var(--accent);
}
.carousel-dots.labeled .carousel-dot.active .dot-label {
@@ -2082,7 +2082,7 @@ tr.editing td {
.mobile-nav-item:hover,
.mobile-nav-item.active {
color: var(--primary);
color: var(--accent);
background: rgba(37, 99, 235, 0.08);
}
@@ -2833,6 +2833,23 @@ html[dir="rtl"] .exportable-chart-wrapper .chart-export-btn.visible {
text-align: left !important;
}
/* ========================================
Focus Visible — Keyboard accessibility
======================================== */
.drp-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.drp-year-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.drp-cancel:focus-visible,
.drp-apply:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-refresh-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-lang-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.multi-select-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
.toggle-switch button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chart-metric-selector button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.controls-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.alt-filter-reset:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* ========================================
Reduced Motion
======================================== */