Skip to content

feat(a11y): add @visx/a11y - #2018

Merged
hshoff merged 17 commits into
masterfrom
harry/visx-a11y
Jun 15, 2026
Merged

hshoff merged 17 commits into
masterfrom
harry/visx-a11y

Conversation

@hshoff

@hshoff hshoff commented Jun 15, 2026

Copy link
Copy Markdown
Member

To be released in 4.1.0

Why @visx/a11y?

The post-4.0 registry work needs primitive charts to share accessibility behavior without turning visx
into a chart framework. Chart render trees should stay explicit: users own the SVG structure, visual
marks, tooltip state, and decorative chrome, while first-party helpers provide reusable semantics for
the pieces that every chart otherwise rebuilds.

@visx/a11y adds that shared layer for primitive charts. It provides generated chart descriptions,
ARIA props, data-table fallbacks, live announcements, and keyboard point exploration that can be
attached to the marks users already render.

The goal is to make accessible primitive charts easier to build consistently across the registry and
product code, while preserving the core visx model: low-level primitives, explicit rendering, and no
chart component hiding behind the package.

💥 Breaking Changes

  • None.

🚀 Enhancements

  • Add @visx/a11y, an optional package for chart accessibility helpers.
  • Add shared chart accessibility types for data accessors, series config, generated ids, ARIA props,
    locale overrides, and focused-point callbacks.
  • Add server-safe generators for chart descriptions, SVG/series/point ARIA props, and hidden data
    table HTML.
  • Add ChartA11yDataTable for exact-value fallback tables.
  • Add ChartA11yAnnouncer for polite or assertive live announcements.
  • Add useChartA11y for pre-bound svgProps, getSeriesProps, getPointProps, description
    metadata, table/announcer components, and imperative announcements.
  • Add useChartKeyboardNav for roving point focus, chart/data mode state, arrow-key navigation,
    Home/End boundaries, Escape exit behavior, keyboard help announcements, and onPointFocus
    tooltip bridging.
  • Export @visx/a11y from the umbrella @visx/visx package.

📝 Documentation

  • Add the @visx/a11y README with motivation, primitive-first design notes, server/client usage, and
    Level 1/2/3 adoption examples.
  • Document the keyboard interaction model for chart root focus, data-mode entry, point traversal,
    series traversal, boundary keys, exit behavior, and keyboard help.
  • Document the consumer responsibility for hiding decorative chart chrome such as grids, axes,
    background rects, and clipping helpers.
  • Add a newest-first MIGRATION.md entry for the new @visx/a11y package in visx 4.1.

🐛 Bug Fix

  • None.

🏠 Internal

  • Add package build, typecheck, and Vitest config for @visx/a11y.
  • Add tests for data normalization, generated ARIA props, chart descriptions, escaped table HTML,
    React data tables, live announcers, and hook return values.
  • Add keyboard navigation coverage across line, bar, pie, and multi-series charts.
  • Add regression coverage for tuple-valued flat data, dense-chart point threshold behavior, keyboard
    help announcements, and fallback table availability during keyboard exploration.
  • Wire @visx/a11y into workspace dependencies, root Vitest coverage, umbrella package exports,
    TypeScript references, and the lockfile.

Validation

  • yarn eslint --quiet packages/visx-a11y
  • yarn vitest run --project @visx/a11y
  • PKG=@visx/a11y yarn type:pkg
  • PKG=@visx/a11y yarn babel:pkg
  • PKG=@visx/visx yarn type:pkg
  • git diff --check
  • airchat codex review --base origin/master

@hshoff hshoff added this to the 4.1.0 milestone Jun 15, 2026
@hshoff hshoff self-assigned this Jun 15, 2026
@hshoff
hshoff requested a review from Copilot June 15, 2026 17:10
@github-actions

github-actions Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Size Changes

Package Diff ESM Prev ESM CJS Prev CJS
visx-visx +2.5% 1.68 KB 1.64 KB 4.3 KB 4.2 KB

Compared to master. File sizes are unminified and ungzipped.

View raw build stats

Previous (master)

{
  "visx-annotation": {
    "esm": 19693,
    "lib": 23755
  },
  "visx-axis": {
    "esm": 14072,
    "lib": 19318
  },
  "visx-bounds": {
    "esm": 1495,
    "lib": 1915
  },
  "visx-brush": {
    "esm": 42024,
    "lib": 44001
  },
  "visx-chart": {
    "esm": 1253,
    "lib": 1676
  },
  "visx-chord": {
    "esm": 1907,
    "lib": 2683
  },
  "visx-clip-path": {
    "esm": 1522,
    "lib": 2713
  },
  "visx-curve": {
    "esm": 351,
    "lib": 2594
  },
  "visx-delaunay": {
    "esm": 1475,
    "lib": 2489
  },
  "visx-demo": {
    "esm": 0,
    "lib": 0
  },
  "visx-drag": {
    "esm": 9078,
    "lib": 11025
  },
  "visx-event": {
    "esm": 3571,
    "lib": 5257
  },
  "visx-geo": {
    "esm": 8104,
    "lib": 11674
  },
  "visx-glyph": {
    "esm": 7243,
    "lib": 11421
  },
  "visx-gradient": {
    "esm": 6928,
    "lib": 11581
  },
  "visx-grid": {
    "esm": 9631,
    "lib": 12892
  },
  "visx-group": {
    "esm": 502,
    "lib": 1030
  },
  "visx-heatmap": {
    "esm": 3496,
    "lib": 4564
  },
  "visx-hierarchy": {
    "esm": 8687,
    "lib": 13458
  },
  "visx-kernel": {
    "esm": 12168,
    "lib": 15402
  },
  "visx-legend": {
    "esm": 14929,
    "lib": 20754
  },
  "visx-marker": {
    "esm": 3855,
    "lib": 6120
  },
  "visx-mock-data": {
    "esm": 325698,
    "lib": 330728
  },
  "visx-network": {
    "esm": 3088,
    "lib": 5105
  },
  "visx-pattern": {
    "esm": 7400,
    "lib": 10317
  },
  "visx-point": {
    "esm": 791,
    "lib": 1901
  },
  "visx-react-spring": {
    "esm": 8724,
    "lib": 11009
  },
  "visx-responsive": {
    "esm": 11775,
    "lib": 14019
  },
  "visx-sankey": {
    "esm": 2501,
    "lib": 3894
  },
  "visx-scale": {
    "esm": 19357,
    "lib": 31820
  },
  "visx-shape": {
    "esm": 52847,
    "lib": 76176
  },
  "visx-stats": {
    "esm": 9258,
    "lib": 10690
  },
  "visx-text": {
    "esm": 6416,
    "lib": 7990
  },
  "visx-theme": {
    "esm": 22444,
    "lib": 31441
  },
  "visx-threshold": {
    "esm": 2067,
    "lib": 2758
  },
  "visx-tooltip": {
    "esm": 9212,
    "lib": 13672
  },
  "visx-vendor": {
    "esm": 2974,
    "lib": 3226
  },
  "visx-visx": {
    "esm": 1676,
    "lib": 4304
  },
  "visx-voronoi": {
    "esm": 1909,
    "lib": 3235
  },
  "visx-wordcloud": {
    "esm": 1996,
    "lib": 2995
  },
  "visx-xychart": {
    "esm": 126022,
    "lib": 155810
  },
  "visx-zoom": {
    "esm": 12508,
    "lib": 14835
  }
}

Current

{
  "visx-a11y": {
    "esm": 28412,
    "lib": 34495
  },
  "visx-annotation": {
    "esm": 19693,
    "lib": 23755
  },
  "visx-axis": {
    "esm": 14072,
    "lib": 19318
  },
  "visx-bounds": {
    "esm": 1495,
    "lib": 1915
  },
  "visx-brush": {
    "esm": 42024,
    "lib": 44001
  },
  "visx-chart": {
    "esm": 1253,
    "lib": 1676
  },
  "visx-chord": {
    "esm": 1907,
    "lib": 2683
  },
  "visx-clip-path": {
    "esm": 1522,
    "lib": 2713
  },
  "visx-curve": {
    "esm": 351,
    "lib": 2594
  },
  "visx-delaunay": {
    "esm": 1475,
    "lib": 2489
  },
  "visx-demo": {
    "esm": 0,
    "lib": 0
  },
  "visx-drag": {
    "esm": 9078,
    "lib": 11025
  },
  "visx-event": {
    "esm": 3571,
    "lib": 5257
  },
  "visx-geo": {
    "esm": 8104,
    "lib": 11674
  },
  "visx-glyph": {
    "esm": 7243,
    "lib": 11421
  },
  "visx-gradient": {
    "esm": 6928,
    "lib": 11581
  },
  "visx-grid": {
    "esm": 9631,
    "lib": 12892
  },
  "visx-group": {
    "esm": 502,
    "lib": 1030
  },
  "visx-heatmap": {
    "esm": 3496,
    "lib": 4564
  },
  "visx-hierarchy": {
    "esm": 8687,
    "lib": 13458
  },
  "visx-kernel": {
    "esm": 12168,
    "lib": 15402
  },
  "visx-legend": {
    "esm": 14929,
    "lib": 20754
  },
  "visx-marker": {
    "esm": 3855,
    "lib": 6120
  },
  "visx-mock-data": {
    "esm": 325698,
    "lib": 330728
  },
  "visx-network": {
    "esm": 3088,
    "lib": 5105
  },
  "visx-pattern": {
    "esm": 7400,
    "lib": 10317
  },
  "visx-point": {
    "esm": 791,
    "lib": 1901
  },
  "visx-react-spring": {
    "esm": 8724,
    "lib": 11009
  },
  "visx-responsive": {
    "esm": 11775,
    "lib": 14019
  },
  "visx-sankey": {
    "esm": 2501,
    "lib": 3894
  },
  "visx-scale": {
    "esm": 19357,
    "lib": 31820
  },
  "visx-shape": {
    "esm": 52847,
    "lib": 76176
  },
  "visx-stats": {
    "esm": 9258,
    "lib": 10690
  },
  "visx-text": {
    "esm": 6416,
    "lib": 7990
  },
  "visx-theme": {
    "esm": 22444,
    "lib": 31441
  },
  "visx-threshold": {
    "esm": 2067,
    "lib": 2758
  },
  "visx-tooltip": {
    "esm": 9212,
    "lib": 13672
  },
  "visx-vendor": {
    "esm": 2974,
    "lib": 3226
  },
  "visx-visx": {
    "esm": 1718,
    "lib": 4399
  },
  "visx-voronoi": {
    "esm": 1909,
    "lib": 3235
  },
  "visx-wordcloud": {
    "esm": 1996,
    "lib": 2995
  },
  "visx-xychart": {
    "esm": 126022,
    "lib": 155810
  },
  "visx-zoom": {
    "esm": 12508,
    "lib": 14835
  }
}

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new optional package, @visx/a11y, providing accessibility helpers (ARIA semantics, generated descriptions, fallback tables, live announcements, and keyboard navigation) designed to integrate with visx’s primitive-first rendering model, and wires it into workspace tooling and the @visx/visx umbrella.

Changes:

  • Adds the new @visx/a11y package (client hook + components, server-safe generators, types, utilities) with Vitest + TS configs and comprehensive tests.
  • Integrates @visx/a11y into the monorepo (lockfile, root Vitest projects) and exports it from @visx/visx.
  • Documents the new package and adds a migration note for visx 4.1.

Reviewed changes

Copilot reviewed 31 out of 32 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
yarn.lock Adds workspace resolution entries for @visx/a11y and wires it into the workspace dependency graph.
vitest.config.ts Adds packages/visx-a11y/vitest.config.ts to the root Vitest project list.
packages/visx-visx/tsconfig.json Adds TS project reference to ../visx-a11y.
packages/visx-visx/src/index.ts Imports and re-exports @visx/a11y under the umbrella namespace.
packages/visx-visx/package.json Adds @visx/a11y as a dependency of @visx/visx.
packages/visx-a11y/vitest.config.ts New Vitest project config + aliases for @visx/a11y and @visx/a11y/server.
packages/visx-a11y/tsconfig.json New TS config for building @visx/a11y.
packages/visx-a11y/test/useChartA11y.test.tsx Tests for useChartA11y semantics, ids, table/announcer bindings, and feature flags.
packages/visx-a11y/test/keyboard.test.tsx Keyboard navigation integration tests across chart types and multi-series behavior.
packages/visx-a11y/test/index.test.ts Verifies package entry points export expected APIs.
packages/visx-a11y/test/generators.test.ts Tests server-safe generators for ARIA props, descriptions, and HTML table output.
packages/visx-a11y/test/dataTable.test.tsx Tests React data table rendering (hidden/visible) and SSR escaping.
packages/visx-a11y/test/data.test.ts Tests data normalization behavior and shared constants.
packages/visx-a11y/test/announcer.test.tsx Tests announcer live region behavior and SSR output.
packages/visx-a11y/src/utils/table.ts Adds normalized-table generation and visually-hidden styles.
packages/visx-a11y/src/utils/format.ts Adds HTML escaping, id generation/slugging, and value formatting helpers.
packages/visx-a11y/src/utils/data.ts Adds normalization logic/constants for single vs multi-series data.
packages/visx-a11y/src/useChartKeyboardNav.ts Adds roving-focus keyboard navigation hook and state coordination.
packages/visx-a11y/src/useChartA11y.tsx Adds main hook composing ARIA generators + keyboard nav + bound DataTable/Announcer.
packages/visx-a11y/src/types.ts Adds shared public types for config, locale overrides, and generated props.
packages/visx-a11y/src/server.ts Adds server-safe entrypoint exports for generators/types.
packages/visx-a11y/src/keyboard/stateMachine.ts Adds keyboard navigation state machine + transitions.
packages/visx-a11y/src/index.ts Adds root entrypoint exports (currently marked 'use client').
packages/visx-a11y/src/generators/description.ts Adds chart description generation (small/dense/pie variants).
packages/visx-a11y/src/generators/dataTableHTML.ts Adds server-side HTML generation for visually hidden tables.
packages/visx-a11y/src/generators/ariaProps.ts Adds ARIA props generator for svg/series/points.
packages/visx-a11y/src/components/ChartA11yDataTable.tsx Adds React fallback data table component.
packages/visx-a11y/src/components/ChartA11yAnnouncer.tsx Adds React live-region announcer component.
packages/visx-a11y/Readme.md Adds package documentation, usage patterns, and keyboard interaction model.
packages/visx-a11y/package.json Adds the new package manifest + exports for . and ./server.
packages/visx-a11y/.npmrc Disables package-lock in this package.
MIGRATION.md Adds a new migration entry describing the optional @visx/a11y package.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/visx-a11y/src/index.ts Resolved
Comment thread packages/visx-visx/src/index.ts Resolved
Comment thread packages/visx-a11y/src/types.ts Resolved
@hshoff
hshoff merged commit 93924fb into master Jun 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants