Tree
A hierarchical, keyboard-navigable list of expandable nodes built from a data array.
Live demo
Tree renders a nested hierarchy from an items array. Each node carries a label, an optional value, href, and children, plus flags for expanded, selected, and disabled.
It builds the WAI-ARIA tree pattern: a role="tree" with nested role="group" levels and role="treeitem" nodes carrying aria-level, aria-expanded, and aria-selected, with a single roving tab stop so the whole tree is one Tab stop and the arrow keys walk it. A twisty rotates on expand. A node with an href renders its row as a link whether or not it has children, so a branch can be both navigable and a group; the row navigates while the twisty (and Left/Right) work the children. A branch without an href is a pure container that toggles on click. Being data-driven keeps it robust across the bindings and a natural fit for a file or navigation tree. Three sizes (sm, md, lg) scale the row density.
When to use
How this component composes with the rest of the set.
Props
4 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Sizes
sm
Compact rows.
md
Default.
lg
Roomy rows.
States
selected
The chosen node: its row takes the accent background and text. When the theme's --selection-cue resolves to marker (a high-contrast or redundant-cues algorithm), a non-color check glyph is added so selection never rests on color alone.
row-hover
Pointer over a row: the hover tint.
expanded
An open parent: the twisty rotates and the child group shows.
focus-visible
Keyboard focus on a node: an inset token ring on its row.
disabled
A locked node: muted and non-interactive.
Anatomy
The named parts that make up the component, with their selectors.
tree
The role="tree" root list holding the top-level nodes.
row
A node's clickable row, indented by its level; a <div>, or an <a> when the node has an href.
twisty
The disclosure caret on parent nodes; rotates 90° when the node is expanded, hidden on leaves.
group
A nested role="group" list of child nodes; hidden when its parent is collapsed.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xoji-default register
(derive(xojiDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--accent-bg
--accent-text
--border-normal
--border-thick
--duration-fast
--ease-standard
--fg-0
--fg-1
--fg-2
--fg-3
--fg-disabled
--font-sans
--radius-sm
--ring
--selection-cue
--space-1
--space-2
--space-4
--state-hover
--text-body
--text-sm
--weight-medium
Accessibility
Code
A documentation tree
An expanded Guides branch with a selected page and links, beside a collapsed Reference branch.
<xoji-tree label="Documentation"></xoji-tree>
<script>
document.querySelector("xoji-tree").items = [
{
label: "Guides",
expanded: true,
children: [
{ label: "Getting started", href: "/guides/start", selected: true },
{ label: "Theming", href: "/guides/theming" },
],
},
{
label: "Reference",
children: [{ label: "Engine", href: "/reference/engine" }],
},
];
</script>
<script lang="ts">
import { Tree } from "@xoji/svelte";
const items = [
{
label: "Guides",
expanded: true,
children: [
{ label: "Getting started", href: "/guides/start", selected: true },
{ label: "Theming", href: "/guides/theming" },
],
},
{ label: "Reference", children: [{ label: "Engine", href: "/reference/engine" }] },
];
</script>
<Tree label="Documentation" {items} onselect={(e) => console.log(e.detail.value)} />
---
import { Tree } from "@xoji/astro";
const items = [
{
label: "Guides",
expanded: true,
children: [
{ label: "Getting started", href: "/guides/start", selected: true },
{ label: "Theming", href: "/guides/theming" },
],
},
{ label: "Reference", children: [{ label: "Engine", href: "/reference/engine" }] },
];
---
<Tree label="Documentation" items={items} />