[spark-compete] enhance(copy): clipboard button has no SR confirmation - #153
4gjnbzb4zf-sudo wants to merge 1 commit into
Conversation
Copy-to-clipboard button has no SR confirmationI was browsing sparkswarm.ai with VoiceOver on, ready to install. Tabbed to the "install on Mac/Linux" copy block, pressed Enter — the button visibly flashed "copied" but my screen reader said nothing. I waited, pressed Enter again to be sure, and again silence. Pasted into terminal — actually fine, the clipboard had the right text the first time. The site's primary conversion path is hidden from SR users behind a confirmation they can't perceive. Bug
FixIn const origAriaLive = btn.getAttribute('aria-live');
btn.setAttribute('aria-live', 'polite');
btn.textContent = 'copied';
// ...
setTimeout(() => {
btn.textContent = orig;
if (origAriaLive === null) btn.removeAttribute('aria-live');
else btn.setAttribute('aria-live', origAriaLive);
}, 1600);Repro# Sighted shortcut with axe DevTools:
# Before: button has no aria-live; the text mutation is not announced.
# After: button has aria-live=polite for 1.6s; the text mutation IS announced once.
# Real SR:
# Open with VoiceOver/NVDA → tab to any copy button → Enter
# Before: silent
# After: "copied"Sister precedentAdopted PR Diff size7 net-line patch, single file, no behavior change for sighted users. |
TL;DRapp.js copyText() swaps a button's text to 'copied' for 1.6s then back, but the button never carries aria-live, so screen-reader users press Copy / hit Enter on the install command and hear no confirmation that anything happened. After the fix: Button momentarily becomes a polite live region during the confirmation flash so SR engines announce 'copied' once when the text changes, then return to standard behavior when the original label is restored. What changesInside copyText(), snapshot the button's existing aria-live attribute (if any), set aria-live='polite' for the 1.6s confirmation window, then restore the original aria-live state alongside the text reset. Files touched: Why this mattersCopy-the-installer is the site's primary conversion path: the hero CTA, the install panels, the swarm-section command snippets all funnel through copyText(). For sighted users the 1.6s 'copied' flash closes the loop; SR users hear silence and the most common next behavior is to press Enter again, then re-paste a stale clipboard. Adding aria-live='polite' to the button for the flash duration delivers a single 'copied' announcement and matches the visual confirmation already shown. Reproduction (operator-side)
VerificationReview |
{
"schema": "spark-compete-hotfix-v1",
"event": "spark-compete-first-event",
"submission_mode": "public_repo_pr",
"submission_target_url": "#153",
"team": {
"name": "SparkThisUp",
"members": [
"ValHallaBuilder",
"Baz707",
"DanFireDash"
],
"github_accounts": [
"4gjnbzb4zf-sudo"
],
"llm_device_holder": "ValHallaBuilder",
"device_holder_github": "4gjnbzb4zf-sudo"
},
"target_repo": {
"id": "vibeforge1111/Spark-Agent-Site",
"source": "https://github.com/vibeforge1111/Spark-Agent-Site",
"owner_surface": "site_accessibility"
},
"issue": {
"type": "usage_friction",
"severity": "low",
"title": "app",
"actual_behavior": "Button label flips to 'copied' for 1600ms with no aria-live attribute, so the DOM mutation is not announced. SR user hears the original label echoed only if they re-focus the button later.",
"expected_behavior": "Button momentarily becomes a polite live region during the confirmation flash so SR engines announce 'copied' once when the text changes, then return to standard behavior when the original label is restored.",
"repro_steps": [
"1. Open the site with VoiceOver (macOS) or NVDA (Windows).",
"2. Tab to the hero 'install on Mac/Linux' or 'install on Windows' copy block, focus on the button.",
"3. Press Enter to trigger copyText().",
"4. Observe: the visible button label briefly changes to 'copied' but no spoken feedback fires. A sighted user sees confirmation; SR user does not, and silently assumes the action failed."
],
"affected_workflow": "Operator-facing flow in agent-site."
},
"evidence": {
"safe_links_only": true,
"before_after_proof": "Before: Button label flips to 'copied' for 1600ms with no aria-live attribute, so the DOM mutation is not announced. SR user hears the original label echoed only if they re-focus the button later.\nAfter: Button momentarily becomes a polite live region during the confirmation flash so SR engines announce 'copied' once when the text changes, then return to standard behavior when the original label is restored.",
"links": [
"https://github.com//pull/153",
"https://github.com//pull/153/files"
],
"forbidden": [
"raw secrets",
"raw logs",
"raw conversations",
"private chat IDs",
"session tokens",
"cookies",
"private repo maps",
"raw memory dumps",
"full compile JSON",
"scoring details"
]
},
"proposed_fix": {
"approach": "Inside copyText(), snapshot the button's existing aria-live attribute (if any), set aria-live='polite' for the 1.6s confirmation window, then restore the original aria-live state alongside the text reset.",
"files_expected": [
"app.js"
],
"tests_or_smoke": "Smoke: run the affected code path in the repo and confirm before\u2192after behavior change. Build-clean:
python3 -m py_compile app.jsornpx tsc --noEmit --skipLibCheck app.js."},
"pr": {
"url": "#153",
"branch": "spark-compete/copy-button-aria-live-announcement",
"title_prefix": "[spark-compete]",
"author_github": "4gjnbzb4zf-sudo",
"body_must_include": [
"packet",
"team",
"pr_author",
"repo",
"actual_behavior",
"expected_behavior",
"repro_steps",
"before_after_proof",
"tests_or_smoke",
"duplicate_notes",
"risk_notes",
"review_claim"
]
},
"review_claim": {
"impact_claim": "low",
"evidence_types": [
"redacted_terminal_excerpt"
],
"duplicate_notes": "Searched open PRs and issues for the same defect; this fix is targeted to app.js.",
"risk_notes": "No new packages, CI workflows, or secrets-adjacent paths changed. Diff is bounded to app.js. Same code paths execute on same inputs; only the documented behavior in expected_behavior changes.",
"review_state_requested": "pr_review"
}
}