Accessibility
VirtualizedList from @prepared911/ui/virtualized-list keeps every row in the accessibility tree. The browser skips layout and paint for rows outside the viewport; it does not unmount them. The component owns the list semantics, the scroll region, and scroll-into-view when focus moves to a skipped row. You name the region, write the row content, and announce paging.
ul with role="list" and one li per item. Do not set a role on the row content. When the row you render is already a list item, pass rowElement={VirtualizedListRowElement.Presentation}: the wrapper becomes a div role="presentation" and your li is the list item, so no li nests in another.Scrollable exposes a role="region" tab stop. Pass scrollRegionLabel so that region has a name.scrollRegionLabel whenever the list scrolls and the rows themselves are not focusable.getItemKey when rows insert or reorder, so focus and state stay on the same item.loadingRow and errorRow are content you render. Put a polite live region on a loading row and an assertive error, with a retry control, when a page fails.--space-4 of overflow margin, which covers the focus ring's offset plus its width.scrollToIndex and scrollToOffset move the scrollport from a button or other control. VirtualizedListAlign.Start and End land the row clear of the edge fade.role="list" on the ul (kept even when the list has no markers).li per item. By default renderItem returns the row's content, not another list item; with rowElement set to Presentation it returns the li, and the wrapper is hidden from assistive technology.aria-posinset or aria-setsize. Every row is present, so the list's count is the DOM's count.