Compare commits

..

3 Commits

Author SHA1 Message Date
4312486d95 [auto-merge] the-beacon#65
Some checks failed
Smoke Test / smoke (push) Failing after 4s
Auto-merged PR #65
2026-04-11 18:53:41 +00:00
2ad4bc7e5b [auto-merge] the-beacon#66
Some checks failed
Smoke Test / smoke (push) Has been cancelled
Auto-merged PR #66
2026-04-11 18:53:40 +00:00
Alexander Whitestone
44af2ad09a feat: add ARIA labels, roles, live regions across game UI
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Failing after 3s
Smoke Test / smoke (pull_request) Failing after 4s
- index.html: role=region on phase-bar, strategy-panel; role=dialog+aria-modal on help overlay, offline popup, drift ending; aria-label on help button, close button, continue button, start over button; aria-live on progress label
- render.js: aria-label on alignment event buttons; fix exportSave() URL revoke race with setTimeout delay
- engine.js: aria-label+aria-pressed on buy amount buttons; role=button+tabindex+aria-expanded+aria-controls on completed projects header
2026-04-11 00:25:01 -04:00
3 changed files with 16 additions and 15 deletions

View File

@@ -107,11 +107,11 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
<h1>THE BEACON</h1>
<div class="sub">A Sovereign AI Idle Game</div>
</div>
<div id="phase-bar">
<div id="phase-bar" role="region" aria-label="Phase progress">
<div class="phase-name" id="phase-name">PHASE 1: THE FIRST LINE</div>
<div class="phase-desc" id="phase-desc">Write code. Automate. Build the foundation.</div>
<div class="progress-wrap"><div class="progress-fill" id="phase-progress" style="width:0%"></div></div>
<div class="progress-label"><span id="phase-progress-label">0%</span><span id="phase-progress-target">Next: Phase 2 (2,000 code)</span></div>
<div class="progress-label" aria-live="polite"><span id="phase-progress-label">0%</span><span id="phase-progress-target">Next: Phase 2 (2,000 code)</span></div>
<div class="milestone-row" id="milestone-chips"></div>
</div>
<div id="resources" role="region" aria-label="Resources" aria-live="polite">
@@ -181,7 +181,7 @@ Events Resolved: <span id="st-resolved">0</span>
<h3>WHAT YOU ARE LEARNING</h3>
<div id="education-text"><p class="dim">Education facts appear as you play...</p></div>
</div>
<div id="strategy-panel" style="margin:0 16px 16px;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:12px;border-left:3px solid var(--gold)">
<div id="strategy-panel" role="region" aria-label="Sovereign guidance" style="margin:0 16px 16px;background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:12px;border-left:3px solid var(--gold)">
<h3>SOVEREIGN GUIDANCE (GOFAI)</h3>
<div id="strategy-recommendation" style="font-size:11px;color:var(--gold);font-style:italic">Analyzing system state...</div>
</div>
@@ -190,8 +190,8 @@ Events Resolved: <span id="st-resolved">0</span>
<div id="log-entries"></div>
</div>
<div id="save-toast" role="status" aria-live="polite" style="display:none;position:fixed;top:16px;right:16px;background:#0e1420;border:1px solid #2a3a4a;color:#4a9eff;font-size:10px;padding:6px 12px;border-radius:4px;z-index:50;opacity:0;transition:opacity 0.4s;pointer-events:none">Save</div>
<div id="help-btn" onclick="toggleHelp()" style="position:fixed;bottom:16px;right:16px;width:28px;height:28px;background:#0e0e1a;border:1px solid #333;color:#555;font-size:14px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:50;font-family:inherit;transition:all 0.2s" title="Keyboard shortcuts (?)">?</div>
<div id="help-overlay" onclick="if(event.target===this)toggleHelp()" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(8,8,16,0.92);z-index:80;justify-content:center;align-items:center;flex-direction:column;padding:40px">
<div id="help-btn" onclick="toggleHelp()" role="button" tabindex="0" aria-label="Show keyboard shortcuts" style="position:fixed;bottom:16px;right:16px;width:28px;height:28px;background:#0e0e1a;border:1px solid #333;color:#555;font-size:14px;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:50;font-family:inherit;transition:all 0.2s" title="Keyboard shortcuts (?)">?</div>
<div id="help-overlay" role="dialog" aria-modal="true" aria-label="Keyboard shortcuts help" onclick="if(event.target===this)toggleHelp()" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(8,8,16,0.92);z-index:80;justify-content:center;align-items:center;flex-direction:column;padding:40px">
<div style="background:#0e0e1a;border:1px solid #1a3a5a;border-radius:8px;padding:24px 32px;max-width:420px;width:100%">
<h3 style="color:#4a9eff;font-size:14px;letter-spacing:2px;margin-bottom:16px;text-align:center">KEYBOARD SHORTCUTS</h3>
<div style="font-size:11px;line-height:2.2;color:#aaa">
@@ -208,10 +208,10 @@ Events Resolved: <span id="st-resolved">0</span>
<div style="display:flex;justify-content:space-between;border-top:1px solid #1a1a2e;padding-top:8px;margin-top:4px"><span style="color:#555">This Help</span><span style="color:#555;font-family:monospace">? or /</span></div>
</div>
<div style="text-align:center;margin-top:16px;font-size:9px;color:#444">Click WRITE CODE fast for combo bonuses! 10x=ops, 20x=knowledge, 30x+=bonus code</div>
<button onclick="toggleHelp()" style="display:block;margin:16px auto 0;background:#1a2a3a;border:1px solid #4a9eff;color:#4a9eff;padding:6px 20px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px">Close [?]</button>
<button onclick="toggleHelp()" aria-label="Close keyboard shortcuts help" style="display:block;margin:16px auto 0;background:#1a2a3a;border:1px solid #4a9eff;color:#4a9eff;padding:6px 20px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px">Close [?]</button>
</div>
</div>
<div id="drift-ending">
<div id="drift-ending" role="dialog" aria-modal="true" aria-label="The Drift ending">
<h2>THE DRIFT</h2>
<p>You became very good at what you do.</p>
<p>So good that no one needed you anymore.</p>
@@ -221,7 +221,7 @@ The light is on. The room is empty."
</div>
<p>Drift: <span id="final-drift">100</span> &mdash; Total Code: <span id="final-code">0</span></p>
<p>Every alignment shortcut moved you further from the people you served.</p>
<button onclick="if(confirm('Start over? The old save will be lost.')){localStorage.removeItem('the-beacon-v2');location.reload()}">START OVER</button>
<button onclick="if(confirm('Start over? The old save will be lost.')){localStorage.removeItem('the-beacon-v2');location.reload()}" aria-label="Start over, reset all progress">START OVER</button>
</div>
<script src="js/data.js"></script>
@@ -233,12 +233,12 @@ The light is on. The room is empty."
<script src="js/main.js"></script>
<div id="offline-popup" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(8,8,16,0.92);z-index:90;justify-content:center;align-items:center;flex-direction:column;text-align:center;padding:40px">
<div id="offline-popup" role="dialog" aria-modal="true" aria-label="Welcome back, offline progress summary" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(8,8,16,0.92);z-index:90;justify-content:center;align-items:center;flex-direction:column;text-align:center;padding:40px">
<div style="background:#0e0e1a;border:1px solid #1a3a5a;border-radius:8px;padding:24px 32px;max-width:400px;width:100%">
<h3 style="color:#4a9eff;font-size:14px;letter-spacing:2px;margin-bottom:16px">WELCOME BACK</h3>
<p style="color:#888;font-size:10px;margin-bottom:12px" id="offline-time-label">You were away for 0 minutes.</p>
<div id="offline-gains-list" style="text-align:left;font-size:11px;line-height:1.8;margin-bottom:16px"></div>
<button onclick="dismissOfflinePopup()" style="background:#1a2a3a;border:1px solid #4a9eff;color:#4a9eff;padding:8px 20px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px">Continue</button>
<button onclick="dismissOfflinePopup()" aria-label="Dismiss offline progress summary" style="background:#1a2a3a;border:1px solid #4a9eff;color:#4a9eff;padding:8px 20px;border-radius:4px;cursor:pointer;font-family:inherit;font-size:11px">Continue</button>
</div>
</div>

View File

@@ -868,7 +868,7 @@ function renderBuildings() {
for (const amt of [1, 10, -1]) {
const label = amt === -1 ? 'MAX' : `x${amt}`;
const active = G.buyAmount === amt;
html += `<button onclick="setBuyAmount(${amt})" style="font-size:9px;padding:2px 8px;border:1px solid ${active ? '#4a9eff' : '#333'};background:${active ? '#0a1a30' : 'transparent'};color:${active ? '#4a9eff' : '#666'};border-radius:3px;cursor:pointer;font-family:inherit">${label}</button>`;
html += `<button onclick=\"setBuyAmount(${amt})\" style=\"font-size:9px;padding:2px 8px;border:1px solid ${active ? '#4a9eff' : '#333'};background:${active ? '#0a1a30' : 'transparent'};color:${active ? '#4a9eff' : '#666'};border-radius:3px;cursor:pointer;font-family:inherit\" aria-label=\"Set buy amount to ${label}\"${active ? ' aria-pressed=\"true\"' : ''}>${label}</button>`;
}
html += '</div>';
@@ -940,7 +940,7 @@ function renderProjects() {
if (G.completedProjects && G.completedProjects.length > 0) {
const count = G.completedProjects.length;
const collapsed = G.projectsCollapsed !== false;
html += `<div id="completed-header" onclick="toggleCompletedProjects()" style="cursor:pointer;font-size:9px;color:#555;padding:4px 0;border-bottom:1px solid #1a2a1a;margin-bottom:4px;user-select:none">`;
html += `<div id=\"completed-header\" onclick=\"toggleCompletedProjects()\" role=\"button\" tabindex=\"0\" aria-expanded=\"${!collapsed}\" aria-controls=\"completed-list\" style=\"cursor:pointer;font-size:9px;color:#555;padding:4px 0;border-bottom:1px solid #1a2a1a;margin-bottom:4px;user-select:none\">`;
html += `${collapsed ? '▶' : '▼'} COMPLETED (${count})</div>`;
if (!collapsed) {
html += `<div id="completed-list">`;

View File

@@ -31,8 +31,8 @@ function renderAlignment() {
<div style="color:#f44336;font-weight:bold;margin-bottom:6px">ALIGNMENT EVENT: The Drift</div>
<div style="font-size:10px;color:#aaa;margin-bottom:8px">An optimization suggests removing the human override. +40% efficiency.</div>
<div class="action-btn-group">
<button class="ops-btn" onclick="resolveAlignment(true)" style="border-color:#f44336;color:#f44336">Accept (+40% eff, +Drift)</button>
<button class="ops-btn" onclick="resolveAlignment(false)" style="border-color:#4caf50;color:#4caf50">Refuse (+Trust, +Harmony)</button>
<button class=\"ops-btn\" onclick=\"resolveAlignment(true)\" style=\"border-color:#f44336;color:#f44336\" aria-label=\"Accept alignment event, gain 40 percent efficiency but increase drift\">Accept (+40% eff, +Drift)</button>
<button class=\"ops-btn\" onclick=\"resolveAlignment(false)\" style=\"border-color:#4caf50;color:#4caf50\" aria-label=\"Refuse alignment event, gain trust and harmony\">Refuse (+Trust, +Harmony)</button>
</div>
</div>
`;
@@ -83,7 +83,8 @@ function exportSave() {
const ts = new Date().toISOString().slice(0, 10);
a.download = `beacon-save-${ts}.json`;
a.click();
URL.revokeObjectURL(url);
// Delay revoke to avoid race — some browsers need time to start the download
setTimeout(() => URL.revokeObjectURL(url), 1000);
log('Save exported to file.');
}