mcpField
C
charts-graphs

charts-graphs

github.com/mgifford设计与 UI57150 次安装6 周前更新
代码仓库

关于 charts-graphs

面向制作或审查图表的开发者,这项无障碍技能指导如何以分层方式呈现图表信息,并兼顾不同感知与交互方式。内容包括文本摘要、结构化数据、键盘操作和动态更新。

主要功能

  1. 01分层呈现图表信息
  2. 02提供摘要与结构化数据
  3. 03颜色和视觉编码建议
  4. 04键盘交互指导
  5. 05动态更新与状态反馈

适用场景

  1. 01为复杂图表补充文本说明
  2. 02为精确数值提供数据表
  3. 03检查交互图表的键盘支持
  4. 04审查颜色编码和筛选反馈

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mgifford/accessibility-skills --skill charts-graphs

安装后在 Claude Code 中输入/charts-graphs确认已加载。

附带文件SYNC.md

详细介绍

图表和图形的无障碍技能

规范来源:mgifford/ACCESSIBILITY.md 中的 examples/CHARTS_GRAPHS_ACCESSIBILITY_BEST_PRACTICES.md 此技能源自该文件。如有疑问,以该示例为准。

创建或审核任何数据可视化时,请遵循这些规则。 仅当项目包含图表或图形时,才加载此技能。


核心要求

图表必须传达信息并支持用户完成预期任务,且不能要求用户必须感知某种特定颜色、查看视觉位置、使用指针,或理解未作说明的图形。无障碍不能只靠一条很长的 alt 标签来实现——应采用分层呈现:易于理解的视觉内容、简明摘要、必要时提供结构化数据,以及为所有交互提供无障碍控件。描述的详略应视用途而定——单值迷你图和探索式仪表板所需的描述并不相同。

先了解用户的问题,再选择图表类型:图表应回答什么问题?哪种比较、趋势或分布最重要?用户需要结论、确切数值,还是探索数据?无障碍摘要应回答与视觉内容相同的问题——不要描述每项视觉属性,却遗漏结论。


严重程度等级(本技能)

等级含义
严重图表传达了关键信息,却完全没有无障碍替代方案
较高存在替代方案,但内容不完整或有明显误导性
中等提供了替代方案,但无法独立依赖颜色或对比度不足会造成使用障碍
较低未遵循最佳实践;对无障碍使用的影响有限

分层信息模型

提供用户需要的信息层次,避免将所有内容重复塞进一个很长的无障碍名称:

  1. 标题——标明主题
  2. 背景信息——范围、日期、单位、群体、筛选条件
  3. 摘要——主要趋势、比较或发现
  4. 结构化详情——必要时提供确切数值、类别或关系
  5. 来源和方法——方便用户评估和复用信息
  6. 交互反馈——当前筛选条件、选择项、加载状态、错误

不要将每个信息层次都放入图像的 alt、SVG <title> 或单个 aria-label 中——很长的无障碍名称难以审核和浏览。

选择呈现方式:

需求合适的起点
一个结论和少量数值可见文本,也可以使用简单的静态图表
精确比较多行或多列数据HTML 数据表格
可缩放的静态图表SVG,或导出为 <img> 的 SVG
包含大量标记的密集呈现Canvas + 可见的 HTML 摘要和数据访问方式
用户探索或筛选图表 + 原生 HTML 控件 + 结构化结果
空间关系地图或示意图 + 文本形式的对应位置或关系

SVG 并非天然具有无障碍性,Canvas 也并非天然缺乏无障碍性——无论采用何种渲染技术,实现都必须提供等效信息。如果表格或简短段落比图表更清楚地回答问题,就采用更简单的呈现方式。


严重:每个图表都必须有文本替代内容

没有文本替代内容的图表属于严重问题——盲人用户和禁用图像的用户将完全无法获取数据。

简单图表 — 使用简洁的 alt 说明一个明确的要点:

<figure>
  <img src="/assets/charts/quarterly-sales.svg"
       alt="Sales increased each quarter, from $1.4 million in Q1 to $2.3 million in Q4."
       width="800" height="450">
  <figcaption>Quarterly sales for 2025. Values are in Canadian dollars.</figcaption>
</figure>

请根据图表在上下文中的用途编写替代文本,而不是罗列视觉元素。避免使用“图表”“销售图表”、文件名、每个坐标轴刻度,或数据无法支持的解读。如果周围的文章已经说明了相同的发现,请让 alt 保持简洁,而不要强行重复说明。

复杂图表 — 简短标识 + 可见的详细描述:

<figure>
  <img src="/assets/charts/visitors-by-sector.svg"
       alt="Line chart of accessibility adoption from 2019 to 2024, described below."
       width="900" height="520">
  <figcaption>
    <h2>Accessibility adoption by sector, 2019 to 2024</h2>
    <p>Adoption increased in all three sectors. Government remained highest,
    rising from 42% to 78%. Corporate adoption rose from 28% to 61%, with its
    largest increase in 2021. Non-profit adoption rose from 19% to 47%.</p>
    <details><summary>View annual values</summary></details>
    <p>Source: Annual organizational survey. Percentages are rounded.</p>
  </figcaption>
</figure>

请确保详细描述可见——这不仅有益于屏幕阅读器用户,也有益于使用放大功能的用户、文本转语音用户、翻译用户,以及不熟悉相关主题的用户。aria-describedby 可以将图像与纯文本描述关联起来,但辅助技术通常会将引用的内容扁平化为一个字符串——标题、列表和表格会失去可导航的结构。不要将 aria-describedby 作为访问结构化详细描述的唯一途径。


严重:在用户需要具体数值时提供结构化数据

<details>
  <summary>View data for monthly website visitors</summary>
  <table>
    <caption>Monthly website visitors, January to March 2025</caption>
    <thead>
      <tr><th scope="col">Month</th><th scope="col">Visitors</th><th scope="col">Change</th></tr>
    </thead>
    <tbody>
      <tr><th scope="row">January</th><td>12,400</td><td>Not applicable</td></tr>
      <tr><th scope="row">February</th><td>15,800</td><td>27.4%</td></tr>
    </tbody>
  </table>
</details>

没有基于标准的阈值规定系列或数据点达到特定数量后就必须提供表格——如果任务需要精确数值或独立比较,请提供结构化数据。对于非常大的数据集,请提供易于使用的筛选表格,以及可下载的开放格式(CSV)文件——下载文件不能替代页面上的摘要。请说明缺失、抑制、估算或四舍五入的数值是如何表示的。


严重:SVG 图表

原子 SVG 图像 — 当图表作为单个图像呈现时:

<figure>
  <svg role="img" aria-labelledby="sales-title" aria-describedby="sales-summary" viewBox="0 0 640 360">
    <title id="sales-title">Quarterly sales for 2025</title>
    
  </svg>
  <figcaption id="sales-summary">
    Sales increased in every quarter, from $1.4 million in Q1 to $2.3 million
    in Q4. Values are in Canadian dollars.
  </figcaption>
</figure>

不要在根元素带有 role="img" 的 SVG 的后代元素上设置 role="list"/role="listitem",并期望它们仍可导航——图像角色表示原子图形,其子元素仅用于呈现;这不仅是支持不一致,还与 role="img" 的语义相矛盾。

结构化或交互式 SVG — 当用户必须浏览或操作各个部分时,不要用 role="img" 将整个图形扁平化。ARIA Graphics Module 定义了结构化图形角色,但必须使用项目实际采用的辅助技术组合进行测试。即使结构化 SVG 的无障碍树可以正常工作,也仍应提供可见的 HTML 摘要和数据呈现——不要迫使用户逐一浏览每个柱形、数据点和网格线才能了解结论。有关装饰性分组排除、唯一 ID 和 DOM 顺序,请参阅 skills/svg/SKILL.md。


严重:基于 Canvas 的图表必须提供无障碍替代内容

<canvas> 绘制的是像素——位图不会向辅助技术公开视觉对象、标签或关系。没有替代内容的 Canvas 图表至少属于严重问题;如果它是数据的唯一呈现方式,则属于严重问题(关键)。

<figure>
  <canvas id="regional-sales-chart" width="800" height="450"
          role="img" aria-label="Q2 sales by region"
          aria-describedby="regional-sales-summary">
    Q2 sales by region. A summary and data table follow.
  </canvas>
  <figcaption id="regional-sales-summary">
    The West had the highest Q2 sales at $510,000. The East had the lowest at
    $280,000. A complete data table follows.
  </figcaption>
</figure>

务必让可视化 Canvas 与相邻的摘要/表格保持同步——这是主要且最可靠的访问途径,比单独依赖 Canvas ARIA 更可靠。Canvas 回退内容(将交互区域映射为可聚焦后代元素)需要一对一地同步状态、焦点、命中区域和键盘操作——除非有经过测试的理由使用回退子树,否则请优先在 Canvas 外提供可见的 HTML 控件。不要只在 Canvas 绘图命令或库配置中放置关键信息。

**特定库的说明:**Chart.js 提供无障碍扩展点,但其内置表格只是起点,并非完整解决方案——请补充相邻摘要。D3 默认渲染 SVG,而非 Canvas——请使用上述 SVG 模式。Highcharts/Vega-Lite/Observable Plot 各自都有无障碍模块——请查阅最新文档,并始终补充相邻的数据表,无论库的说明如何宣称。请将生成的 ARIA 视为由团队负责维护的代码——检查浏览器无障碍树,不要依赖营销描述。

严重:颜色与视觉编码

不要仅用颜色来区分系列、状态、阈值或区域——约 8% 的男性和 0.5% 的女性存在色觉缺陷。请结合使用:直接文本标签、不同的线型、点形状、填充图案、符号、边界或说明清楚的位置。

<svg width="0" height="0" aria-hidden="true" style="position:absolute">
  <defs>
    <pattern id="pattern-dots" patternUnits="userSpaceOnUse" width="6" height="6">
      <circle cx="2" cy="2" r="1.5" fill="currentColor"/>
    </pattern>
  </defs>
</svg>
<rect fill="url(#pattern-dots)" x="50" y="50" width="60" height="100"/>

适用于色觉缺陷者的调色板:

  • Okabe-Ito —— 定量数据的推荐默认调色板:#E69F00、#56B4E9、#009E73、#F0E442、#0072B2、#D55E00、#CC79A7、#000000
  • ColorBrewer —— 提供顺序型(有序数据)、发散型(有明确中心的数据)或定性型(分类数据)调色板,并带有适用于色觉缺陷者的筛选器

不要仅仅因为某个调色板被描述为“适用于色盲者”就依赖它——请测试完整图表,包括线条粗细、点的大小、重叠标记、选中状态以及每种支持的主题。模拟是辅助审查的手段,不能替代标准测试。避免:仅用红色/绿色来区分(这是最常见的色觉缺陷组合);高饱和度的彩虹/jet 调色板(感知顺序不佳,对许多色觉缺陷者而言颜色均匀)。

**对比度:**文本标签 ≥4.5:1(大号文本为 3:1);必要的非文本图形信息与相邻颜色之间 ≥3:1。装饰性网格线不会仅仅因为出现在图表中就必须达到 3:1——只需测试理解或操作图表所必需的视觉信息。


严重:将数据浏览与操作分开

大多数图表数值无需交互。请提供摘要/表格用于浏览;按钮、链接和复选框则留给实际操作(筛选、选择、深入查看)。图例和筛选器请使用原生 HTML 控件:

<fieldset class="series-controls">
  <legend>Data series shown</legend>
  <label><input type="checkbox" name="series" value="2024" checked> 2024 sales</label>
  <label><input type="checkbox" name="series" value="2025" checked> 2025 sales</label>
</fieldset>

不要将每个数据点都加入页面的 Tab 键顺序——一个包含数百个焦点停靠点的图表虽然可以访问,但实际上无法操作。


严重:键盘交互

通过指针/触摸可用的所有功能,也必须支持键盘操作。优先使用 Tab 键在原生 HTML 控件之间进行常规导航。如果图表确实需要复合导航模型:按一次 Tab 键进入复合控件;使用方向键在可比较的项目间移动;适用时支持 Home/End;图表更新时保留当前位置;在图表之前或图表处提供说明;显示当前项目的名称/值/状态;提供直接退出的方式。不要为了显得全面而凭空设计键盘行为——按键必须符合控件的实际语义和实现。焦点指示器必须在所有图表颜色上保持可见,且不得被 SVG viewBox 或溢出容器裁切。

如果存在静态替代方案,则无法通过键盘访问的交互式图表属于严重问题;如果它是唯一的呈现方式,则属于关键问题。


严重:工具提示与数据详情

不要只通过悬停才能查看数值——请使用直接标签、无障碍表格、可聚焦控件或由用户控制的详情面板。对于大量详情,优先使用可展开内容,而非短暂显示的工具提示:

<button type="button" aria-expanded="false" aria-controls="march-details">
  March: 19,200 visitors
</button>
<div id="march-details" hidden>
  <p>March increased by 21.5% from February.</p>
</div>

如果自定义内容在悬停/聚焦时显示,WCAG 1.4.13 通常要求该内容可被关闭(无需移动指针/焦点)、可悬停且保持显示。同一内容必须能够通过键盘焦点触发;触摸用户则需要点击触发或持久显示的替代方式。不要在通过 aria-describedby 引用工具提示的同时对其设置 aria-hidden="true"——隐藏内容无法提供无障碍描述。


中等:动态更新和状态消息

筛选条件变化时,请根据同一数据模型更新可见标题、摘要、表格和图表。以编程方式公开结果数量、加载状态和更新结果:

<p id="chart-status" role="status" aria-atomic="true"></p>
function reportChartUpdate({ period, regions, total }) {
  document.getElementById("chart-status").textContent =
    `Showing ${period}: ${regions} regions, total sales ${total}.`;
}

请在更新发生前创建状态区域;公告应简洁。不要仅为强制重复播报而清空并重写实时区域,也不要播报快速更新图表中的每个数据点。WCAG 4.1.3 不要求每次变化都提供状态消息,而是要求现有消息可通过编程方式确定。对于实时图表,请提供暂停/刷新控件,并在新数据到达时保留焦点/选中状态。


中等:动画图表必须遵循 prefers-reduced-motion

@media (prefers-reduced-motion: reduce) {
  .chart-mark, .chart-axis {
    animation: none;
    transition: none;
  }
}
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Chart.js
options: { animation: prefersReduced ? false : { duration: 800 } }
// D3.js
selection.transition().duration(prefersReduced ? 0 : 750).attr('height', d => yScale(d.value));

请针对图表组件进行设置,而不是对所有 SVG 子元素施加通用规则。确保默认情况下无需动画也能理解信息;筛选条件变化时避免为每个标记添加动画;不要仅用动态效果表示变化;按照 WCAG 2.2.2 提供暂停/停止控件;无论用户的动态效果偏好如何,都应避免闪烁内容(WCAG 2.3.1)。对于循环播放/自动更新的仪表板,请提供可见的暂停控件。


中等:响应式布局、重排和缩放

请在 320 CSS 像素和 400% 缩放下测试——标题、摘要、控件、描述和表格单元格都必须能够重排,且不丢失信息。WCAG 1.4.10 对确实需要二维布局的内容(某些地图、示意图、数据表格)有例外——它不要求压缩每个图表,直到标签难以辨认。

<div class="chart-scroll" role="region" aria-label="Monthly sales chart, horizontally scrollable" tabindex="0">
  
</div>
.chart-scroll { max-inline-size: 100%; overflow: auto; }

需要二维滚动时:仅将滚动限制在图表/表格内,而非整个页面;提供可见名称和操作说明;确保键盘可操作;保持控件/焦点可见;提供无需空间平移即可理解的摘要。交互式图表控件必须满足 WCAG 2.5.8 最小目标尺寸要求,或符合明确规定的例外——较小的可视数据点可使用更大的不可见点击区域,而不改变标记的显示尺寸。


按图表类型提供指导

  • 条形图/柱状图 — 定量坐标轴应从零开始,除非使用了明确说明的基线;标注类别/单位;避免使用会扭曲长度/位置的 3D 效果
  • 折线图 — 除颜色外,还应通过标签/样式/形状区分数据系列;指出数据间隙,不要将未观测到的值在视觉上连接起来;说明对数/非线性刻度
  • 饼图/环形图 — 仅在任务确实是展示部分与整体关系时使用;避免用于精确比较或包含大量/相似扇区的情况;直接标注扇区;不要只将中心总计作为嵌入图形中的文字
  • 散点图 — 说明两个坐标轴、单位、样本量,以及点大小/形状的含义;描述群集/相关性/异常值,但不要夸大因果关系
  • 堆叠图表 — 只有共享基线的分段易于比较;提供表格以便精确比较不同类别;标注总计,并说明数值是绝对值还是百分比
  • 热力图 — 绝不能仅靠颜色渐变表达信息;重要单元格应通过标签/符号/边界/表格提供说明;注明刻度、阈值和缺失数据的处理方式
  • 地图 — 应以文本或结构化 HTML 提供位置/数值/路线,不要只依靠视觉位置/颜色/指针探索(参见 skills/maps/SKILL.md)

轻微:声音化和多模态替代方案

声音化(音高/时长/节奏/立体声位置)是一种实用的可选探索方式,不能替代文本和结构化数据。请说明每种声音维度代表什么;提供开始/暂停/重播/音量/静音控件;切勿自动播放音频;以文本提供等效数值;不要仅用声音表示状态或错误。


主题、强制颜色和导出

使用语义令牌(表面、文本、坐标轴、系列、焦点、选中状态、状态)验证每个受支持的主题。在强制颜色模式下:使用在颜色替换后仍可辨识的文本标签/图案/形状;必需的描边/焦点指示器使用系统颜色;不要对整个图表应用 forced-color-adjust: none;确认选中/隐藏/突出显示的系列仍可区分。

@media (forced-colors: active) {
  .chart-series { fill: none; stroke: CanvasText; }
  .chart-series[data-selected="true"] { stroke: Highlight; stroke-width: 4; }
}

**导出:**在 HTML/PDF/演示文稿格式中保留有意义的标题和简洁的替代说明;包含可见摘要和来源;在格式支持的情况下,包含结构化表格标签;提供开放格式的底层数据;保留单位/区域设置/日期范围/筛选条件/缺失值说明;当任务需要具体数值时,不要只导出截图。单独测试生成的 PDF/电子表格——网页图表的无障碍特性不会自动延续到导出文件中。


测试

  • **内容审查:**确认标题/范围/单位/筛选条件/来源准确;将可见摘要与当前数据进行比对;确认结论没有夸大相关性/精确度/确定性;检查缺失/估算/四舍五入的数值
  • **视觉/交互:**在灰度和 CVD 模拟模式下检查所有系列;测量每个主题/状态下的文本和必需非文本内容的对比度;测试 200%/400% 缩放和窄视口;测试键盘访问、焦点顺序、滚动区域;通过指针、键盘和触控触发工具提示/详情;更改每个筛选条件,并确认摘要/表格/图表保持同步;测试减少动态效果、增加对比度、强制颜色;暂停实时更新,并确认用户有时间读完内容
  • **AT 审查:**确认预期的角色/名称/描述;确认可见的结构化详情仍可通过标题/列表/表格进行导航;确保装饰性内部元素不会造成干扰;确认控件会播报名称/角色/值/状态;确认更新消息简洁,且不会过度重复
  • **自动化:**可以检测缺失的名称、无效的 ARIA、表格标记问题、对比度失败、重复 ID 和焦点问题——但无法判断摘要是否准确、表格是否等效,或探索是否实用。在组件测试、页面级扫描、视觉回归和数据同步测试中纳入图表;仍需进行人工审查

常见故障

故障修正方法
alt="Chart" 只说明对象是什么,却没有说明其中的信息说明图表的用途或主要发现
将所有视觉细节都放进一个很长的可访问名称中将标题、摘要、结构化数据和来源分开
aria-describedby 指向复杂表格,并假定它能保留导航功能让表格以结构化 HTML 的形式保持可见
SVG 根元素使用 role="img",同时后代元素使用列表/控件角色选择原子图像或经过测试的结构化图形模型,不要两者兼用
Canvas 数据只存在于绘图指令中提供可见摘要和 HTML 数据访问方式
虽然网格线只是装饰,却强制要求达到 3:1 对比度只测试理解或操作图表所必需的视觉信息
系列之间只通过颜色区分添加直接标签、线型、数据点形状或图案
将每个数据点都加入 Tab 顺序使用表格进行浏览;仅为实际操作保留控件
只有悬停提示会显示精确值提供直接标签、表格、键盘访问方式或持久显示面板
工具提示既是 aria-describedby 内容,又设置为 aria-hidden="true"确保描述内容可访问
每次筛选条件变化都强制通过实时区域播报仅播报有意义的状态消息
将图表压缩到 320px,直到标签无法辨认调整辅助内容的布局;必要时使用带标签的局部滚动区域
未测试输出就接受库的无障碍声明测试已安装的版本,并检查生成的语义
网页图表通过测试,但 PDF/图像导出没有替代内容分别测试并修复每种输出格式

完成标准检查清单

  • 图表回答了一个已记录的用户问题
  • 在适用情况下提供标题、范围、单位、日期范围、筛选条件和来源
  • 简明的文本替代内容传达了重要发现
  • 复杂信息提供可见的详细描述或结构化等效内容
  • 任务需要时,可通过无障碍 HTML 获取精确值
  • 说明缺失、取整、估算或抑制显示的值
  • 颜色不是区分数据或状态的唯一视觉方式
  • 使用适合色觉障碍者的调色板(Okabe-Ito 或 ColorBrewer 色觉障碍安全筛选器)
  • 文本和必要的图形信息符合对比度要求
  • SVG 使用原子 role="img" 或经过测试的结构化图形模型之一——不可混用
  • Canvas 图表:role="img" + aria-label + 相邻且同步的摘要/表格
  • 只有实际图表操作才会创建交互控件(不是为每个数据点创建控件)
  • 所有功能均可通过键盘操作;焦点始终可见
  • 悬停信息也可通过焦点和触摸获取
  • 动态更新会保留焦点,并提供简明的状态反馈
  • 辅助内容会重新排布;必要的二维滚动区域范围受限且可操作
  • 目标尺寸符合 WCAG 2.5.8,或满足规定的例外情况
  • 已测试减少动态效果、强制颜色和受支持的主题
  • 导出/下载格式保留信息和无障碍特性
  • 已使用屏幕阅读器进行测试

主要 WCAG 成功标准

  • 1.1.1 非文本内容(A)——没有文本替代内容时为严重问题
  • 1.3.1 信息和关系(A)
  • 1.3.3 感官特征(A)——说明不能只依赖位置/形状
  • 1.4.1 颜色的使用(A)——颜色是唯一编码方式时为严重问题
  • 1.4.3 对比度(最低)(AA)
  • 1.4.10 重排(AA)
  • 1.4.11 非文本对比度(AA)
  • 1.4.13 悬停或焦点时的内容(AA)
  • 2.1.1 键盘(A)
  • 2.2.2 暂停、停止、隐藏(A)
  • 2.3.1 三次闪烁或低于阈值(A)——快速闪烁的图表会构成严重问题
  • 2.5.8 目标尺寸(最低)(AA)
  • 4.1.3 状态消息(AA)

参考资料

标准展望: 数据可视化的对比度要求可能会随着 WCAG 3.0 的 APCA 模型而变化。目前适用的非文本对比度仍为 3:1。 请关注:https://www.w3.org/TR/wcag-3.0/

评论

支持 Markdown