Skip to content

fix(a11y): apply external a11y-skill audit findings - #190

Merged
sinduri-g merged 8 commits into
mainfrom
fix/a11y-skill-audit
Jul 20, 2026
Merged

sinduri-g merged 8 commits into
mainfrom
fix/a11y-skill-audit

Conversation

@sinduri-g

@sinduri-g sinduri-g commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor

Audited this repo against three external agent a11y skill sets — frontend-a11y (mikemai2awesome), accessibility-skills (mgifford), and the Intopia web-accessibility skill — and applied the findings that were genuine divergences. The codebase was already strongly compliant (native <details>, mounted-then-filled live regions, no positive tabindex, :focus-visible, route-change announcer, tooltip Escape handling); these are the remaining items.

Changes

  • Navbar: hamburger keeps a constant aria-label="Menu"; aria-expanded carries open/closed instead of flipping the name.
  • ConsentBanner: dropped the redundant title on the reset button (title is for <iframe> only; aria-label already names it).
  • AboutSection / ChallengeHighlights: removed synthetic <section aria-label="…"> landmarks that had no real heading; render plain <div>s so the screen-reader landmarks menu is not cluttered.
  • SolutionDetail: renamed the sidebar <aside> label from "Solution navigation" to "Solution sidebar" (it is a complementary region, not navigation).
  • External links: moved the "opens in a new tab" hint out of each link's accessible name into a single shared hidden #new-tab-hint node referenced via aria-describedby. Removed inline sr-only spans and the phrase from social-icon aria-labels across 22 components/pages, and updated the adventure generator (annotateExternalLinks) so generated prose links use aria-describedby too.

Notes

  • The new-tab-hint change also adds aria-describedby to a few external links that previously had no hint at all, making hinting consistent. Improvement, not a regression.
  • Regenerated adventure files are isolated in their own commit; no YAML source content changed.

Verification

  • npm run lint: clean
  • npm test: 1097 passed
  • npm run build: clean rebuild succeeds
  • npm run test:e2e: 215 passed (axe in dark + light mode)

🤖 Generated with Claude Code

Audited against frontend-a11y (mikemai2awesome), accessibility-skills
(mgifford), and the Intopia web-accessibility skill.

- Navbar: hamburger keeps a constant `aria-label="Menu"`; `aria-expanded`
  carries open/closed instead of flipping the name (keep-name-constant rule).
- ConsentBanner: drop redundant `title` on the reset button (title attribute
  is for `<iframe>` only; `aria-label` already names it).
- AboutSection, ChallengeHighlights: drop synthetic `<section aria-label>`
  landmarks with no real heading; render plain `<div>`s so the landmarks menu
  is not cluttered by generically named regions.
- SolutionDetail: rename the sidebar `<aside>` label from "Solution navigation"
  to "Solution sidebar" (it is a complementary region, not navigation).
- External links: move the "opens in a new tab" hint out of each link's
  accessible name into a single shared hidden `#new-tab-hint` node referenced
  via `aria-describedby`. Removes the inline sr-only spans and the phrase from
  social-icon `aria-label`s across components and pages, and updates the
  adventure generator (`annotateExternalLinks`) to emit `aria-describedby`.
- Update tests and styleguide for the new external-link pattern.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- Run `npm run generate` after updating `annotateExternalLinks`.
- External links in generated prose now carry `aria-describedby="new-tab-hint"`
  instead of an inline sr-only "(opens in new tab)" span.
- No YAML source content changed.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
@sinduri-g sinduri-g changed the title Fix/a11y skill audit fix(a11y): apply external a11y-skill audit findings Jul 20, 2026
@github-actions

github-actions Bot commented Jul 20, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-20 13:19 UTC

- remark-gfm autolinks bare URLs authored in prose, so addresses like
  http://localhost:8080/ became clickable new-tab external links on the
  deployed site, pointing at the visitor's own machine.
- `annotateExternalLinks` now unwraps non-public URLs (loopback, mDNS, and
  single-label hosts) to plain text instead of stamping target/rel and the
  new-tab hint on them. Real public links are unchanged.
- Add a regression test asserting generated files link no loopback URL and
  keep localhost addresses as plain text.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- Run `npm run generate` after the annotateExternalLinks change.
- localhost URLs in generated prose now render as plain text, not links.
- No YAML source content changed.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- isNonPublicUrl now also unwraps private IPv4 ranges (10.x, 192.168.x,
  172.16-31.x) so a bare private-IP URL in prose cannot become a new-tab link
  to the visitor's own network on the deployed site.
- Extend the regression test's loopback pattern to cover those ranges.
- No content change: no adventure prose contains such URLs, so regeneration is
  a no-op.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- The rule was globally off to allow intentional role="list" on <ul>/<ol>
  (Safari VoiceOver strips list semantics under list-none).
- Re-enable with { ul: ["list"], ol: ["list"] } so that workaround stays valid
  while other redundant roles (e.g. role="button" on <button>) are caught at
  lint time. Verified: role="button" on <button> is flagged, role="list" on
  <ul> is not; lint stays green.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
CLAUDE.md tells contributors to read ACCESSIBILITY.md before touching any
component, so the adjudicated rules belong there, not only in the on-demand
audit command. Two existing bullets contradicted the shipped code and are fixed.

- ACCESSIBILITY.md:
  - External links: replace the removed per-link sr-only span with
    aria-describedby="new-tab-hint"; add the no-linked-localhost rule.
  - Semantic HTML: correct the section-naming bullet (name a <section> landmark
    only via a real heading; never invent a synthetic aria-label; use a plain
    <div> when there is no heading).
  - ARIA: constant toggle name across states; title only on <iframe>; the
    native-disabled vs aria-disabled nuance.
- a11y-audit.md: Component Rules + Contrast and Transparency guardrail sections,
  plus a Credits section for the community sources that informed them.
- keyboard.md: Disabled Controls rule per the WAI-ARIA APG Button Pattern.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
- Rename the floating consent-reset button's accessible name from "Reset cookie
  preferences" to "Cookie Preferences": clicking re-opens the preferences banner
  rather than resetting to defaults, so the destination name is more accurate.
  The button stays icon-only with the Cookie glyph aria-hidden (name via
  aria-label).
- Fix the visual-regression mask selector, which referenced a non-existent
  aria-label="Change cookie preferences" and never matched; it now mirrors the
  real aria-label (the file's documented pattern).
- Update the e2e and unit name assertions to match.

Signed-off-by: Sinduri Guntupalli <sinduri.guntupalli@dynatrace.com>
@sinduri-g
sinduri-g merged commit b577672 into main Jul 20, 2026
10 checks passed
@sinduri-g
sinduri-g deleted the fix/a11y-skill-audit branch July 20, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant