Skip to content

Commit fab679b

Browse files
committed
Add ARIA labels and accessibility improvements (#35)
1 parent 48446e5 commit fab679b

4 files changed

Lines changed: 117 additions & 82 deletions

File tree

src/GexVisor.UI/Components/Gex/GexHeader.razor

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,55 @@
44
@inject IJSRuntime JS
55
@implements IDisposable
66

7-
<header class="gex-header">
7+
<header class="gex-header" role="banner">
88
<div class="header-left">
9-
<a href="/" class="logo" title="Back to Home">
10-
<div class="logo-icon">Γ</div>
9+
<a href="/" class="logo" title="Back to Home" aria-label="Go to home page">
10+
<div class="logo-icon" aria-hidden="true">Γ</div>
1111
<div>
1212
<h1>GEX REGIME COMPARATOR <span class="badge">RESEARCH</span></h1>
1313
</div>
1414
</a>
1515
</div>
1616

17-
<div class="header-center">
18-
<div class="metric-card">
17+
<div class="header-center" role="region" aria-label="Current market metrics">
18+
<div class="metric-card" aria-live="polite">
1919
<div class="metric-label">@SymbolLabel Price</div>
2020
<div class="metric-value price-value">$@Price.ToString("F2")</div>
2121
</div>
22-
<div class="metric-card">
22+
<div class="metric-card" aria-live="polite">
2323
<div class="metric-label">S² Factor</div>
2424
<div class="metric-value s2-value">@S2Factor.ToString("F1")x</div>
2525
</div>
26-
<div class="metric-card">
26+
<div class="metric-card" aria-live="polite">
2727
<div class="metric-label">Total GEX</div>
2828
<div class="metric-value gex-value">@FormatGex(TotalGex)</div>
2929
</div>
3030
</div>
3131

3232
<div class="header-right">
3333
<div class="export-dropdown">
34-
<button class="export-btn" @onclick="ToggleExportMenu" title="Export Chart as PNG">
35-
<i class="fas fa-camera"></i>
34+
<button class="export-btn" @onclick="ToggleExportMenu" title="Export Chart as PNG" aria-label="Export chart as PNG" aria-expanded="@_showExportMenu" aria-haspopup="menu">
35+
<i class="fas fa-camera" aria-hidden="true"></i>
3636
</button>
3737
@if (_showExportMenu)
3838
{
39-
<div class="export-menu">
40-
<button @onclick="ExportPractitioner">Practitioner View</button>
41-
<button @onclick="ExportPaper2">Paper 2 View</button>
42-
<button @onclick="ExportBoth">Export Both</button>
39+
<div class="export-menu" role="menu" aria-label="Export options">
40+
<button @onclick="ExportPractitioner" role="menuitem">Practitioner View</button>
41+
<button @onclick="ExportPaper2" role="menuitem">Paper 2 View</button>
42+
<button @onclick="ExportBoth" role="menuitem">Export Both</button>
4343
</div>
4444
}
4545
</div>
46-
<button class="help-btn" @onclick="OnHelpClick" title="Keyboard Shortcuts (?)">?</button>
47-
<div class="year-badge @(HasData ? "" : "inactive")">@YearDisplay</div>
46+
<button class="help-btn" @onclick="OnHelpClick" title="Keyboard Shortcuts (?)" aria-label="Show keyboard shortcuts">?</button>
47+
<div class="year-badge @(HasData ? "" : "inactive")" aria-label="Current year: @YearDisplay">@YearDisplay</div>
4848
</div>
4949
</header>
5050

5151
@if (ShowVolWarning)
5252
{
53-
<div class="vol-warning @(IsNegativeGamma ? "active" : "")">
53+
<div class="vol-warning @(IsNegativeGamma ? "active" : "")" role="status" aria-live="polite" aria-label="Gamma regime status">
5454
<div class="vol-stat">
55-
<div class="vol-pulse"></div>
55+
<div class="vol-pulse" aria-hidden="true"></div>
5656
<span>@GammaStatus</span>
5757
</div>
5858
<div class="vol-stat">

src/GexVisor.UI/Components/Gex/GexSidebar.razor

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@
44
@inject GexDataService DataService
55
@implements IDisposable
66

7-
<div class="gex-sidebar">
7+
<div class="gex-sidebar" role="complementary" aria-label="GEX Controls and Information">
88
<!-- Data Source Section -->
99
<div class="section-title">Data Source</div>
1010
<div class="data-source-controls">
11-
<div class="data-mode-toggle">
12-
<button class="mode-btn @(IsDemo ? "active" : "")" @onclick="() => SetMode(DataMode.Demo)">Demo</button>
13-
<button class="mode-btn @(!IsDemo ? "active" : "")" @onclick="() => SetMode(DataMode.Real)">Real Data</button>
11+
<div class="data-mode-toggle" role="radiogroup" aria-label="Data source selection">
12+
<button class="mode-btn @(IsDemo ? "active" : "")" @onclick="() => SetMode(DataMode.Demo)" role="radio" aria-checked="@IsDemo" aria-label="Use demo data">Demo</button>
13+
<button class="mode-btn @(!IsDemo ? "active" : "")" @onclick="() => SetMode(DataMode.Real)" role="radio" aria-checked="@(!IsDemo)" aria-label="Use real market data">Real Data</button>
1414
</div>
1515

1616
@if (!IsDemo)
1717
{
1818
<div class="symbol-selectors">
1919
<div class="selector-row">
20-
<select @onchange="OnAssetClassChange" value="@SelectedAssetClass">
20+
<select @onchange="OnAssetClassChange" value="@SelectedAssetClass" aria-label="Select asset class">
2121
<option value="">Select asset class...</option>
2222
@foreach (var ac in AssetClasses)
2323
{
@@ -26,7 +26,7 @@
2626
</select>
2727
</div>
2828
<div class="selector-row">
29-
<select @onchange="OnSymbolChange" value="@SelectedSymbol" disabled="@string.IsNullOrEmpty(SelectedAssetClass)">
29+
<select @onchange="OnSymbolChange" value="@SelectedSymbol" disabled="@string.IsNullOrEmpty(SelectedAssetClass)" aria-label="Select trading symbol">
3030
<option value="">Select symbol...</option>
3131
@foreach (var sym in Symbols)
3232
{
@@ -45,28 +45,28 @@
4545
<!-- Simulation Section -->
4646
<div class="section-title">Simulation</div>
4747
<div class="sim-controls">
48-
<div class="media-controls">
49-
<button class="media-btn" @onclick="StateService.JumpToStart" title="Start (Home)"></button>
50-
<button class="media-btn" @onclick="JumpToPrevYear" title="Prev Year (↓)">«</button>
51-
<button class="media-btn" @onclick="StateService.StepBackward" title="Step Back (←)"></button>
52-
<button class="media-btn play-btn @(IsSimulating ? "active" : "")" @onclick="StateService.ToggleSimulation" title="Play/Pause (Space)">
48+
<div class="media-controls" role="group" aria-label="Playback controls">
49+
<button class="media-btn" @onclick="StateService.JumpToStart" title="Start (Home)" aria-label="Jump to start"></button>
50+
<button class="media-btn" @onclick="JumpToPrevYear" title="Prev Year (↓)" aria-label="Jump to previous year">«</button>
51+
<button class="media-btn" @onclick="StateService.StepBackward" title="Step Back (←)" aria-label="Step backward"></button>
52+
<button class="media-btn play-btn @(IsSimulating ? "active" : "")" @onclick="StateService.ToggleSimulation" title="Play/Pause (Space)" aria-label="@(IsSimulating ? "Pause simulation" : "Play simulation")" aria-pressed="@IsSimulating">
5353
@(IsSimulating ? "" : "")
5454
</button>
55-
<button class="media-btn" @onclick="StateService.StepForward" title="Step Fwd (→)"></button>
56-
<button class="media-btn" @onclick="JumpToNextYear" title="Next Year (↑)">»</button>
57-
<button class="media-btn" @onclick="StateService.JumpToEnd" title="End (End)"></button>
55+
<button class="media-btn" @onclick="StateService.StepForward" title="Step Fwd (→)" aria-label="Step forward"></button>
56+
<button class="media-btn" @onclick="JumpToNextYear" title="Next Year (↑)" aria-label="Jump to next year">»</button>
57+
<button class="media-btn" @onclick="StateService.JumpToEnd" title="End (End)" aria-label="Jump to end"></button>
5858
</div>
5959

60-
<div class="speed-control">
61-
<button class="speed-btn @(PlaybackSpeed == 1 ? "active" : "")" @onclick="() => SetSpeed(1)">0.5x</button>
62-
<button class="speed-btn @(PlaybackSpeed == 2 ? "active" : "")" @onclick="() => SetSpeed(2)">1x</button>
63-
<button class="speed-btn @(PlaybackSpeed == 4 ? "active" : "")" @onclick="() => SetSpeed(4)">2x</button>
60+
<div class="speed-control" role="radiogroup" aria-label="Playback speed">
61+
<button class="speed-btn @(PlaybackSpeed == 1 ? "active" : "")" @onclick="() => SetSpeed(1)" role="radio" aria-checked="@(PlaybackSpeed == 1)" aria-label="Half speed">0.5x</button>
62+
<button class="speed-btn @(PlaybackSpeed == 2 ? "active" : "")" @onclick="() => SetSpeed(2)" role="radio" aria-checked="@(PlaybackSpeed == 2)" aria-label="Normal speed">1x</button>
63+
<button class="speed-btn @(PlaybackSpeed == 4 ? "active" : "")" @onclick="() => SetSpeed(4)" role="radio" aria-checked="@(PlaybackSpeed == 4)" aria-label="Double speed">2x</button>
6464
</div>
6565

6666
<div class="timeline-scrubber">
6767
<div class="timeline-track">
68-
<div class="timeline-progress" style="width: @ProgressPercent%"></div>
69-
<input type="range" min="0" max="@MaxIndex" value="@CurrentIndex" @oninput="OnScrub" class="timeline-input" />
68+
<div class="timeline-progress" style="width: @ProgressPercent%" aria-hidden="true"></div>
69+
<input type="range" min="0" max="@MaxIndex" value="@CurrentIndex" @oninput="OnScrub" class="timeline-input" aria-label="Timeline position" aria-valuemin="0" aria-valuemax="@MaxIndex" aria-valuenow="@CurrentIndex" aria-valuetext="@EventDate" />
7070
</div>
7171
</div>
7272

@@ -82,29 +82,29 @@
8282
<PriceSparkline Title="@SparklineTitle" />
8383

8484
<!-- Parameters Section -->
85-
<div class="section-title">Parameters</div>
86-
<div class="control-group">
85+
<div class="section-title" id="params-heading">Parameters</div>
86+
<div class="control-group" role="group" aria-labelledby="params-heading">
8787
<div class="control-label">
88-
<span>Spot Price (@SymbolLabel)</span>
89-
<span class="control-value">$@Price.ToString("F0")</span>
88+
<span id="price-label">Spot Price (@SymbolLabel)</span>
89+
<span class="control-value" aria-live="polite">$@Price.ToString("F0")</span>
9090
</div>
91-
<input type="range" min="@StrikeStart" max="@StrikeEnd" step="1" value="@Price" @oninput="OnPriceChange" />
91+
<input type="range" min="@StrikeStart" max="@StrikeEnd" step="1" value="@Price" @oninput="OnPriceChange" aria-labelledby="price-label" aria-valuetext="$@Price.ToString("F0")" />
9292
</div>
9393

9494
<div class="control-group">
9595
<div class="control-label">
96-
<span>Open Interest</span>
97-
<span class="control-value">@OpenInterest.ToString("F1")M</span>
96+
<span id="oi-label">Open Interest</span>
97+
<span class="control-value" aria-live="polite">@OpenInterest.ToString("F1")M</span>
9898
</div>
99-
<input type="range" min="3" max="12" step="0.1" value="@OpenInterest" @oninput="OnOiChange" />
99+
<input type="range" min="3" max="12" step="0.1" value="@OpenInterest" @oninput="OnOiChange" aria-labelledby="oi-label" aria-valuetext="@OpenInterest.ToString("F1") million" />
100100
</div>
101101

102102
<div class="control-group">
103103
<div class="control-label">
104-
<span>Dealer Positioning</span>
105-
<span class="control-value">@Tilt.ToString("F2")</span>
104+
<span id="tilt-label">Dealer Positioning</span>
105+
<span class="control-value" aria-live="polite">@Tilt.ToString("F2")</span>
106106
</div>
107-
<input type="range" min="-0.5" max="0.5" step="0.01" value="@Tilt" @oninput="OnTiltChange" />
107+
<input type="range" min="-0.5" max="0.5" step="0.01" value="@Tilt" @oninput="OnTiltChange" aria-labelledby="tilt-label" aria-valuetext="@Tilt.ToString("F2")" />
108108
</div>
109109

110110
<!-- Zero Gamma Section -->

src/GexVisor.UI/Pages/Index.razor

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,98 +2,98 @@
22

33
<PageTitle>GexVisor</PageTitle>
44

5-
<div class="landing-page">
5+
<main class="landing-page" role="main">
66
<header class="landing-header">
77
<h1>GexVisor</h1>
88
<p class="tagline">Options Flow Analysis & Research Tools</p>
99
</header>
1010

11-
<div class="app-grid">
12-
<a href="/gex" class="app-card featured">
13-
<div class="app-icon">
11+
<nav class="app-grid" aria-label="Application modules">
12+
<a href="/gex" class="app-card featured" aria-label="GEX Visualizer - Interactive gamma exposure visualization">
13+
<div class="app-icon" aria-hidden="true">
1414
<i class="fas fa-chart-bar"></i>
1515
</div>
1616
<div class="app-info">
1717
<h2>GEX Visualizer</h2>
1818
<p>Interactive gamma exposure visualization with dual-view analysis (Practitioner & Paper 2 models)</p>
19-
<div class="app-status ready">
20-
<i class="fas fa-check-circle"></i> Ready
19+
<div class="app-status ready" aria-label="Status: Ready">
20+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
2121
</div>
2222
</div>
2323
</a>
2424

25-
<a href="/arcade" class="app-card">
26-
<div class="app-icon arcade">
25+
<a href="/arcade" class="app-card" aria-label="Research Arcade - Pattern discovery and experimental tools">
26+
<div class="app-icon arcade" aria-hidden="true">
2727
<i class="fas fa-flask"></i>
2828
</div>
2929
<div class="app-info">
3030
<h2>Research Arcade</h2>
3131
<p>Pattern discovery pipeline, paper implementations, and experimental analysis tools</p>
32-
<div class="app-status ready">
33-
<i class="fas fa-check-circle"></i> Ready
32+
<div class="app-status ready" aria-label="Status: Ready">
33+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
3434
</div>
3535
</div>
3636
</a>
3737

38-
<a href="/notebook" class="app-card">
39-
<div class="app-icon notebook">
38+
<a href="/notebook" class="app-card" aria-label="Research Notebook - Document observations and findings">
39+
<div class="app-icon notebook" aria-hidden="true">
4040
<i class="fas fa-book"></i>
4141
</div>
4242
<div class="app-info">
4343
<h2>Research Notebook</h2>
4444
<p>Document observations, hypotheses, and findings while exploring GEX regimes</p>
45-
<div class="app-status ready">
46-
<i class="fas fa-check-circle"></i> Ready
45+
<div class="app-status ready" aria-label="Status: Ready">
46+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
4747
</div>
4848
</div>
4949
</a>
5050

51-
<a href="/trading" class="app-card">
52-
<div class="app-icon trading">
51+
<a href="/trading" class="app-card" aria-label="Paper Trading - Log hypothetical trades and track P&L">
52+
<div class="app-icon trading" aria-hidden="true">
5353
<i class="fas fa-chart-line"></i>
5454
</div>
5555
<div class="app-info">
5656
<h2>Paper Trading</h2>
5757
<p>Log hypothetical trades based on GEX signals and track theoretical P&L</p>
58-
<div class="app-status ready">
59-
<i class="fas fa-check-circle"></i> Ready
58+
<div class="app-status ready" aria-label="Status: Ready">
59+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
6060
</div>
6161
</div>
6262
</a>
6363

64-
<a href="/backtests" class="app-card">
65-
<div class="app-icon backtest">
64+
<a href="/backtests" class="app-card" aria-label="Backtest Results - Compare strategy performance">
65+
<div class="app-icon backtest" aria-hidden="true">
6666
<i class="fas fa-vial"></i>
6767
</div>
6868
<div class="app-info">
6969
<h2>Backtest Results</h2>
7070
<p>Track and compare strategy backtests with regime-conditional performance analysis</p>
71-
<div class="app-status ready">
72-
<i class="fas fa-check-circle"></i> Ready
71+
<div class="app-status ready" aria-label="Status: Ready">
72+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
7373
</div>
7474
</div>
7575
</a>
7676

77-
<a href="/tasks" class="app-card">
78-
<div class="app-icon taskboard">
77+
<a href="/tasks" class="app-card" aria-label="Research Tasks - Kanban task board">
78+
<div class="app-icon taskboard" aria-hidden="true">
7979
<i class="fas fa-clipboard-list"></i>
8080
</div>
8181
<div class="app-info">
8282
<h2>Research Tasks</h2>
8383
<p>Kanban-style task board for managing research ideas and tracking progress</p>
84-
<div class="app-status ready">
85-
<i class="fas fa-check-circle"></i> Ready
84+
<div class="app-status ready" aria-label="Status: Ready">
85+
<i class="fas fa-check-circle" aria-hidden="true"></i> Ready
8686
</div>
8787
</div>
8888
</a>
89-
</div>
89+
</nav>
9090

91-
<footer class="landing-footer">
92-
<div class="shortcuts-hint">
93-
<span class="key">Space</span> Play/Pause
94-
<span class="key">←→</span> Step
95-
<span class="key">R</span> Reset
96-
<span class="key">F</span> Fullscreen
91+
<footer class="landing-footer" role="contentinfo">
92+
<div class="shortcuts-hint" aria-label="Keyboard shortcuts hint">
93+
<span class="key" aria-hidden="true">Space</span><span class="sr-only">Space key for</span> Play/Pause
94+
<span class="key" aria-hidden="true">←→</span><span class="sr-only">Arrow keys for</span> Step
95+
<span class="key" aria-hidden="true">R</span><span class="sr-only">R key for</span> Reset
96+
<span class="key" aria-hidden="true">F</span><span class="sr-only">F key for</span> Fullscreen
9797
</div>
9898
</footer>
99-
</div>
99+
</main>

src/GexVisor.UI/wwwroot/css/app.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,38 @@ tr:hover {
225225

226226
.pulse { animation: pulse 2s ease-in-out infinite; }
227227
.glow { animation: glow 2s ease-in-out infinite; }
228+
229+
/* Accessibility - Screen Reader Only */
230+
.sr-only {
231+
position: absolute;
232+
width: 1px;
233+
height: 1px;
234+
padding: 0;
235+
margin: -1px;
236+
overflow: hidden;
237+
clip: rect(0, 0, 0, 0);
238+
white-space: nowrap;
239+
border: 0;
240+
}
241+
242+
/* Focus visible styles for keyboard navigation */
243+
:focus-visible {
244+
outline: 2px solid var(--accent-cyan);
245+
outline-offset: 2px;
246+
}
247+
248+
/* Skip to main content link (hidden until focused) */
249+
.skip-link {
250+
position: absolute;
251+
top: -40px;
252+
left: 0;
253+
background: var(--accent-cyan);
254+
color: var(--bg-dark);
255+
padding: 0.5rem 1rem;
256+
z-index: 1000;
257+
font-weight: 600;
258+
}
259+
260+
.skip-link:focus {
261+
top: 0;
262+
}

0 commit comments

Comments
 (0)