Announcing Graph Structure Summaries

Permalink to "Announcing Graph Structure Summaries"

A sighted user glancing at a network diagram picks up its shape in a second: two dense clusters joined by one bridge, a hub in the middle, a few stragglers at the edge. A screen reader user meeting the same diagram hears “image” or a generic label — or, if the graph is explorable, starts at one node with no idea of the whole. A structural summary provides the glance: how many nodes and connections, what the main groups are, which nodes are hubs, what stands out.

This guide belongs to accessible network & graph diagrams.

Spec reference

Permalink to "Spec reference"
  • SC 1.1.1 Non-text Content (A) — the short text alternative identifies the graph; the summary is the long description of its structure.
  • SC 1.3.1 Info and Relationships (A) — the summary’s structure (a list of key facts) is marked up as a list.
  • SC 4.1.3 Status Messages (AA) — when filters change the graph, a short summary update is a status message.
  • figure / figcaption, aria-describedby — the usual way to tie a summary to a graphic.
Generating a summary from the graph model Flow: compute basic counts; find hubs by degree; find clusters with a community algorithm; find isolated nodes and bridges; write a sentence and bullets; place before the graph. Generating a summary from the graph modelCountsnodes, edges, density"42 services, 96 connections"Hubshighest degree"Auth service: 11 connections"Clusterscommunity detection"3 groups: checkout, catalogue,data"Outliersisolated nodes, bridges"1 bridge: Gateway joins checkoutand catalogue"Write and placesentence plus bulletscaption or description before thegraph

When to use — and when not to

Permalink to "When to use — and when not to"

Write a summary for every network diagram. Generate it from data for any graph that changes; hand-write it only for static, published diagrams.

Do not make the summary a description of the drawing (“blue circles on the left are connected to orange circles”) — describe the structure in domain terms. Do not try to list every node in the summary; that is what the table is for.

The misapplication: an aria-label on the SVG like “Network graph showing service dependencies”. It names the graphic but says nothing about its structure.

A label versus a structural summary Comparison: a label names the diagram but conveys no structure; a summary gives counts, hubs, groups, outliers and pointers to the table and traversal. A label versus a structural summary✗ Label only"Service dependency graph"No sense of shapeHubs unknownOutliers unknownDead end✓ Structural summary"42 services, 96 connections, 3 groups""Auth service is the most connected (11)""Gateway is the only link between groups""2 services have no connections"Links to the table and to exploration

Annotated code example

Permalink to "Annotated code example"
<figure aria-labelledby="g-title" aria-describedby="g-summary">
  <h3 id="g-title">Service dependency graph</h3>
  <div id="g-summary">
    <p>42 services with 96 connections, in 3 groups.</p>                    <!-- headline -->
    <ul>                                                                     <!-- SC 1.3.1 -->
      <li>Most connected: Auth service (11), Gateway (9), Payments API (7).</li>
      <li>Groups: Checkout (14 services), Catalogue (17), Data platform (11).</li>
      <li>Gateway is the only connection between Checkout and Catalogue.</li>
      <li>2 services have no connections: Legacy reports, Fax bridge.</li>
    </ul>
    <p><a href="#edges-h">Table of all connections</a> · <a href="#graph-explore">Explore the graph with the keyboard</a></p>
  </div>
  <svg viewBox="0 0 20 20" id="graph-explore" role="application" aria-label="Service dependency graph, interactive" tabindex="0">…</svg>
</figure>
function summarise(g) {
  const hubs = [...g.nodes].sort((a, b) => b.degree - a.degree).slice(0, 3);
  const groups = louvain(g);                                   // community detection
  const bridges = findBridgesBetween(groups, g);
  const isolated = g.nodes.filter(n => n.degree === 0);
  return {
    headline: `${g.nodes.length} services with ${g.edges.length} connections, in ${groups.length} groups.`,
    bullets: [
      `Most connected: ${hubs.map(h => `${h.name} (${h.degree})`).join(', ')}.`,
      `Groups: ${groups.map(c => `${c.label} (${c.size} services)`).join(', ')}.`,
      ...bridges.map(b => `${b.name} is the only connection between ${b.a} and ${b.b}.`),
      isolated.length ? `${isolated.length} services have no connections: ${isolated.map(n => n.name).join(', ')}.` : null,
    ].filter(Boolean),
  };
}

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"
Event Expected result Notes
Reach the figure Heading, then summary paragraph and list Summary before the graph in reading order
Focus the graph Name, then description (summary) Long descriptions are read in full; keep it short
Apply a filter “Showing 14 services, 31 connections, 1 group” Polite status, once
Follow “Table of all connections” Focus moves to the table heading —
Length of different text alternatives Bar chart of approximate word counts: an aria-label of 4 words, a structural summary of 60 words, and a full node listing of 600 words. Length of different text alternativesaria-label onlynames the graph; no structureStructural summarycounts, hubs, groups, outliersFull node listingbelongs in a table, not a description
The summary sits between a label and a listing: enough to orient, short enough to hear.

Integration context

Permalink to "Integration context"

Summaries orient; adjacency tables as network graph alternatives complete; keyboard traversal of node-link graphs explores. The same generate-from-data approach for other charts is in generating accessible text alternatives for D3 charts and writing chart summaries and key takeaways.

Gotchas

Permalink to "Gotchas"

Algorithm labels. Community detection returns numbered clusters, not names. Name clusters by their dominant node type or a domain label; “cluster 2” means nothing.

Unstable clusters. Some algorithms give different groupings on each run. Seed them or cache results so the summary does not change between visits for no reason.

Over-long descriptions. aria-describedby reads everything in one go. Keep the summary to a headline and a handful of bullets.

Common mistakes

Permalink to "Common mistakes"
  • A label instead of a summary.
  • A summary that describes colours and positions instead of structure.
  • A hand-written summary on a graph that changes daily.
  • Listing every node in the description.

Framework notes

Permalink to "Framework notes"

Graphology (JavaScript) provides degree, community detection (Louvain) and bridge-finding utilities that work with Sigma.js renderers. NetworkX (Python) can precompute summaries server-side for large graphs. Cytoscape.js has degree centrality and clustering extensions. Whichever you use, the summary is data; render it with the graph and update it with filters.

import louvain from 'graphology-communities-louvain';
const communities = louvain(graph);           // node -> community id

Worked example

Permalink to "Worked example"

A knowledge-graph explorer showed 300 concepts with an aria-label of “Concept graph”. Screen reader users had no idea where to begin. A generated summary now reads: “300 concepts, 1,140 links, 6 topic areas. Most connected: Accessibility tree (48), Focus management (41), Live regions (37). Testing and Charts are linked only through Colour contrast.” With links to the table and to exploration starting at the top hub, users had a map before they entered the territory.

Writing good summary bullets

Permalink to "Writing good summary bullets"

Each bullet should answer a question a sighted user answers at a glance. “What is the biggest thing?” — the top hubs with degrees. “How is it organised?” — the named groups with sizes. “What is surprising?” — bridges, isolated nodes, unusually dense pairs. “What changed?” — for monitored graphs, new or removed nodes since the last view. Order bullets by importance for the task, and keep each under twenty words so they are easy to hear.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Provide a GraphSummary component that accepts the graph model and label functions for node types and clusters, renders the headline and bullets, and exposes the text for aria-describedby and exports.

FAQ

Permalink to "FAQ"
How long should a summary be?

A headline sentence and three to six bullets. Anything longer belongs in the table.

Should the summary mention specific nodes?

The top hubs, bridges and outliers, yes — they are what sighted users notice first.

Can I use AI to write summaries?

Generated text needs review for accuracy. Computed facts (counts, degrees) are safer than free-form narration.

Where should the summary go visually?

Above or beside the graph, visible to everyone. Sighted users benefit from it too.

Should filter changes re-announce the whole summary?

Only the headline: “Showing 14 services, 31 connections.” Users can read the updated bullets if they want.

What about directed graphs?

Mention direction where it matters: “Auth service is called by 11 services and calls 2.”

Should the summary mention data freshness?

For monitored graphs, yes: “As of 10:42” in the headline tells users whether the structure they are hearing is current.

Permalink to "Related"

← Back to Accessible Network & Graph Diagrams