Tree
Trees display hierarchical data (file systems, org charts, or nested categories) with expand/collapse per branch.
Try it
Usage
Pass a nodes tree structure or compose tree items as children. Bind value to the selected node id.
import { RuiTree, type RuiTreeNode } from '@ecopages/radiant-ui/tree';
const nodes: RuiTreeNode[] = [
{ id: 'src', label: 'src', children: [{ id: 'app', label: 'app.ts' }] },
];
<RuiTree nodes={nodes} value="app" label="Project files" />Custom markup
<rui-tree> coordinates any light-DOM tree that matches its query contract. The RuiTree helper stamps these targets; it is not required.
import '@ecopages/radiant-ui/tree';
<rui-tree value="app" label="Project files">
<ul role="tree" aria-label="Project files" aria-multiselectable="false">
<li>
<button type="button" role="treeitem" data-value="src" aria-expanded="true">
src
</button>
<ul role="group">
<li>
<button type="button" role="treeitem" data-value="app">
app.ts
</button>
</li>
</ul>
</li>
</ul>
</rui-tree>;Node selection
Bind value to highlight the active node. Use keyboard arrows to navigate and expand branches.
Theming
Tree maps to semantic surface and status roles, never Tailwind palette steps:
| Part | CSS roles |
|---|---|
Surface (rui-tree) | background, on-background, border, radius-container, shadow-sm |
| Branch guides | border |
| Node hover | surface |
| Selected node | primary-container, on-primary-container |
| Node focus | surface, focus-ring |
Override at the theme layer, not in component CSS.
API
RuiTree is a custom element (<rui-tree>) that owns selection, expansion, and roving-tabindex navigation; the RuiTree JSX view authors tree items from a nodes structure.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
label | string | '' | Accessible name for the tree. |
value | string | '' | Selected item's data-value / id. |
Light-DOM contract
| Target | Required | Host writes | Author owns |
|---|---|---|---|
[role="tree"] | yes | — | root list; view sets aria-label, aria-multiselectable |
[role="treeitem"] | yes | aria-selected, roving tabIndex | data-value, aria-expanded (branch nodes) |
data-value | per item | — | selection identity; fallback id or text |
[role="group"] | for branches | hidden | sibling after expandable treeitem |
Do not set aria-selected or tabIndex on treeitems. Nested hosts: none.
Events
| Event | Detail | Description |
|---|---|---|
rui-change | { value: string } | Emitted when a tree item is selected. |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
nodes | RuiTreeNode[] | Tree structure for the RuiTree view. |
RuiTreeNode is { id, label, children?, expanded? }.
View helpers
| Component | Target stamped | Notes |
|---|---|---|
RuiTree | [role="tree"] with data-ref="root" | Accepts nodes or children. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-tree | Root list (role="tree"). |
.rui-tree__item | List item wrapping a node. |
.rui-tree__node | Node button (role="treeitem"). |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Surface | --background, --on-background, --border, --radius-container, --shadow-control |
| Branch guides | --border |
| Node hover | --surface |
| Selected node | --primary-container, --on-primary-container |
| Node focus | --surface, --focus-ring |
Accessibility
- Tree exposes
role="tree"withrole="treeitem"children. - Expand/collapse state is communicated with
aria-expanded. - Provide
labelso users know what hierarchy the tree represents.