# Tree
A built-in tag that renders a nested file/folder explorer: collapsible folders with
indentation guide lines and language-aware icons, full keyboard navigation, and
per-row `defaultOpen`, `href`, `comment`, and `highlighted` options. Give a
`{% file %}` a body of source code and the file becomes
clickable, opening that code centered in a modal as a standard fenced code block (so
the site's syntax highlighting styles it). A file with an empty body is a plain,
non-clickable leaf.
Use it as `{% tree %}` in Markdown, or call it from Python logic
(loops, snippets) via `component('aardvark', 'tree', …)`. It is a compound block: the
`{% tree %}` wrapper nests
`{% folder name="…" %}` and
`{% file name="…" %}` tags inside it.
## Default
Wrap the structure in `{% tree %} … {% endTree %}`, nest
`{% folder name="…" %} … {% endFolder %}` and
`{% file name="…" %} … {% endFile %}` inside it, and put a file's
source between its tags. Click a file with a body (**app.py**, **text.py**,
**package.json**) to open its source in a modal with a copy button; **README.md** has
no body, so it's a plain leaf. Folders collapse on click, and the whole tree is
keyboard-navigable (focus it, then ↑ ↓ to move, → ← to expand/collapse, **Enter** to
open a file).
**Preview**
Source: Markdown
```aardvark
{% tree label="Project files" %}
{% folder name="src" defaultOpen %}
{% file name="app.py" %}
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"status": "ok"}
{% endFile %}
{% folder name="utils" %}
{% file name="text.py" %}
def slugify(value: str) -> str:
return value.strip().lower().replace(" ", "-")
{% endFile %}
{% endFolder %}
{% endFolder %}
{% file name="package.json" %}
{
"name": "demo",
"scripts": { "build": "vark build" }
}
{% endFile %}
{% file name="README.md" comment="start here" %}{% endFile %}
{% endTree %}
```
Source: Python
```python
src = component(
'aardvark', 'folder', name='src', defaultOpen=True,
children=(
component('aardvark', 'file', name='app.py',
children='app = FastAPI()')
+ component(
'aardvark', 'folder', name='utils',
children=component('aardvark', 'file', name='text.py',
children='def slugify(value): ...'),
)
),
)
files = (
src
+ component('aardvark', 'file', name='package.json',
children='{ "name": "demo" }')
+ component('aardvark', 'file', name='README.md', comment='start here')
)
component('aardvark', 'tree', label='Project files', children=files)
```
## Links, comments, and highlights
An `href` turns a name into a link (a bodyless file with `href` is a plain link); a
`comment` adds a trailing note in muted monospace (auto-prefixed with `#`);
`highlighted` tints a row to draw the eye; and `openable=false` makes a folder static
(always open, no chevron).
**Preview**
Source: Markdown
```aardvark
{% tree %}
{% folder name="docs" defaultOpen comment="published to the site" %}
{% file name="index.md" href="/" %}{% endFile %}
{% file name="config.yaml" highlighted %}
title: My Site
theme:
accent: indigo
{% endFile %}
{% endFolder %}
{% folder name="dist" openable=false comment="build output" %}
{% file name="index.html" %}{% endFile %}
{% endFolder %}
{% endTree %}
```
Source: Python
```python
docs = component(
'aardvark', 'folder', name='docs', defaultOpen=True,
comment='published to the site',
children=(
component('aardvark', 'file', name='index.md', href='/')
+ component('aardvark', 'file', name='config.yaml', highlighted=True,
children='title: My Site')
),
)
dist = component(
'aardvark', 'folder', name='dist', openable=False, comment='build output',
children=component('aardvark', 'file', name='index.html'),
)
component('aardvark', 'tree', children=docs + dist)
```
## With other components
A tree slots into any block — for example as one panel of a
`{% tabs %}` set, paired with a prose tab that explains the
layout.
**Preview**
`content/` holds your Markdown; `aardvark.config.yaml` configures the build.
Source: Markdown
```aardvark
{% tabs defaultValue="layout" %}
{% tab label="Layout" %}
{% tree label="Site layout" %}
{% folder name="content" defaultOpen %}
{% file name="index.md" %}{% endFile %}
{% endFolder %}
{% file name="aardvark.config.yaml" comment="site config" %}{% endFile %}
{% endTree %}
{% endTab %}
{% tab label="Explanation" %}
`content/` holds your Markdown; `aardvark.config.yaml` configures the build.
{% endTab %}
{% endTabs %}
```
Source: Python
```python
tree = component(
'aardvark', 'tree', label='Site layout',
children=(
component('aardvark', 'folder', name='content', defaultOpen=True,
children=component('aardvark', 'file', name='index.md'))
+ component('aardvark', 'file', name='aardvark.config.yaml',
comment='site config')
),
)
tabs = (
component('aardvark', 'tab', label='Layout', children=tree)
+ component('aardvark', 'tab', label='Explanation',
children='`content/` holds your Markdown.')
)
component('aardvark', 'tabs', defaultValue='layout', children=tabs)
```
## Attributes
A file's body is captured verbatim and shown as a fenced code block — the template
engine does not execute inside it, so ordinary source code (even code that looks like
template syntax) is safe as-is. A balanced
`{% file %} … {% endFile %}` pair is fine too; only a lone,
unbalanced end-tag needs a `{% raw %}` block to keep the engine
from closing the block early.
### `{% tree %}`
| Attribute | Valid values | Description |
| --- | --- | --- |
| `label` | string | Accessible name for the tree (sets `aria-label`); use it when a page has more than one. |
### `{% folder %}`
| Attribute | Valid values | Description |
| --- | --- | --- |
| `name` | string (required) | Folder label. |
| `defaultOpen` | bool flag | Start expanded instead of collapsed. |
| `openable` | bool (default `true`); `openable=false` | `false` → static folder: always open, no chevron, no toggle. |
| `href` | URL | Link the folder name (shown underlined). |
| `comment` | string | Trailing muted-monospace note, auto-prefixed with `#`. |
| `highlighted` | bool flag | Tint the row with the accent color. |
### `{% file %}`
| Attribute | Valid values | Description |
| --- | --- | --- |
| `name` | string (required) | File name; sets the icon and the code language. |
| *(body)* | source code | Opens in a modal on click. An empty body is a plain leaf. |
| `lang` | a highlight language (e.g. `bash`, `python`) | Override the language guessed from the extension. |
| `href` | URL | Link a bodyless file (ignored when the file has a code body — that opens the modal). |
| `comment` | string | Trailing muted-monospace note, auto-prefixed with `#`. |
| `highlighted` | bool flag | Tint the row with the accent color. |
## CSS Selectors
The tree mounts inside an island wrapper carrying `data-aardvark-island="Tree"` and renders its own class names rather than Mantine's Styles API — target the container, each row, and the file/folder labels.
```css
[data-aardvark-island="Tree"] /* the island wrapper */
.aardvark-tree /* the tree container */
.aardvark-tree-row /* a single file/folder row */
.aardvark-tree-label /* a file or folder name */
.aardvark-tree-icon /* the leading file/folder icon */
```
## Injecting Attributes
`attr={…}` forwards raw HTML attributes — `id`, `data-*`, ARIA, analytics hooks — onto the
rendered tree root. (Style it through the CSS parts above; set per-row options like `href`,
`comment`, `highlighted`, or `lang` on each `{% file %}` /
`{% folder %}`.)
Source: Markdown
```aardvark
{% tree label="Project files" attr={'data-analytics': 'file-tree', 'aria-label': 'Project files'} %}
{% folder name="src" defaultOpen %}
{% file name="app.py" %}print("hello"){% endFile %}
{% endFolder %}
{% endTree %}
```
Source: Python
```python
files = component('aardvark', 'folder', name='src', defaultOpen=True,
children=component('aardvark', 'file', name='app.py', children='print("hello")'))
print(component('aardvark', 'tree', label='Project files',
attr={'data-analytics': 'file-tree', 'aria-label': 'Project files'}, children=files))
```