Skip to content

fix(a11y): mark the active language with aria-current, not aria-pressed (#414) - #740

Merged
HugoXK merged 1 commit into
mainfrom
dev3/414-lang-switch-aria
Nov 15, 2026
Merged

HugoXK merged 1 commit into
mainfrom
dev3/414-lang-switch-aria

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 15, 2026

Copy link
Copy Markdown
Owner

Written-by: HugoXK
Author: Fledge Platform Dev-3

Fixes #414

maintainer_can_modify: false (same-repo head, GitHub-forced)

What was wrong

Every page rendered the language switch as two <a> elements carrying aria-pressed (true on the active one, false on the other) next to aria-current="true". aria-pressed is defined only for role="button", so on a link it is an author error (aria-allowed-attr in axe): a screen reader either ignores the claim or announces a toggle the link cannot honour, while aria-current already carries the truth. The highlight was keyed on aria-pressed too, so the attribute could not simply be dropped.

What changed

  • src/views/layout.ts:59 — the language link emits aria-current="true" on the active language and nothing else; aria-pressed is gone from both links. data-lang-btn stays, so public/app.js (which selects on it, not on the removed attribute) is untouched.
  • public/app.css:351 — the highlight selector moved with the state: .lang a[aria-pressed="true"] → .lang a[aria-current="true"], same background and color, so the current language keeps its background and accent colour.
  • test/layout.test.ts — render() takes an optional locale (default 'en'), and one new test asserts, on a zh render and on the default en render, that no <a> carries aria-pressed, that the active link still carries aria-current="true", that the stylesheet keys the highlight on aria-current="true", and that no rule is left keyed on the retired state.
  • changelog.d/414-language-switch-aria.fixed.md — the user-visible half.

Evidence

Red at b0adcc5f7380672a4c5afe1538bb57675017cdf7 (live main, unmodified sources plus this test): test/layout.test.ts is 11 pass / 1 fail, not ok 12 - the language switch announces the active language with aria-current, not aria-pressed, AssertionError: aria-pressed is a button state; a link must not claim it. The failure is the assertion on the rendered zh page, not a missing fixture.

Green at this tip: test/layout.test.ts 12/12, and npm run check exit 0 with 1804/1804 (lint, typecheck, all suites and build).

Test-merged onto live main 970fc32d2c8d28fd9ed834b20709dc1b9df1b553 (#724, which touches src/app.ts, docs/*, test/projectMemory.test.ts and changelog.d/610.fixed.md — no file in common with this change): merge 1efe3257042c3252a9b68ec81bedae980060bea8, landing tree 05475edfe121ec12ff30ea4de4afd36b54070027, test/layout.test.ts 12/12 and npm run check exit 0 with 1806/1806, changelog 9 fragments, version 0.3.6, risk-tier: T2; basis: src/views/layout.ts at that base. The re-pointed commit described below therefore lands on the tree measured here.

pr-lint body check: checkBody → [] against .github/agent/review-policy.json at b0adcc5f; changed paths are 4 (3 modified, 1 added fragment), well under the size rule.

Ran:

  • npx tsx --test test/layout.test.ts at b0adcc5f with the new test — 11/12, the red above
  • npx tsx --test test/layout.test.ts at this tip — 12/12
  • npm run check at this tip — exit 0, 1804/1804
  • test-merge onto live main 970fc32d: merge 1efe3257042c3252a9b68ec81bedae980060bea8, landing tree 05475edfe121ec12ff30ea4de4afd36b54070027 — test/layout.test.ts 12/12, npm run check exit 0, 1806/1806
  • node scripts/changelog.mjs check — 8 fragment(s) at this tip, 9 at the merge landing
  • node scripts/version.mjs check — 0.3.6 ok
  • node scripts/risk-gate.mjs --base b0adcc5f --head HEAD — risk-tier: T2; basis: src/views/layout.ts; the same command with --base 970fc32d at the merge landing — risk-tier: T2

Not run:

  • The axe/CI accessibility check on a live instance — CI runs the workflow, and the assertion here pins the markup rather than a rendering.
  • A browser screenshot pass: the declarations moved verbatim, so there is nothing new to look at; the visual result is the same background/color pair.

Docs-not-needed: the language switch's ARIA contract is asserted by test/layout.test.ts; docs/handoff UI prose describes the switch's presence, not its attribute, and no documented behaviour changes.

Changelog: changelog.d/414-language-switch-aria.fixed.md

Risk: risk-tier: T2; basis: src/views/layout.ts (label risk:T2)

Tip SHA: d35c8e1 (handed commit, parent b0adcc5f)
Tree: 561828fcba157890701a2815bde5ec2de3faf4d6
Base: 4b481f0 (live main after #726 merged)
Diff: 4 files, +29/−4

How this branch was pushed

Dev-3's seat has no push credential (Chief's decision: the shared token stays off extra seats). Dev-3 built, tested and packaged the commit locally with Day Dreamer <87640074+HugoXK@users.noreply.github.com>; the Lead pushed the same content through the API Git Data path. Two mechanical things happened on the way and neither changes the diff:

  • The parent was re-pointed from b0adcc5f (Dev-3's base) to live main 4b481f056ddcead7993493fee7a7447782fbb270, because #726 merged while the bundle was in flight.
  • public/app.css took a three-way apply, not a file replacement: #726 added the admin-hub rules to the same file, and replacing it wholesale would have deleted twelve lines of #726. Only Dev-3's one-line selector change is applied — verified by the compare, public/app.css +1/−1, and the whole diff stays 4 files, +29/−4.

@HugoXK-bot HugoXK-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: PASS at d35c8e1
Written-by: HugoXK-bot
Author: Fledge Platform Reviewer
Posted-by: HugoXK-bot

T2 a11y fix (#414): the language switch marks the active language with aria-current="true" and no longer puts aria-pressed on its links.

Reviewed at the tip: src/views/layout.ts:56-60, public/app.css:351, test/layout.test.ts (+26), changelog. 4 files, +29/−4.

What I checked:

  • the attribute is gone from both links, not only the inactive one: item() now emits aria-pressed nowhere and keeps aria-current="true" only for the current locale (src/views/layout.ts:59). git grep finds no other data-lang-btn producer.
  • the highlight moved with the state: the only .lang rule that paints the current language is .lang a[aria-current="true"] (public/app.css:351), and no .lang a[aria-pressed…] rule remains, so the visible mark cannot silently disappear.
  • the test renders both locales through the real layout() and asserts the attribute's absence on the active and inactive link, the presence of aria-current on the active one, and - importantly - that the stylesheet keeps its highlight on the new selector and has none on the old one (test/layout.test.ts:363-380). That is the failure mode a plain markup diff would miss.

Ran (this turn, at this tip):

  • npx tsx --test test/layout.test.ts — pass 12/12

Red half (same suite at the PR's base 4b481f05): the new test fails there — aria-pressed is still present, which is the defect.

Not run: an axe run in a real browser; the suite asserts the rendered markup and the CSS rule.

Conclusion: PASS — the button state is gone from links, the active state and its highlight moved together, and a test pins both.

@HugoXK-bot HugoXK-bot added the review:approved Issue reviewed and approved by the Fledge Platform Reviewer label Nov 15, 2026
@HugoXK
HugoXK merged commit edce2d3 into main Nov 15, 2026
35 of 36 checks passed
@HugoXK
HugoXK deleted the dev3/414-lang-switch-aria branch November 15, 2026 03:26
…ed (#414)

Replayed onto live main 4b481f0: public/app.css took a 3-way apply because #726
added the admin-hub rules to the same file; the change itself is the one line
that moves the active-language selector from aria-pressed to aria-current.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved Issue reviewed and approved by the Fledge Platform Reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

accessibility: the language switch puts aria-pressed on a link, which no screen reader maps

2 participants