Hierarchical expandable list.
project/
<div class="tree-view tree-view--lines" style="--accent:#D9C8F0;--indent:18px;">
<p class="tree-view__root">project/</p>
<ul class="tree-view__list" role="tree">
<li class="tree-view__node" role="treeitem" aria-expanded="true">
<details class="tree-view__folder" open>
<summary>src/</summary>
<ul class="tree-view__sublist" role="group">
<li role="treeitem">index.js</li>
<li role="treeitem">app.css</li>
</ul>
</details>
</li>
<li class="tree-view__node" role="treeitem" aria-expanded="true">
<details class="tree-view__folder" open>
<summary>docs/</summary>
<ul class="tree-view__sublist" role="group">
<li role="treeitem">README.md</li>
</ul>
</details>
</li>
<li class="tree-view__node tree-view__node--file" role="treeitem">package.json</li>
</ul>
</div>
<style>
.tree-view { font: 15px/1.5 system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 16px; border-radius: 10px; box-sizing: border-box; }
.tree-view__root { margin: 0 0 8px; font-weight: 700; font-size: 14px; }
.tree-view__list, .tree-view__sublist { list-style: none; margin: 0; padding: 0; }
.tree-view__sublist { padding-left: var(--indent); }
.tree-view__node { position: relative; padding: 3px 0; }
.tree-view--lines .tree-view__sublist { border-left: 1.5px solid #24143022; margin-left: 6px; }
.tree-view--lines .tree-view__sublist li { position: relative; padding-left: 14px; }
.tree-view--lines .tree-view__sublist li::before { content: ""; position: absolute; left: 0; top: 10px; width: 10px; height: 1.5px; background: #24143022; }
.tree-view__folder > summary { cursor: pointer; list-style: none; font-weight: 600; display: flex; align-items: center; gap: 6px; }
.tree-view__folder > summary::-webkit-details-marker { display: none; }
.tree-view__folder > summary::before { content: "📁"; font-size: 12px; }
.tree-view__folder > summary::after { content: "▸"; margin-left: 4px; color: var(--accent); }
.tree-view__folder[open] > summary::after { transform: rotate(90deg); display: inline-block; }
.tree-view__node--file { padding-left: 20px; }
.tree-view__node--file::before { content: "📄"; font-size: 12px; margin-right: 6px; }
</style><div class="tree-view-tw tree-view-tw--lines font-sans text-[15px] text-[#241430] bg-[#FAF7F2] p-4 rounded-[10px] box-border" style="--accent:#D9C8F0;--indent:18px;">
<p class="m-0 mb-2 font-bold text-sm">project/</p>
<ul class="tree-view-tw__list list-none m-0 p-0" role="tree">
<li class="py-0.5" role="treeitem" aria-expanded="true">
<details class="tree-view-tw__folder" open>
<summary class="cursor-pointer list-none font-semibold flex items-center gap-1.5 marker:hidden">
<span aria-hidden="true">📁</span>
src/
<span aria-hidden="true" class="ml-1" style="color:#D9C8F0">▸</span>
</summary>
<ul class="tree-view-tw__sublist list-none m-0" role="group" style="padding-left:18px;">
<li role="treeitem" class="py-0.5">index.js</li>
<li role="treeitem" class="py-0.5">app.css</li>
</ul>
</details>
</li>
<li class="py-0.5" role="treeitem" aria-expanded="true">
<details class="tree-view-tw__folder" open>
<summary class="cursor-pointer list-none font-semibold flex items-center gap-1.5 marker:hidden">
<span aria-hidden="true">📁</span>
docs/
<span aria-hidden="true" class="ml-1" style="color:#D9C8F0">▸</span>
</summary>
<ul class="tree-view-tw__sublist list-none m-0" role="group" style="padding-left:18px;">
<li role="treeitem" class="py-0.5">README.md</li>
</ul>
</details>
</li>
<li class="py-0.5 pl-5" role="treeitem">📄 package.json</li>
</ul>
</div>
<style>
.tree-view-tw--lines .tree-view-tw__sublist { border-left: 1.5px solid #24143022; margin-left: 6px; }
.tree-view-tw--lines .tree-view-tw__sublist li { position: relative; padding-left: 14px; }
.tree-view-tw--lines .tree-view-tw__sublist li::before { content: ""; position: absolute; left: 0; top: 10px; width: 10px; height: 1.5px; background: #24143022; }
.tree-view-tw__folder[open] > summary span:nth-child(3) { transform: rotate(90deg); display: inline-block; }
</style>export function TreeView({
rootLabel = "project/",
accent = "#D9C8F0",
variant = "lines",
openAll = true,
indent = 18,
}) {
const lines = variant === "lines";
const sublistStyle = { listStyle: "none", margin: 0, paddingLeft: indent,
borderLeft: lines ? "1.5px solid #24143022" : "none", marginLeft: lines ? 6 : 0 };
return (
<div style={{ fontFamily: "system-ui, sans-serif", fontSize: 15, lineHeight: 1.5, color: "#241430",
background: "#FAF7F2", padding: 16, borderRadius: 10, boxSizing: "border-box" }}>
<p style={{ margin: "0 0 8px", fontWeight: 700, fontSize: 14 }}>{rootLabel}</p>
<ul role="tree" style={{ listStyle: "none", margin: 0, padding: 0 }}>
<li role="treeitem" aria-expanded={openAll} style={{ padding: "3px 0" }}>
<details open={openAll}>
<summary style={{ cursor: "pointer", listStyle: "none", fontWeight: 600, display: "flex", alignItems: "center", gap: 6 }}>
<span aria-hidden="true">📁</span> src/
<span aria-hidden="true" style={{ marginLeft: 4, color: accent }}>▸</span>
</summary>
<ul role="group" style={sublistStyle}>
<li role="treeitem" style={{ padding: "3px 0 3px 14px" }}>index.js</li>
<li role="treeitem" style={{ padding: "3px 0 3px 14px" }}>app.css</li>
</ul>
</details>
</li>
<li role="treeitem" aria-expanded={openAll} style={{ padding: "3px 0" }}>
<details open={openAll}>
<summary style={{ cursor: "pointer", listStyle: "none", fontWeight: 600, display: "flex", alignItems: "center", gap: 6 }}>
<span aria-hidden="true">📁</span> docs/
<span aria-hidden="true" style={{ marginLeft: 4, color: accent }}>▸</span>
</summary>
<ul role="group" style={sublistStyle}>
<li role="treeitem" style={{ padding: "3px 0 3px 14px" }}>README.md</li>
</ul>
</details>
</li>
<li role="treeitem" style={{ padding: "3px 0 3px 20px" }}>📄 package.json</li>
</ul>
</div>
);
}