Frontend: systematic a11y audit — trade/chart/wallet aria gaps + axe in CI #214
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:implement
agent:implement
agent:open_issues
agent:ready
agent:research
agent:security_audit
agent:verify
architecture
backend
blocker:hybrid
blocker:launch
blocker:limit-orders
blocker:v2
block:log_only
block:security
bug
ci
contracts
correctness
deploy
dev
devops
docs
documentation
duplicate
e2e
enhancement
epic
feature
frontend
functional-completion
gas
good first issue
governance
help wanted
high-risk
hooks
hybrid
indexer
infra
infrastructure
integrators
invalid
launch-blocker
limit-orders
localnet
localterra
low priority
missing-implementation
needs-design
ops
performance
priority
high
priority
medium
product
qa
QA
question
ready
ready
research
scripts
security
security-hardening
smartcontracts
tech-debt
testing
ux
UX
v2
verification
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-dex-terraclassic#214
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Close the partial accessibility gap on retail-critical surfaces (trade, price chart, wallet): finish missing
aria-*/ programmatic names / live regions where canvas and custom widgets hide meaning from assistive tech, and add a repeatable audit gate (@axe-core/playwrightand/oreslint-plugin-jsx-a11y) so regressions fail CI instead of relying on ad-hoc source review.Tracked in gap inventory:
gaps/GAP_1780023683.md— Accessibility (Partial: somearia-*on trade/chart/wallet; no systematic a11y audit or axe in CI). Product backlog: same doc § P3 — a11y CI — axe-playwright on critical routes.Builds on closed label sweep (#143), focus rings (#144), wallet chip (#186), portal listbox (#181), and chart empty-state docs (#151).
Current codebase
What already exists
Layout.tsx—aria-labelon nav/menus,aria-expanded/role="menu"TradeOrderTicket.tsx—role="tablist"/aria-selectedfor limit vs market;LimitOrderBidAskSideSelector.tsx—radiogroup+aria-checked;LimitOrderPreSubmitSummary.tsx—role="region"OrderBookPanel.tsx— per-rowaria-labelon Edit/Cancel; book is a<ul>grid, not a semantic<table>; column headers are visual<span>s onlyPriceChart.tsx— intervalrole="group", fullscreenaria-pressed/aria-label, interval-loadingaria-live;PriceChartOverlayMenu.tsx—aria-expanded/aria-controls;PriceChartLightweightCanvas.tsx— canvasaria-hidden(correct — plot is decorative to AT);PriceChartEmptyState.tsx—role="img"+aria-labelWalletButton.tsx— connected triggeraria-labelincludes network; menurole="menu"/menuitem; dismiss backdrop labeled;WalletModal.tsx— per-optionaria-labelwith extension detection;WalletLuncBalance.tsx—aria-busy+sr-onlyloadingdocs/frontend.md§ form labels, focus visible, chart empty a11y;skills/AGENTS_FRONTEND_A11Y_FORM_LABELS.md,skills/AGENTS_FRONTEND_A11Y_FOCUS.mdaxe-core,@axe-core/playwright,jest-axe, oreslint-plugin-jsx-a11yinfrontend-dapp/package.json; Playwright E2E (playwright.config.ts, 5 workers) has navigation/wallet specs but no accessibility scansKnown gaps (trade / chart / wallet focus)
PriceChart/PriceChartLightweightCanvastitleonlyPriceChartdata-testid="trade-chart-headline-price"1mtext but noaria-pressedname beyond visual state for some AT combosPriceChartinterval rowregion/aria-labelledbytying controls to “Price chart for {pair}”PriceChart,ChartsPage.tsxOrderBookPanelrole="tabpanel"+aria-labelledbywiringTradeOrderTicketWalletButtonModal.tsxfocuses panel on open; no focus trap oraria-describedbyfor connect errorsWalletModal+Modalrole="alert"; othersrole="status"on non-errors (e.g. ticket mutation block) — inconsistent priorityTradeOrderTicket,TradeMarketOrderPanelTradeOrderTicket,LimitOrdersPage, escrow fieldTesting today
aria-*on components (e.g.LimitOrderBidAskSideSelector.test.tsx,WalletLuncBalance.test.tsx,PriceChart.test.tsxempty state).e2e/navigation.spec.ts, trade specs) — layout/functional only.docs/qa-templates/qa-test-pass.mdmentions slippage accessibility in passing — not enforced.Why this is needed
GAP_1780023683explicitly rates a11y Partial and lists axe-playwright on critical routes as P3 work that should graduate to enforced quality bar.docs/frontend.mdalready states chart empty-state and walletaria-labelrules; CI should enforce the same class of checks on/trade,/charts, and header wallet flows.Constraints and guardrails
aria-hidden— Do not expose raw canvas pixels to AT; provide textual summary (aria-liveregion, visually hidden list, orrole="region"with headline + interval + last price) perdocs/frontend.md§ Trade page — price chart.e2e-smoke(or dedicatede2e-a11yproject) withVITE_PLAYWRIGHT_E2E=true, risk modal suppressed; no newtest.skipwithout issue link.playwright.config.tsunless stability review says otherwise (.cursor/rules/playwright-workers.mdc)./trade(default pair),/charts, connected-wallet header (smoke connect or mock store if needed). Exclude full tx projects initially to avoid LocalTerra flake coupling.tags: ['wcag2a', 'wcag2aa']; document any intentional exceptions in test file comments +docs/frontend.md.PriceChartLightweightCanvaswithout scoped override.sounds.playButtonPress()or existingdata-testidhooks used by E2E.aria-labelcopy should match visible labels.Relevant files
frontend-dapp/src/pages/TradePage.tsx,TradePage.test.tsxfrontend-dapp/src/components/trade/TradeOrderTicket.tsx,TradeMarketOrderPanel.tsxfrontend-dapp/src/components/trade/OrderBookPanel.tsx,OrderBookPanel.test.tsxfrontend-dapp/src/components/charts/PriceChart.tsx,PriceChartOverlayMenu.tsx,PriceChartLightweightCanvas.tsx,PriceChartEmptyState.tsx,frontend-dapp/src/pages/ChartsPage.tsxfrontend-dapp/src/components/wallet/WalletButton.tsx,WalletModal.tsx,WalletDropdownMenuItems.tsx,WalletLuncBalance.tsxfrontend-dapp/src/components/ui/Modal.tsxfrontend-dapp/e2e/navigation.spec.ts, newfrontend-dapp/e2e/a11y-*.spec.tsfrontend-dapp/playwright.config.ts,frontend-dapp/package.json,frontend-dapp/eslint.config.jsdocs/frontend.md,docs/qa-templates/qa-test-pass.mdskills/AGENTS_FRONTEND_A11Y_FORM_LABELS.md,skills/AGENTS_FRONTEND_A11Y_FOCUS.mdgaps/GAP_1780023683.mdRecommended direction
Phase A — Inventory + fixes (trade / chart / wallet)
@axe-core/playwrightlocally on/trade,/charts, wallet open/closed; export violation list into issue comment ordocs/frontend.mdappendix.section/role="region"witharia-labelledbypointing at “Price (USD)” heading; addsr-onlyoraria-live="polite"summary for interval + last price + loading state; ensure interval buttons have discernible names (aria-label={${iv} interval}if needed).<table>with<th scope="col">for Price/Size/Total, or (b) keep list but addaria-labelon each row summarizing side/price/size and expose column headers viaid+headers/aria-describedby.tabpanel/aria-controlsfor limit vs market panels; auditrole="status"vsrole="alert"for tx errors.menuitemor menu container; on close, return focus to trigger; optional focus trap insideModalfor connect flow (coordinate with risk modaldismissible={false}behavior).Phase B — Automation
@axe-core/playwrightdevDependency; helperassertNoCriticalA11yViolations(page, { include, exclude }).e2e/a11y-critical-routes.spec.ts: scan/trade,/charts, wallet menu (use existing connect helpers orPLAYWRIGHT_SKIP_CHAIN=1smoke with disconnected state where valid).eslint-plugin-jsx-a11ywith recommended rules; fix baseline in trade/chart/wallet dirs first.jest-axeon renderedPriceChartempty +WalletButtondisconnected — complements but does not replace route-level axe.Phase C — Docs
docs/frontend.md§ Accessibility CI — routes scanned, how to run locally (npm run test:e2e -- e2e/a11y-critical-routes.spec.ts), exception process.skills/AGENTS_FRONTEND_A11Y_CI.mdfor agents.Acceptance criteria
/tradeand/chartsat default viewport (1280×720), and on header wallet connect menu (disconnected + connected states as feasible in smoke).aria-hiddenfrom canvas.@axe-core/playwright(or equivalent) runs in CI frontend E2E job on critical routes; failures block merge.docs/frontend.mdupdated with a11y CI invariants; gap row ingaps/GAP_1780023683.md→ Improved or Done.Test plan — functional paths
/tradewith default pair, wait for chart shell1h→15maria-labelpresent; axe cleanaria-pressedtoggles; focus not lost irrecoverably/chartsselect pairaria-selected; panel content associatedrole="dialog"+ title; wallet options namednavigation.spec.tsbandrole="alert"or status messages announced; axe still passes or documented exceptiontest:e2e(or a11y project) green with axe stepAutomated:
npm run test:run(Vitest);npm run test:e2e -- e2e/a11y-critical-routes.spec.ts(and full smoke on release candidates).Test plan — attack / abuse vectors
Accessibility fixes must not weaken security or mislead users about chain state.
pairAddresschange; no announcement of prior pair’s last pricearia-hiddenon real buttonsaria-label— Labels contradict visible networkgetNetworkBadgeCopy()short label (#186)aria-livefires every 30s refetcharia-labelincludesorder_id; prefill E2E unchangeddisableRulesin CIPLAYWRIGHT_SKIP_CHAIN=1Verification criteria
frontend-dappVitest green; new tests reference this issue IID in describe names or comments.npm run test:e2eincludes axe spec green on CI (or newe2e-a11yproject documented indocs/frontend.md)./trade— hears chart summary + interval; tab through order book actions.rg 'axe-core|@axe-core/playwright' frontend-dapp/package.jsonshows devDependency; no duplicate competing scanners without justification.Accessibilityrow updated ingaps/GAP_1780023683.md.Labels / metadata
frontend,QAmentioned in issue #215
mentioned in commit
99f4097b28mentioned in commit
e02d991ce9Implementation summary (GitLab #214)
Merged to
mainine02d991— systematic a11y on trade/chart/wallet plus an axe CI gate.What changed
role="region"with labelled heading +aria-livetext summary (interval, last price, loading); interval buttons getaria-label; canvas staysaria-hidden.<table>with column headers; rowaria-labelincludes side, order id, price, size.tabpanelwiring; pair-paused banner usesrole="alert".role="menu"contains menuitems only (header outside); connect buttonaria-label.@axe-core/playwright+e2e/a11y-critical-routes.spec.tsin e2e-smoke (WCAG 2.1 A/AA, critical/serious gate).skills/AGENTS_FRONTEND_A11Y_CI.md, gap row updated ingaps/GAP_1780023683.md.Verification checklist
cd frontend-dapp && npm run test:run— Vitest green (chart, order book, wallet a11y tests).PLAYWRIGHT_SKIP_CHAIN=1 npm run test:e2e:smoke -- e2e/a11y-critical-routes.spec.ts— axe smoke passes locally./tradewith indexer up: VoiceOver/NVDA hears chart summary on interval change; canvas not exposed.@brouie — please run through the checklist when you have a moment; leaving the issue open until verified.
mentioned in issue #228
marked as related to #228
mentioned in commit
13fef659eaVerification report (agent — GitLab #214)
Worktree:
verify/issue-214→ merged tomainas13fef65.What was already on
main(verified)@axe-core/playwrightine2e-smokeviae2e/a11y-critical-routes.spec.ts(WCAG 2.1 A+AA, zero critical/serious gate)role="region",aria-livepolite summary, intervalaria-label/aria-pressed<table>+<th scope="col">, rowaria-labelwith order idtablist/tab/tabpanelwiringdocs/frontend.md§ Accessibility CI,skills/AGENTS_FRONTEND_A11Y_CI.md, gap row Improved ingaps/GAP_1780023683.mdPriceChart.test.tsx,OrderBookPanel.test.tsx,WalletButton.test.tsx(#214 contracts)Fix applied during verification
/chartsaxe failure (aria-hidden-focus): lightweight-charts injects focusable#tv-attr-logoinsidearia-hiddencanvas wrapper.layout.attributionLogo: falseinPriceChartLightweightCanvasPriceChart(data-testid="price-chart-tradingview-attribution")docs/frontend.md,AGENTS_FRONTEND_A11Y_CI.md,AGENTS_FRONTEND_PRICE_CHART.mdAutomated runs (pass)
npm run test:run— PriceChart, OrderBookPanel, WalletButton, PriceChartLightweightCanvasPLAYWRIGHT_SKIP_CHAIN=1 npm run test:e2e:smoke -- e2e/a11y-critical-routes.spec.ts(4/4)npm run test:e2e:smoke -- e2e/a11y-critical-routes.spec.tswith LocalTerra/indexer up (4/4)Checklist for human follow-up
/trade: hear chart summary + interval; tab order book Edit/Cancel/trade+/charts)1h→15m: summary updates; re-run axe locally if desiredmainafter13fef65Not closed
Manual AT spot-check in verification criteria was not performed in this agent session. @brouie — please run NVDA/VoiceOver item above; if pass, close #214.
Verification report (agent — requested as #2174; mapped to #214)
Note: GitLab issue #2174 returns 404 on this project. The three checklist items in your prompt match the open human follow-up on #214 (a11y CI). Verification used worktree
verify/issue-214→ merged tomainase0a3c9f.Focused checklist (your three items)
/trade+/charts)data-testid="price-chart-tradingview-attribution"visible with canvas on both routes. Vitest asserts visible + href.aria-livesummaryInterval 1h→Interval 15mafter click. New Vitest: updates aria-live summary when switching 1h to 15m (#214).aria-labelConnected wallet on Local).Fix merged during verification
/tradeaxescrollable-region-focusable(serious): order bookoverflow-y-autopanes now usetabIndex={0}+role="region"+aria-labelso keyboard users can scroll asks/bids. Documented indocs/frontend.md§ Accessibility CI andskills/AGENTS_FRONTEND_A11Y_CI.md.Automated runs
npm run test:run— PriceChart (incl. new interval/attribution tests), OrderBookPanel, WalletButtone2e/a11y-critical-routes.spec.ts— trade + charts axe pass on worktree build (wallet specs need connected dev wallet / chain; skipped-chain wallet click timed out — not a regression on the three UI items above)Manual follow-up (issue stays open)
/trade(chart summary, book columns, tabs) — @brouiesounds.playButtonPress, etc.) — human only; see sub-checklist below for @totdkingSub-checklist: sound effects only (@totdking)
Please verify audio feedback only (no need to re-test chart attribution / interval summary / wallet Escape if the three items above already passed for you):
Report pass/fail here; tag @brouie if anything blocks closing #214 after AT pass.
Status: Leaving #214 open — NVDA/VoiceOver and sound-effects manual items remain. @brouie — please confirm AT when you can; if the three focused UI items above fail on
mainaftere0a3c9f, reply here.mentioned in commit
507ed875cfmentioned in commit
e0a3c9f02dmentioned in issue #244
#214 verified — good to close from my side. The manual AT (screen-reader) pass that was left to me is done.
NVDA (Windows) on /trade — all 5 contracts pass:
Automated / source / docs (re-confirmed on current main):
Remaining: the UI sound-effects sub-checklist is @totdking's (audio feedback, not assistive-tech) — separate from the a11y/AT criteria above.
So all the acceptance + verification criteria in the a11y/AT scope are met. Good to close once @totdking signs off the sounds sub-checklist. @PlasticDigits
Follow-up to my AT note above — I also ran the sound-effects sub-checklist, all pass:
So nothing is outstanding now: AT (NVDA) 5/5 + sounds 5/5 + the automated/axe/docs layer all verified. Fully good to close. @PlasticDigits
mentioned in issue #366
mentioned in issue #361
mentioned in issue #422
mentioned in issue #524
mentioned in issue #671
mentioned in issue #705