/ Docs

A11y #

Baseline keyboard navigation, single-select, and focus management. Adds ARIA-compliant interaction without the full selection() plugin.

import { createVList, a11y } from "vlist";

const list = createVList({
  container: "#app",
  item: { height: 48, template: renderItem },
  items: data,
}, [a11y()]);

list.on("focus:change", ({ id, index }) => {
  console.log("Focused:", id);
});

Config #

No configuration — a11y() takes no arguments.

When to use #

Scenario Plugin
No keyboard nav needed Neither — set interactive: false
Basic keyboard nav + single-select a11y()
Multi-select, range, Ctrl+A selection()

a11y() is a no-op when selection() is active — you don't need both.

Keyboard #

Key Action
Arrow Up / Down Move focus by 1 (list), by row (grid), within lane (masonry)
Arrow Left / Right Disabled in list mode. Move by 1 in grid, adjacent lane in masonry
Home / End Jump to first / last item
Page Up / Down Jump by visible page
Space / Enter Toggle selection on focused item

Navigation adapts automatically when combined with grid() or masonry() — no extra configuration needed.

Events #

Event Payload
focus:change { id: string | number, index: number }
selection:change { selected: (string | number)[], items: T[] }

ARIA #

  • Sets aria-activedescendant on the content element to track keyboard focus
  • Items receive aria-selected and CSS classes ({prefix}-item--focused, {prefix}-item--selected) via the item state callback
  • Requires interactive: true (the default) for role="listbox" and tabindex="0" on the content element
  • Screen reader announcements via a live region ("Item N of M", "Selected", "Deselected")

Notes #

  • Group headers are automatically skipped during keyboard navigation
  • Click selects the clicked item; focus ring only shown on keyboard navigation
  • Focus is restored to the last focused item when the list regains focus
  • Priority 55 — runs after selection() (50) so it can detect and yield to it