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
+60 -38
View File
@@ -37,9 +37,21 @@ export const chartColors = {
success: '#059669',
danger: '#dc2626',
muted: '#94a3b8',
grid: '#f1f5f9'
grid: '#e2e8f0' // fallback only; use getChartTheme().border at runtime
};
export function getChartTheme() {
const style = getComputedStyle(document.documentElement);
const get = (v: string) => style.getPropertyValue(v).trim();
return {
surface: get('--surface') || '#ffffff',
textPrimary: get('--text-primary') || '#0f172a',
textMuted: get('--text-muted') || '#64748b',
border: get('--border') || '#e2e8f0',
textInverse: get('--text-inverse') || '#ffffff',
};
}
// Extended palette for charts with many categories (events, channels)
export const chartPalette = [
'#2563eb', // blue
@@ -54,15 +66,15 @@ export const chartPalette = [
'#ea580c', // orange
];
export const createDataLabelConfig = (showDataLabels: boolean): any => ({
export const createDataLabelConfig = (showDataLabels: boolean, overrides?: { color?: string; backgroundColor?: string }): any => ({
display: showDataLabels,
color: '#1e293b',
color: overrides?.color ?? '#1e293b',
font: { size: 10, weight: 600 },
anchor: 'end',
align: 'end',
offset: 4,
padding: 4,
backgroundColor: 'rgba(255, 255, 255, 0.85)',
backgroundColor: overrides?.backgroundColor ?? 'rgba(255, 255, 255, 0.85)',
borderRadius: 3,
textDirection: 'ltr', // Force LTR for numbers - fixes RTL misalignment
formatter: (value: number | null) => {
@@ -74,43 +86,53 @@ export const createDataLabelConfig = (showDataLabels: boolean): any => ({
}
});
export const createBaseOptions = (showDataLabels: boolean): any => ({
responsive: true,
maintainAspectRatio: false,
locale: 'en-US', // Force LTR number formatting
layout: {
padding: {
top: showDataLabels ? 25 : 5,
right: 5,
bottom: 5,
left: 5
}
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: '#1e293b',
padding: 12,
cornerRadius: 8,
titleFont: { size: 12 },
bodyFont: { size: 11 },
rtl: false,
textDirection: 'ltr'
export const createBaseOptions = (showDataLabels: boolean): any => {
const theme = getChartTheme();
return {
responsive: true,
maintainAspectRatio: false,
locale: 'en-US', // Force LTR number formatting
layout: {
padding: {
top: showDataLabels ? 25 : 5,
right: 5,
bottom: 5,
left: 5
}
},
datalabels: createDataLabelConfig(showDataLabels)
},
scales: {
x: {
grid: { display: false },
ticks: { font: { size: 10 }, color: '#94a3b8' }
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: theme.surface,
titleColor: theme.textPrimary,
bodyColor: theme.textMuted,
borderColor: theme.border,
borderWidth: 1,
padding: 12,
cornerRadius: 8,
titleFont: { size: 12 },
bodyFont: { size: 11 },
rtl: false,
textDirection: 'ltr'
},
datalabels: createDataLabelConfig(showDataLabels, {
color: theme.textPrimary,
backgroundColor: theme.surface + 'dd',
})
},
y: {
grid: { color: chartColors.grid },
ticks: { font: { size: 10 }, color: '#94a3b8' },
border: { display: false }
scales: {
x: {
grid: { display: false },
ticks: { font: { size: 10 }, color: theme.textMuted }
},
y: {
grid: { color: theme.border },
ticks: { font: { size: 10 }, color: theme.textMuted },
border: { display: false }
}
}
}
});
};
};
export const lineDatasetDefaults = {
borderWidth: 2,