Announcing Olli v3: Diagrams, Choropleths, and Customizable Descriptions
Data visualizations are one of the most common ways information is communicated on the web, and they are frequently inaccessible to blind or low-vision screen reader users. Olli is an open-source JavaScript library that converts visualizations into keyboard-navigable tree structures, where each level of the tree describes a different level of detail about the chart. Navigate to the root and hear a chart summary; press the down arrow to enter the x-axis and hear each category; go deeper to reach individual data points.
Today we’re releasing Olli v3, a ground-up rewrite that significantly expands what Olli can describe and how users interact with it.
Try it: focus on the example below, then use the arrow keys to navigate. Press Down to drill into axes and categories, Left/Right to step across siblings, Up to go back. Press ? for a full list of keyboard shortcuts. If you’re having trouble, see the setup guide to make sure your screen reader is configured so keypresses reach Olli.
A more diverse example gallery
Olli originally shipped with examples primarily covering bar charts, scatterplots, and line charts. In v3, the gallery has grown to cover a much wider range of charts:
- Pie and donut charts — arc-based charts that are common in dashboards and reports
- Heatmaps and table-based plots — including lasagna plots for temporal pattern visualization
- Streamgraphs and area charts — stacked area visualizations common in time-series data
- Bubble plots — scatterplots with a size dimension
- Population pyramids — back-to-back bar charts used in demographic analysis
- Multi-view displays — faceted, layered, and concatenated chart compositions
Choropleth maps
The most exciting new example type is geographic. Olli v3 can now describe choropleth maps—the kind of shaded geographic maps you see in election results, public health dashboards, and census data reporting. Geographic data often comes in formats like TopoJSON with numeric FIPS codes instead of readable place names. When you navigate a tree and hear “county 06037”, that’s not useful. So Olli’s adapter layer automatically detects FIPS codes in the data, looks them up, and enriches each data point with the corresponding county name, state name, and US census region. Navigate the choropleth example and you’ll hear “Los Angeles County, California” instead of a five-digit code.
This locale-aware data enrichment is currently built for the US (county and state FIPS codes), but the approach is designed to be extensible to other regions and geographic coding systems in the future.
Beyond charts: diagram accessibility
Olli v3 isn’t just about charts anymore. Under the hood, the data model was generalized from a chart-specific representation to a domain-agnostic hypergraph. This means Olli can now describe structured information that isn’t necessarily tree-shaped—things like mechanical diagrams, system architectures, or any set of elements with relationships between them. This work is grounded in our research on accessible descriptions of relational diagrams, published as Benthic.
Why a hypergraph rather than a tree? In many diagrams, an element belongs to more than one grouping at the same time. For example, a rope passes through two different pulley systems, or a component sits inside one subsystem while also being connected to another. A tree forces each element into a single parent, but a hypergraph lets the same element appear in multiple parent contexts. In the Benthic paper we called this parent context switching: the ability to navigate an element from any of the groups it belongs to. This is what makes the resulting accessible structure perceptually congruent—its organization mirrors the visual groupings a sighted reader perceives.
The new olliDiagram entry point accepts a DiagramSpec describing elements and their relationships: containment, grouping, connections, alignment, and distribution. A Bluefish adapter converts Bluefish layout specifications into diagram specs automatically.
The gallery includes a pulley diagram example drawn from our Benthic user studies. It represents a mechanical system of pulleys, ropes, and weights. Navigate the structure to understand which components are connected, which systems they belong to, and how the elements are spatially organized.
This architecture also opens the door for third-party domain plugins. If you have a domain with structured relationships that should be navigable as a hypergraph, you can build a domain plugin that provides its own description tokens, keyboard shortcuts, and dialogs. You can also directly author a hypergraph and render it with olli’s domain-agnostic base entrypoint.
Better descriptions
In v2, Olli’s descriptions were generated from a fixed template. Our research paper Customization is Key found that screen reader users have diverse and sometimes conflicting preferences for how chart descriptions should be structured—what information to include, how verbose to be, and in what order. v3 puts this finding into practice with a token-based description system. Each piece of information in a description is a separate token—e.g., the item’s name, its position among siblings, its depth level, the number of children, aggregate statistics. Tokens are composed into recipes that you can customize:
- Presets: switch between Detailed, Standard, and Minimal verbosity in one step
- Per-token control: turn individual tokens on or off, choose between concise and full versions
- Token reordering: put the information you care about most at the beginning of the description
- Live preview: see how your changes affect the description before committing
Press d on any tree to open the description settings dialog and try it. Your customizations persist in your browser across sessions.
Improved new user experience
Olli relies on keyboard interaction, which means screen reader users need to be in the right mode for keypresses to reach Olli rather than being intercepted by the screen reader itself. This is a common stumbling block.
v3 adds a built-in help dialog (press ? from any tree) that detects your operating system and shows setup instructions for your screen reader, with explicit support for VoiceOver, NVDA, and JAWS. The help dialog also lists all available keyboard shortcuts for the current tree, so you don’t need to memorize anything up front. Different trees may have different shortcuts available depending on their domain (charts vs. diagrams), and the help dialog always reflects what’s available in the current graph.
Chart highlighting
When you navigate an Olli tree that’s paired with a rendered chart, v3 now provides utilities to highlight the corresponding data on the visualization. Move focus to “Origin equals USA” in the tree, and only the USA data points stay fully visible on the chart while everything else dims. This creates a bidirectional link between the accessible tree and the visual representation. This is useful for sighted collaborators following along, for presentations, or for users with low vision who benefit from both the tree and the visual.
This works through a bridge library (@umwelt-data/umwelt-utils) that syncs Olli’s focus state with Vega-Lite’s selection store. The same pattern works for Bluefish diagrams: navigate the tree and the corresponding SVG elements highlight in the diagram. The quickstart guide shows how to set this up in your own project.
Simpler API for developers
v2 had two npm packages: olli and olli-adapters. v3 consolidates everything into a single olli package with subpath exports:
// v2
import { olli } from 'olli';
import { VegaLiteAdapter } from 'olli-adapters';
const spec = VegaLiteAdapter(vlSpec);
const el = olli(spec);
container.appendChild(el);
// v3
import { olliVis } from 'olli';
import { VegaLiteAdapter } from 'olli/adapters';
const spec = await VegaLiteAdapter(vlSpec);
const handle = olliVis(spec, container);
The adapters subpath is lazy-loaded, which means importing olli doesn’t pull in vega-lite or other adapter dependencies. This keeps bundle size small for applications that only use one adapter.
v3 has three entry points depending on your data:
| Entry point | Input | Use when |
|---|---|---|
olliVis |
Chart spec | Tabular data with axes, legends, marks |
olliDiagram |
Diagram spec | Relational data with containment, connections |
olli |
Raw hypergraph | Custom domain or pre-built graph |
All three return an OlliHandle for programmatic control:
const handle = olliVis(spec, container);
handle.onFocusChange((navId) => { /* ... */ });
handle.onSelectionChange((selection) => { /* ... */ });
handle.setSelection(myPredicate);
handle.focus(someNavId);
handle.destroy();
Under the hood
v3 is a ground-up rewrite organized as a layered architecture. At the bottom, olli-core provides a domain-agnostic hypergraph data model, predicate-based selection, and a reactive navigation runtime built on Solid.js signals. Above that, olli-render-solid implements the accessible tree view as declarative Solid.js components. Domain-specific logic lives in separate layers: olli-vis for charts, olli-diagram for diagrams. Each registers their own description tokens, keyboard shortcuts, dialogs, and predicate providers through an extensible registry pattern. Adapters for Vega-Lite and Bluefish sit at the top, converting external specs into Olli’s internal representation. The public olli package wraps everything with a vanilla JS API.
This layered design means adding a new domain (like we did with diagrams) doesn’t require touching core navigation or rendering code. The developer docs cover the architecture in detail, including a guide for creating custom domains.
Get started
- Try the gallery to see Olli v3 in action across chart types and diagrams.
- Read the getting started guide to learn keyboard navigation with a screen reader.
- Follow the developer quickstart to add Olli to your own site.
- Install it:
npm install olli
If you’re migrating from v2, the main changes are: adapters are now at olli/adapters instead of olli-adapters, adapter calls are async, and the entry point mounts into a container and returns an OlliHandle instead of returning a raw DOM element.
We’d love your feedback! Open an issue or try Olli with your own visualizations and let us know how it goes.