Accessible Org Charts and Hierarchy Diagrams
Permalink to "Accessible Org Charts and Hierarchy Diagrams"Org charts, reporting structures, product taxonomies and decision trees are drawn as boxes connected by lines, but they are hierarchies — data that ARIA already knows how to express. The accessible approach is not to describe the drawing; it is to present the same hierarchy as a tree or nested list that screen reader users can navigate by level, with the visual chart as a parallel view. When the two share data and focus, everyone works with the same structure.
This guide belongs to accessible network & graph diagrams.
Spec reference
Permalink to "Spec reference"- SC 1.3.1 Info and Relationships (A) — reporting relationships must be programmatic: a nested list or a tree with levels.
- SC 1.1.1 Non-text Content (A) — the visual chart’s text alternative is the tree plus a summary.
- SC 2.1.1 Keyboard (A) — expand, collapse and inspect people or units without a pointer.
- SC 1.4.10 Reflow (AA) — wide charts must not require two-dimensional scrolling at 400% zoom; the tree view reflows naturally.
- ARIA tree pattern — for interactive hierarchies; a nested
ulsuffices for static ones.
When to use — and when not to
Permalink to "When to use — and when not to"Present any hierarchy diagram with a tree or nested list equivalent. For static, small charts (a team page), a nested list with role and team size is enough. For large, interactive charts (a company directory), use the tree pattern with lazy loading and search.
Do not describe the chart in a paragraph (“The CEO is at the top; below her are…”) beyond a dozen people; it does not scale and cannot be navigated. Do not use an edge table for strict hierarchies — levels are clearer as a tree.
The misapplication: a canvas-drawn org chart with a single “Organisation chart” alt text and pan-and-zoom as the only navigation.
Annotated code example
Permalink to "Annotated code example"<section aria-labelledby="org-h">
<h2 id="org-h">Organisation</h2>
<p>142 people. Ana Ruiz (CEO) has 3 direct reports.</p> <!-- summary -->
<ul role="tree" aria-label="Reporting lines"> <!-- SC 1.3.1 -->
<li role="treeitem" aria-expanded="true" tabindex="0">
Ana Ruiz, CEO <span class="meta">— 3 direct reports, 142 people</span>
<ul role="group">
<li role="treeitem" aria-expanded="false" tabindex="-1" aria-describedby="dl-1">
Li Wei, VP Engineering <span class="meta">— 2 teams, 88 people</span>
<span id="dl-1" class="visually-hidden">Product team also reports to Sam Okafor (dotted line).</span>
<ul role="group" hidden>…</ul>
</li>
</ul>
</li>
</ul>
</section>
<!-- Visual chart rendered from the same data; aria-hidden because the tree is the equivalent -->
<div class="org-canvas" aria-hidden="true"></div>
// Keep the visual chart in sync with tree focus (and vice versa)
tree.addEventListener('focusin', (e) => {
const id = e.target.closest('[role="treeitem"]')?.dataset.id;
if (id) chart.centerOn(id).highlight(id); // sighted keyboard users see where they are
});
chart.on('nodeclick', (id) => focusTreeItem(id, { expandAncestors: true }));
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Event | Expected announcement | AT deviations |
|---|---|---|
| Tab into tree | “Reporting lines, tree, Ana Ruiz, CEO — 3 direct reports, 142 people, expanded, level 1” | — |
| Right / Down | Expand / next | “Li Wei, VP Engineering — 2 teams, 88 people, collapsed, level 2” |
| Pause on Li Wei | Description: dotted line | Read after a pause |
| Search “Okafor” | Focus moves to Sam Okafor; ancestors expanded | — |
Integration context
Permalink to "Integration context"The tree behaviour comes from the tree role for category and file browsers and, for large organisations, lazy-loading tree nodes with aria-busy. Non-hierarchical relationships belong in a graph with adjacency tables. Selecting a person often opens a profile in a side panel — see master-detail & split views.
Gotchas
Permalink to "Gotchas"Dotted lines and matrix organisations. A person with two managers breaks a strict tree. Place them under their primary manager and describe the secondary line in text, or show them in both places with “(also listed under …)”.
Photos. Profile photos are decorative in an org chart when names are present; use alt="".
Vacancies. “Open role” boxes need text: “Vacant: Senior engineer, Platform team”.
Common mistakes
Permalink to "Common mistakes"- An image of the chart with a one-line alt text.
- Indentation-only structure with no levels.
- Dotted lines visible only as dashed strokes.
- Pan-and-zoom as the only navigation.
Framework notes
Permalink to "Framework notes"Libraries like d3-org-chart, OrgChart.js and GoJS render visual charts; none provides an accessible tree. Render the tree with your accessible tree component (React Aria, Angular CDK) from the same data and hide the canvas from AT with aria-hidden="true" once the tree is the equivalent. Synchronise selection both ways.
const chart = new OrgChart().container('.org-canvas').data(people).onNodeClick(id => focusTreeItem(id)).render();
Worked example
Permalink to "Worked example"An HR portal’s org chart was a zoomable SVG of 1,800 people with no text equivalent. A blind manager could not find her own skip-level reports. The portal added a “Reporting lines” tree beside the chart, lazy-loaded below the second level, with search by name and role, team sizes in each item, and dotted-line relationships as descriptions. Focus in the tree centres the visual chart on the same person, so sighted colleagues on a call can follow along.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Offer a HierarchyView that renders a tree from hierarchical data and optionally a visual chart from the same data, with focus synchronisation. Teams then cannot ship a chart without its tree.
FAQ
Permalink to "FAQ"Should the visual chart be hidden from screen readers?
If an equivalent tree is present, hiding the canvas or SVG avoids duplicated or meaningless output. Keep a short label on the figure.
Nested list or tree widget?
Nested list for small, static charts; tree widget for interactive or large ones.
How do I show headcount?
In each item’s text: “2 teams, 88 people”. Users use it to understand scope without expanding.
What about decision trees?
The same approach: a tree where each node states the question or outcome, with branch labels as part of child names (“If yes: …”).
Do org charts need search?
Beyond a hundred people, yes.
Can the tree replace the chart entirely?
For many users, yes. Keep the chart for those who benefit from the spatial view.
How do I show reporting changes over time?
Offer a date selector and announce changes as a summary (“3 people moved to Platform team since 1 September”), with a table of moves. Animating boxes between positions is not an accessible way to show change.
Related
Permalink to "Related"- The tree role — the equivalent structure
- Lazy-loading tree nodes — large hierarchies
- Keyboard traversal of node-link graphs — when relationships are not a tree