Skip to content

feat(a11y): compiler-generated chart descriptions + ECharts aria/decal#62

Open
zl190 wants to merge 1 commit into
microsoft:devfrom
zl190:feat/a11y-baseline
Open

feat(a11y): compiler-generated chart descriptions + ECharts aria/decal#62
zl190 wants to merge 1 commit into
microsoft:devfrom
zl190:feat/a11y-baseline

Conversation

@zl190

@zl190 zl190 commented Jul 18, 2026

Copy link
Copy Markdown

What

Refs #48. The compiled output of all three backends currently carries no accessibility surface — probed directly: the VL spec has no description, the ECharts option has no aria, and the Chart.js config has no attachment point. Since Flint sits at the compile step, one generator covers all three backends. This PR adds that baseline.

Implementation

  • New core/a11y-description.ts: a deterministic description generator built from what the pipeline already knows — chart type, encoded fields with semantic types and units, and cheap statistics (category count, series count, value range). Structural + statistical only; no trend interpretation. Generated copy looks like:
    • "Bar Chart of revenue (USD) by region. 3 categories. Range 145–168."
    • "Line Chart of revenue by month, grouped by team. 2 series. Range 214–217."
    • "Scatter Plot." — when a statistic can't be computed it is left out.
  • Injection, on by default (invisible metadata, no visual change): VL top-level description; ECharts aria: { enabled: true, label: { description } }; Chart.js has no native slot, so the config carries _a11y.description (matching the existing _warnings / _pivot config metadata) with a two-line wiring note in the docs for putting it on the canvas aria-label.
  • ECharts decal patterns (a visible change) are opt-in via options.a11yDecal, flat naming matching addTooltips.
  • docs/accessibility.md: what is injected, the knob, the Chart.js wiring, and a Chartability coverage note stating what this baseline addresses and what it doesn't.

Three decisions here are policy calls, each a one-line flip if you'd prefer them different: default-on for the invisible metadata, opt-in for decal, and stopping the copy at structural + statistical.

Verification

  • tests/a11y-baseline.test.ts — 11 cases: generator output across semantic combos (incl. unit / intrinsicDomain annotations), VL description present, ECharts aria on by default, decal only when opted in, Chart.js attachment, JSON purity unaffected
  • Full suite: 434 + 50 passing with no changes to existing test expectations; typecheck clean; eslint clean on new files

ECharts decal off vs on (the only visual change, and only when opted in):

ECharts rendering with decal patterns off versus on

🤖 Generated with Claude Code

Generate a short accessible description from the semantic layer at compile
time (chart type, measure with unit, dimension, series, category/series
counts, value range — structural and statistical only, no interpretive
claims) and emit it on every backend: Vega-Lite top-level description,
ECharts aria.label via the built-in aria module (enabled by default), and
_a11y.description metadata for Chart.js hosts to wire onto the canvas
aria-label. field_display_names is respected.

ECharts decal patterns (color-vision-deficiency support) are available
behind options.a11yDecal — opt-in because they visibly change the chart,
while the description metadata is invisible and always on.

- core/a11y-description.ts: deterministic description builder
- AssembleOptions.a11yDecal knob (ECharts only)
- tests: 11 cases (copy exactness, grouped series, display names,
  determinism, per-backend surfaces, decal gating, JSON purity); no
  existing test expectations changed
- docs/accessibility.md + api-reference link (includes the Chart.js
  wiring note and a Chartability coverage statement)

Refs microsoft#48

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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