Olli is a library for converting data representations into accessible text structures for screen reader users. It features a hypergraph core that supports both data visualizations and arbitrary diagrams.
Docs & examples: https://umwelt-data.github.io/olli/
Five layers, strict bottom-up dependency:
L5 olli-adapters Vega, Vega-Lite, Observable Plot, Bluefish → specs
L4 olli-vis Visualization domain (tokens, dialogs, keybindings, presets)
olli-diagram Diagram domain (generic hypergraph authoring)
L3 olli-render-solid Accessible ARIA tree view (Solid components)
L2 olli-core Description framework (token registry, customization, reactive describe)
L1 olli-core Navigation runtime (nav tree, focus, selection, plugin registries)
L0 olli-core Hypergraph data model + predicate evaluation
olli Vanilla-JS consumer wrapper (imperative API over Solid internals)
| Package | Description |
|---|---|
olli-core |
Hypergraph, predicates, navigation runtime, description framework |
olli-render-solid |
Solid components: TreeView, TreeItem, NodeLabel, Dialog |
olli-vis |
Visualization domain: spec types, lowerer, tokens, dialogs, keybindings, presets |
olli-diagram |
Diagram domain: direct hyperedge authoring |
olli-adapters |
Adapters for Vega, Vega-Lite, Observable Plot, Bluefish |
olli |
Vanilla-JS entry point with imperative OlliHandle API |
pnpm installpnpm run check # Full verification: boundaries + build + test typecheck + test (run before pushing)
pnpm run build # Build all packages (tsc -b for internals, bundled build for olli)
pnpm test # vitest (single run)
pnpm test:watch # vitest in watch mode
pnpm run check:boundaries # Verify import layer rules
pnpm run coverage:vl # Run VegaLiteAdapter against a vega-lite checkout's example specs and diff against scripts/vl-coverage-baseline.json (see --help)Adapter snapshot tests capture the structural skeleton of each adapter's output to guard against silent changes. Snapshots live in packages/olli-adapters/src/__snapshots__/. When you intentionally change adapter output:
pnpm test -- --updateReview the diff in the .snap file to confirm only expected changes.
check:boundaries enforces the layer architecture — a package can only import from packages in lower layers. If you add a new import and this check fails, you're violating the dependency direction.
pnpm --filter playground dev # Interactive dev environment for testing olli
pnpm --filter docs dev # Docs site at http://localhost:5173/olli/See apps/docs/README.md for the step-by-step guide.
olli is the sole public npm package — consumers only need npm install olli. All internal workspace packages (olli-core, olli-render-solid, olli-vis, olli-diagram, olli-adapters) are bundled into the published olli package at build time — they are never published separately. The package exposes two entry points: olli (core + renderer + types) and olli/adapters (all adapters). This split keeps adapter dependencies like vega-lite lazy — they're only loaded when olli/adapters is imported.
olli also depends on @umwelt-data/umwelt-utils, which is a separate published package shared with the Umwelt project. If you've made changes to umwelt-utils, publish it first.
-
Ensure
@umwelt-data/umwelt-utilsis published and the version inpackages/olli/package.jsonpoints to a real npm version (not alink:path). The same applies forpackages/olli-core/package.json,packages/olli-vis/package.json, andpackages/olli-adapters/package.json. -
Run the full check:
pnpm run check # boundaries + build + test — must pass -
Build the publishable bundle:
pnpm --filter olli run build
This runs four steps (see
packages/olli/README.mdfor details):tsc -b— compiles all workspace packagestsup— bundles JS, inlining all workspace packagesrollup— bundles.d.ts, inlining all workspace type declarations- CSS copy from
olli-render-solid
-
Verify the bundle has no internal references:
grep 'from.*olli-core\|from.*olli-vis\|from.*olli-diagram\|from.*olli-adapters\|from.*olli-render' packages/olli/dist/index.js packages/olli/dist/index.d.ts packages/olli/dist/adapters.js packages/olli/dist/adapters.d.tsThis should produce no output.
-
Publish:
cd packages/olli npm publish --access public -
Smoke test in a fresh directory:
mkdir /tmp/olli-test && cd /tmp/olli-test npm init -y && npm install olli node -e "import('olli').then(m => console.log(Object.keys(m)))"