Problem/Motivation

Government and enterprise users require data visualization tools to meet strict accessibility standards (Section 508 and WCAG 2.1 AA/AAA). Currently, the Charts module generates visual-only charts (SVG or Canvas) that can be "black boxes" to screen reader users. There is no standardized way to provide a text-based data alternative (like an HTML table) or to attach essential descriptive metadata (visible captions and screen-reader summaries) directly to the chart element.

Steps to reproduce

  1. Create a chart using any supported library (e.g., Highcharts*, Chart.js) via Views or the Charts API.
  2. Inspect the output: the chart is rendered without a semantic container linking it to a description.
  3. Attempt to add a visible caption or a hidden summary for AT users; there are no configuration options for this.
  4. Attempt to view the underlying data as a screen reader user; the data is only available within the visual graphic, making it inaccessible.

Proposed resolution

We should implement a comprehensive accessibility suite that works across all charting libraries by handling accessibility at the Drupal render layer, rather than relying on library-specific plugins.

  • Semantic Wrapper: Charts should be wrapped in a <div role="figure"> with aria-labelledby and aria-describedby attributes to strictly associate descriptions with the graphic.
  • Data Table Alternative: A new service (ChartTableBuilder) should automatically transform chart data into an accessible HTML table. This will ensure all users can access the precise data points.
  • User Control: Site builders should be able to toggle the data table's visibility (Collapsible, Always Visible, Screen Reader Only, or Disabled).
  • Descriptive Fields: Need to add "Figure Caption" (visible) and "Chart Summary" (visually hidden) fields to the configuration.
  • Token Support: Captions and summaries should support global, entity, and Views tokens, allowing for dynamic descriptions (e.g., "Monthly Sales for [node:title]").

Remaining tasks

  • Verify that the ChartTableBuilder service correctly parses and formats data structures across all supported libraries (Highcharts, Google, Chart.js, etc.) and complex types (scatter, heatmap, candlestick).
  • Verify that existing sites remain unaffected (no visual regressions) unless they explicitly opt-in by configuring the new features.
  • Update the module documentation and charts.api.php to reflect the new render element properties.
  • Conduct a formal accessibility review against the latest ADA/508 standards to ensure the new markup meets compliance targets.

User interface changes

  • Chart Configuration (Views/Blocks/Element): Added a "Figure caption" textarea (supports HTML) and a "Chart summary" textarea.
  • Display Settings: Added a "Show data table below chart" checkbox.
  • Table Configuration: When the table is enabled, new options appear to control its visibility state ("Collapsible details", "Always visible", "Screen reader only") and to assign custom CSS classes to the toggle button.
  • Frontend: Charts now appear inside a semantic container. If enabled, a "View data table" button (or the table itself) appears below the chart.

API changes

  • New Service: charts.table_builder (Drupal\charts\Service\ChartTableBuilder) is available for generating tables from chart render elements programmatically.
  • Render Element: The chart element accepts new properties:
    • #figure_caption: String or render array.
    • #chart_summary: String.
    • #display_data_table: Boolean.
    • #accessible_table: String ('collapsible', 'visible', 'invisible', 'disabled').
    • #accessible_table_button_class: String.

Data model changes

  • Updated charts.schema.yml to include the new display settings fields (display_data_table, accessible_table, accessible_table_button_class, figure_caption, chart_summary).
  • Existing configurations will need to be updated (via save) or handled via post-update hooks to pick up default values, though the code ensures backward compatibility by defaulting to disabled/empty if keys are missing.

Issue fork charts-3550972

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mutasim al-shoura created an issue. See original summary.

mutasim al-shoura’s picture

StatusFileSize
new3.61 KB
andileco’s picture

@mutasim al-shoura - thank you for creating this ticket. If you are using Highcharts, here are my recommendations that don't require a patch:

*If you scroll to the bottom of https://www.highcharts.com/docs/accessibility/accessibility-module, you'll see that using the caption is a great option for the description, and the nice thing is that it gets included in the Chart export, too.

However, the above is really Highcharts-specific. Would you be interested in making a more generic solution, which would apply to all the libraries? You have a start to that in your patch.

By the way, if you could create a merge request, I would appreciate that over a patch, as it runs code checks and tests.

andileco’s picture

Issue tags: +a11y

Adding the a11y tag.

andileco’s picture

Issue summary: View changes
andileco’s picture

Title: Add an ALT text field for chart descriptions. » Provide features to ensure that Charts meets accessibility standards
Assigned: Unassigned » andileco
Priority: Normal » Major
andileco’s picture

andileco’s picture

Status: Active » Needs review
andrewozone’s picture

StatusFileSize
new459.73 KB

I worked with @andileco today at the DrupalCon contribution day. We used Drupal Forge to spin up a Drupal CMS site, installed the Charts module, and tested the latest patch.

Critical Issue: Drupal CMS Compatibility

While testing with Drupal CMS and the Byte 1.0.2 theme, we encountered an error indicating a missing Twig template. This issue should be addressed to ensure the Charts module is fully compatible with the Drupal CMS ecosystem. @andileco has taken detailed notes on this bug.

Recommendation 1: Documentation & Authoring Guidelines

We recommend updating the module documentation to provide clear guidance and examples for new fields. Without this, content authors may provide duplicative or overly verbose descriptions.

Proposed Standards for Authors:

  • Character Limit: Keep alt text to 125–150 characters for maximum screen reader and browser compatibility.
  • Purpose-Driven: Summarize the chart’s purpose and the conclusions drawn from it.
  • The Formula: [Chart type] of [Type of data] where [Reason for including chart]. Example: "Bar chart of the average household's funds over time comparing saving versus investing, where investing grows money faster than saving."
  • Placement: Place the summary first to ensure screen reader users receive vital information immediately.

Additional Resources:

Recommendation 2: UI Description Updates

Update the description language for the new fields within the module interface to lead content authors directly to the updated documentation.

Recommendation 3: ARIA Attribute Usage

We suggest removing aria-labelledby in favor of aria-describedby. Using both can be too verbose for site visitors using assistive technology. Following HTML specs, aria-describedby is the more appropriate choice for these detailed descriptions.

Additional Feature Requests

Accessibility Enhancements

  • Color Contrast Checker: A tool to check the contrast ratios of chart items. Ideally, within the interface rather than relying on content authors using another tool.
  • Textures & Patterns: An option to add textures to data series to assist users with color vision deficiency (color blindness).
  • Visible Text Description (Best Practice): An on-screen text area near the chart for trends, patterns, and conclusions. This helps all users—not just those using screen readers—connect the dots between data and takeaways.
  • Table Alternative (Recommended): A tabular data format option for users who process data better in a non-visual way.

General UI

Captions: An optional field for data attribution and sources.

screen shot of rendered HTML pointing at the ARIA attributes

andileco’s picture

OK, some follow-ups:

  1. I wasn't able to replicate that bug we saw. I installed DrupalCMS with Byte and didn't have any issues this time.
  2. I updated the language for the "Caption" and "Accessible summary" fields to make them more clear.
  3. I made some adjustments to the "Accessible table" section, making the trigger button text configurable and also ensuring that the table element fills its parent container.
  4. I removed the ariaLabeledBy property, since that was confusing.

I'm going to create the documentation page and a link from within the code as a separate, follow-on issue.

Going to follow up to this ticket with a Change Record, as the Twig template is changing.

andileco’s picture

Status: Needs review » Reviewed & tested by the community

I'm going to commit, but I wanted to list a few changes since the last review.

1) I added an aria label, as the element needs a name. The element comes from the chart title, but it's not grabbed from the rendered chart, it's grabbed from the settings.
2) I added tabindex=0 on the chart container to make it focusable so that the screen reader announces the "summary" text before moving to the table.
3) I added column and row scopes on the already semantic table.
4) I ensured that the caption in the table contains the chart title or a placeholder if no title has been set - otherwise it was ending abruptly.
5) I also returned to the original Twig template and moved the logic I had added into the .module file - this reduces the likelihood that something a user had done within Twig will break. That said, the next release will bump a minor version.

andileco’s picture

Status: Reviewed & tested by the community » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.