Components / Layout & Navigation / Tree
Tree beta
Nested things, honestly indented — files, folders, and outlines that fold away.
- kata-ui
- components
- button
- button.css
- preview.html
- date-picker
- date-picker.js
- icons
- star.svg
- build.py
<ul class="k-tree" style="max-width:300px">
<li><div class="k-tree-row"><button class="tg" type="button" aria-expanded="true" aria-label="Toggle kata-ui"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><span class="fi fold"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></svg></span><span class="nm">kata-ui</span></div>
<ul>
<li><div class="k-tree-row"><button class="tg" type="button" aria-expanded="true" aria-label="Toggle components"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><span class="fi fold"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></svg></span><span class="nm">components</span></div>
<ul>
<li><div class="k-tree-row"><button class="tg" type="button" aria-expanded="false" aria-label="Toggle button"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><span class="fi fold"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></svg></span><span class="nm">button</span></div>
<ul hidden>
<li><div class="k-tree-row"><span class="tg"></span><span class="fi"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5"/></svg></span><span class="nm">button.css</span></div></li>
<li><div class="k-tree-row"><span class="tg"></span><span class="fi"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5"/></svg></span><span class="nm">preview.html</span></div></li>
</ul>
</li>
<li><div class="k-tree-row"><button class="tg" type="button" aria-expanded="false" aria-label="Toggle date-picker"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><span class="fi fold"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></svg></span><span class="nm">date-picker</span></div>
<ul hidden>
<li><div class="k-tree-row"><span class="tg"></span><span class="fi"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5"/></svg></span><span class="nm">date-picker.js</span></div></li>
</ul>
</li>
</ul>
</li>
<li><div class="k-tree-row"><button class="tg" type="button" aria-expanded="false" aria-label="Toggle icons"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button><span class="fi fold"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></svg></span><span class="nm">icons</span></div>
<ul hidden>
<li><div class="k-tree-row"><span class="tg"></span><span class="fi"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5"/></svg></span><span class="nm">star.svg</span></div></li>
</ul>
</li>
<li><div class="k-tree-row sel"><span class="tg"></span><span class="fi"><svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8l-5-5Z"/><path d="M14 3v5h5"/></svg></span><span class="nm">build.py</span></div></li>
</ul>
</li>
</ul>.k-tree{list-style:none;margin:0;padding:0;font-family:var(--k-font);font-size:13.5px;color:var(--k-ink);text-align:left}
.k-tree ul{list-style:none;margin:0;padding-left:19px;border-left:1px solid var(--k-line);margin-left:8px}
.k-tree-row{display:flex;align-items:center;gap:7px;padding:4.5px 7px;border-radius:8px}
.k-tree-row:hover{background:var(--k-bg-soft)}
.k-tree-row .tg{width:20px;height:20px;border:none;background:transparent;color:var(--k-ink-3);display:grid;place-items:center;cursor:pointer;border-radius:5px;padding:0;flex:none}
.k-tree-row .tg svg{transition:transform .15s var(--k-ease)}
.k-tree-row .tg[aria-expanded="false"] svg{transform:rotate(-90deg)}
.k-tree-row .fi{color:var(--k-ink-3);display:grid;place-items:center;flex:none}
.k-tree-row .fi.fold{color:var(--k-accent)}
.k-tree-row .nm{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.k-tree-row.sel{background:var(--k-accent-bg);color:var(--k-accent-ink);font-weight:550}
document.querySelectorAll('.k-tree .tg').forEach(btn => {
btn.addEventListener('click', () => {
const open = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!open));
const ul = btn.closest('li').querySelector(':scope > ul');
if (ul) ul.hidden = open;
});
});
Usage
For file structures and nested navigation. Depth beyond four levels is a smell; flatten or split before the indent eats the label.
Accessibility
Real nested lists with aria-expanded on the toggles, so the hierarchy is announced as a hierarchy. Files and folders are text, not links, unless they navigate.
Related parts
Accordion beta
Disclosure built on native <details>. Zero JavaScript, keyboard and find-in-page included.
Breadcrumbs beta
Where you are, as a path. The last crumb is the page itself — not a link.
Pagination beta
Page numbers with a clear current page: ink-filled, everything else quiet.
Scroll Area beta
A long list in a fixed frame, with a scrollbar that matches the room and a fade that admits there is more.