0.5.0 is the first element quality release, covering every A, B and C element. Components rebuild cleanly when moved or re-populated, keyboard and screen-reader access is fixed across the C components, and several XSS paths are closed. Themes gain a --btn-border-width hook,…
Author-grouped view of site content. Reads pages.json and renders an accordion of authors with their pages listed underneath.
Overview
Reads pages.json via data-lens-src (or src) and groups pages by author. It adds a filter input, a live result count and per-author page counts. If the source request fails, any light-DOM markup you wrote inside the host stays in place and still gets the filter; if the host was empty, an error message is shown instead. See Fallback markup.
The demo loads a local fixture with several authors, shown twice: sorted alphabetically and with sort="count".
Group order for fetched data: alphabetical by author, or by page count (most first; ties alphabetical). Any other value falls back to alpha.
heading-level
1–6
2
Level of the generated Authors (N) heading. Set it to fit the page outline, for example 3 when the index sits under an <h2>.
Source contract
The component expects either a JSON array of records or an object with a pages array, where each record carries at minimum:
pages.json record
{"url":"/docs/concepts/document-provenance/","title":"Document Provenance","author":"Thomas A. Powell","authorUrl":"/team/tpowell/","modified":"2026-04-24","provenance":"ai-assisted","signed":true}
VB's Cook SSG plugin generate-pages-json emits this shape automatically.
Fallback markup
Light-DOM markup inside the host is the no-JS rendering, and it is never thrown away for nothing:
No src: nothing is fetched. The component keeps your markup and adds the filter input and live status.
src succeeds: the fetched data replaces the markup.
src fails (network error, non-2xx status, or invalid JSON): your markup stays and is enhanced exactly as if there were no src. An error message appears only if the host was empty.
Each group to be filtered must carry data-author-group. A group is hidden when its visible text does not contain the query. Matching ignores case and diacritics, so zoe finds Zoë. Native <details> elements keep the groups usable and keyboard-accessible without JavaScript.
Authored fallback with a source
<author-indexsrc="/pages.json"placeholder="Filter authors"><h2>Authors <small>(1)</small></h2><detailsdata-author-groupid="author-thomas-a-powell"><summary>Thomas A. Powell (2)</summary><ul><li><ahref="/docs/concepts/document-provenance/">Document Provenance</a></li><li><ahref="/docs/concepts/data-api/">Data API</a></li></ul></details></author-index>
Accessibility
Filter: a type="search" input whose accessible name is the placeholder text.
Result count: a polite live region (role="status") after the input announces results as you type, such as 2 of 5 authors match “zo” or Showing all 5 authors. It is visually hidden while there are results. When nothing matches, it becomes the visible No authors match … message.
Page counts: the visible (3) is aria-hidden. Screen readers hear real visually-hidden text instead (Ada Lovelace, 3 pages), because an aria-label on a generic <span> is ignored.
Heading: the generated heading defaults to <h2>. Use heading-level to keep the page outline correct.
Provenance colours use the VB status tokens (--color-success-text, --color-warning-text, --color-error-text), so they follow the theme and keep text contrast. The provenance word is always shown as text, so colour is never the only signal.
Ids and links
Group ids (author-<slug>) are stable link targets. The slug is the author name, NFKD-normalised, with diacritics stripped and lowercased. Letters and digits of any script are kept, so Zoë Ng becomes author-zoe-ng and 李明 becomes author-李明. A name with nothing usable left falls back to author. On a collision, either within one index or with an id already on the page (for example two indexes), the slug gets a -2, -3, … suffix. Ids are never empty and never shared.
Links from the source (url, authorUrl) are only rendered when they are http:, https: or relative. Values such as javascript: or data: are dropped. The page title is then shown as plain text, and the profile link is omitted.
Lifecycle
The fetch uses an AbortController that is aborted when the element is removed, so a detached index never renders late. Once the index is rendered, moving it in the DOM re-wires the filter without refetching or rebuilding.