Open demo

CSS code

HTML
<div class="grid">
<div><span class="lbl">Component library, empty search</span>
<div class="sf" data-filter-tree>
  <label class="sf-label" for="sfa">Search components</label>
  <div class="sf-field"><svg class="sf-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="sf-input" id="sfa" type="search" placeholder="Try date or toast" autocomplete="off" spellcheck="false" data-filter-input></div>
  <p class="sf-status" role="status" data-filter-status data-noun="components">29 components</p>
  <ul class="sf-tree" data-filter-list aria-label="components">
    <li><details open><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Forms</span><span class="sf-count" aria-hidden="true">9</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#button"><span class="sf-name" data-label>Button</span></a></li>
        <li><a class="sf-leaf" href="#checkbox"><span class="sf-name" data-label>Checkbox</span></a></li>
        <li><a class="sf-leaf" href="#date-picker"><span class="sf-name" data-label>Date picker</span></a></li>
        <li><a class="sf-leaf" href="#input"><span class="sf-name" data-label>Input</span></a></li>
        <li><a class="sf-leaf" href="#radio-group"><span class="sf-name" data-label>Radio group</span></a></li>
        <li><a class="sf-leaf" href="#select"><span class="sf-name" data-label>Select</span></a></li>
        <li><a class="sf-leaf" href="#slider"><span class="sf-name" data-label>Slider</span></a></li>
        <li><a class="sf-leaf" href="#switch"><span class="sf-name" data-label>Switch</span></a></li>
        <li><a class="sf-leaf" href="#text-area"><span class="sf-name" data-label>Text area</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Overlays</span><span class="sf-count" aria-hidden="true">5</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#dialog"><span class="sf-name" data-label>Dialog</span></a></li>
        <li><a class="sf-leaf" href="#drawer"><span class="sf-name" data-label>Drawer</span></a></li>
        <li><a class="sf-leaf" href="#popover"><span class="sf-name" data-label>Popover</span></a></li>
        <li><a class="sf-leaf" href="#toast"><span class="sf-name" data-label>Toast</span></a></li>
        <li><a class="sf-leaf" href="#tooltip"><span class="sf-name" data-label>Tooltip</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Navigation</span><span class="sf-count" aria-hidden="true">5</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#breadcrumb"><span class="sf-name" data-label>Breadcrumb</span></a></li>
        <li><a class="sf-leaf" href="#menu"><span class="sf-name" data-label>Menu</span></a></li>
        <li><a class="sf-leaf" href="#pagination"><span class="sf-name" data-label>Pagination</span></a></li>
        <li><a class="sf-leaf" href="#tabs"><span class="sf-name" data-label>Tabs</span></a></li>
        <li><a class="sf-leaf" href="#tree-view"><span class="sf-name" data-label>Tree view</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Data display</span><span class="sf-count" aria-hidden="true">6</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#avatar"><span class="sf-name" data-label>Avatar</span></a></li>
        <li><a class="sf-leaf" href="#badge"><span class="sf-name" data-label>Badge</span></a></li>
        <li><a class="sf-leaf" href="#card"><span class="sf-name" data-label>Card</span></a></li>
        <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Table</span><span class="sf-count" aria-hidden="true">2</span></summary>
          <ul class="sf-sub">
            <li><a class="sf-leaf" href="#sortable-table"><span class="sf-name" data-label>Sortable table</span></a></li>
            <li><a class="sf-leaf" href="#data-grid"><span class="sf-name" data-label>Data grid</span></a></li>
          </ul>
        </details></li>
        <li><a class="sf-leaf" href="#timeline"><span class="sf-name" data-label>Timeline</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Feedback</span><span class="sf-count" aria-hidden="true">4</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#alert"><span class="sf-name" data-label>Alert</span></a></li>
        <li><a class="sf-leaf" href="#progress-bar"><span class="sf-name" data-label>Progress bar</span></a></li>
        <li><a class="sf-leaf" href="#skeleton"><span class="sf-name" data-label>Skeleton</span></a></li>
        <li><a class="sf-leaf" href="#spinner"><span class="sf-name" data-label>Spinner</span></a></li>
      </ul>
    </details></li>
  </ul>
</div>
</div>
<div><span class="lbl">Staff directory, filtered</span>
<div class="sf" data-filter-tree>
  <label class="sf-label" for="sfb">Search people</label>
  <div class="sf-field"><svg class="sf-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="sf-input" id="sfb" type="search" placeholder="Name" autocomplete="off" spellcheck="false" value="ma" data-filter-input></div>
  <p class="sf-status" role="status" data-filter-status data-noun="people">10 people</p>
  <ul class="sf-tree" data-filter-list aria-label="people">
    <li><details open><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Engineering</span><span class="sf-count" aria-hidden="true">5</span></summary>
      <ul class="sf-sub">
        <li><details open><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Platform</span><span class="sf-count" aria-hidden="true">3</span></summary>
          <ul class="sf-sub">
            <li><a class="sf-leaf" href="#lena-fischer"><span class="sf-name" data-label>Lena Fischer</span><span class="sf-meta">Staff Engineer</span></a></li>
            <li><a class="sf-leaf" href="#tomasz-wolny"><span class="sf-name" data-label>Tomasz Wolny</span><span class="sf-meta">Site Reliability</span></a></li>
            <li><a class="sf-leaf" href="#ama-mensah"><span class="sf-name" data-label>Ama Mensah</span><span class="sf-meta">Backend Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Mobile</span><span class="sf-count" aria-hidden="true">2</span></summary>
          <ul class="sf-sub">
            <li><a class="sf-leaf" href="#sara-lindqvist"><span class="sf-name" data-label>Sara Lindqvist</span><span class="sf-meta">iOS Engineer</span></a></li>
            <li><a class="sf-leaf" href="#mateo-ruiz"><span class="sf-name" data-label>Mateo Ruiz</span><span class="sf-meta">Android Engineer</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Sales</span><span class="sf-count" aria-hidden="true">3</span></summary>
      <ul class="sf-sub">
        <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Enterprise</span><span class="sf-count" aria-hidden="true">2</span></summary>
          <ul class="sf-sub">
            <li><a class="sf-leaf" href="#amara-osei"><span class="sf-name" data-label>Amara Osei</span><span class="sf-meta">Account Executive</span></a></li>
            <li><a class="sf-leaf" href="#jonas-weber"><span class="sf-name" data-label>Jonas Weber</span><span class="sf-meta">Solutions Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Partnerships</span><span class="sf-count" aria-hidden="true">1</span></summary>
          <ul class="sf-sub">
            <li><a class="sf-leaf" href="#marta-silva"><span class="sf-name" data-label>Marta Silva</span><span class="sf-meta">Partner Manager</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>People</span><span class="sf-count" aria-hidden="true">2</span></summary>
      <ul class="sf-sub">
        <li><a class="sf-leaf" href="#hannah-kim"><span class="sf-name" data-label>Hannah Kim</span><span class="sf-meta">People Partner</span></a></li>
        <li><a class="sf-leaf" href="#omar-farouk"><span class="sf-name" data-label>Omar Farouk</span><span class="sf-meta">Recruiter</span></a></li>
      </ul>
    </details></li>
  </ul>
</div>
</div>
</div>
CSS
/* Filterable tree: a search field above nested <details> branches. Matches get <mark>, their ancestors open. */
.sf{--sf-accent:#0369a1;--sf-text:#1f2937;--sf-muted:#5b6577;--sf-line:#e3e7ed;--sf-hover:#f0f6fb;--sf-mark:#fde68a}
.sf-icon{width:18px;height:18px;top:50%;left:12px;transform:translateY(-50%);color:var(--sf-muted);pointer-events:none}
.sf-input::placeholder{color:#6b7280}
.sf-tree mark{background:var(--sf-mark);color:inherit;border-radius:3px;padding:0 1px}
.sf summary{list-style:none}
.sf summary::-webkit-details-marker{display:none}
.sf-sum{cursor:pointer;min-height:32px;padding:0 6px}
.sf-sum:hover,.sf-leaf:hover{background:var(--sf-hover)}
.sf-sum:focus-visible,.sf-leaf:focus-visible{outline:2px solid var(--sf-accent);outline-offset:1px}
.sf-chev{width:14px;height:14px;flex:none;color:var(--sf-muted);transition:transform .15s ease}
.sf details[open]>summary .sf-chev{transform:rotate(90deg)}
.sf-sub{margin-left:13px!important;padding-left:8px!important;border-left:1px solid var(--sf-line)}
.sf-leaf{min-height:30px;padding:5px 8px;color:#334155}
.sf-leaf:hover{color:#0c4a6e}
.sf-meta{font-size:.78rem}
@media (prefers-reduced-motion:reduce){.sf-chev{transition:none}}
.sf{background:#fff;border:1px solid #e2e7ee;border-radius:14px;padding:20px;box-shadow:0 10px 30px -22px rgba(15,23,42,.3)}
.sf-label{display:block;margin-bottom:8px;font-size:.85rem;font-weight:600}
.sf-field{position:relative}
.sf-icon{position:absolute}
.sf-input{display:block;width:100%;padding:10px 12px 10px 40px;border:1px solid #8a94a6;border-radius:10px;background:#fff;font:inherit;font-size:.92rem;color:var(--sf-text);-webkit-appearance:none;appearance:none}
.sf-input:focus{outline:3px solid #bae0f7;outline-offset:0;border-color:var(--sf-accent)}
.sf-status{min-height:1.2em;margin:10px 0 8px;font-size:.8rem;color:var(--sf-muted)}
.sf-tree,.sf-sub{margin:0;padding:0;list-style:none}
.sf-tree{font-size:.9rem;color:var(--sf-text)}
.sf-sum{display:flex;align-items:center;gap:8px;border-radius:6px;font-weight:600}
.sf-count{margin-left:auto;padding:1px 8px;border-radius:99px;background:#eef2f6;color:#475569;font-size:.72rem;font-weight:600;font-variant-numeric:tabular-nums}
.sf-leaf{display:flex;align-items:baseline;gap:8px;border-radius:6px;text-decoration:none}
.sf-meta{margin-left:auto;color:var(--sf-muted)}
JavaScript
/* Filterable tree: typing hides branches without a match, opens the ancestors of every match, wraps the
   matching text in <mark> and announces the result count through a polite role="status" line. */
document.querySelectorAll('[data-filter-tree]').forEach(box => {
  const input = box.querySelector('[data-filter-input]');
  const status = box.querySelector('[data-filter-status]');
  const list = box.querySelector('[data-filter-list]');
  const noun = status.dataset.noun;
  const labels = [...list.querySelectorAll('[data-label]')];
  labels.forEach(l => { l.dataset.text = l.textContent; });
  const total = [...list.querySelectorAll('li')].filter(li => !li.querySelector(':scope > details')).length;
  let saved = null, timer;
  const mark = (el, q) => {
    const t = el.dataset.text, at = q ? t.toLowerCase().indexOf(q) : -1;
    if (at < 0) { el.textContent = t; return false; }
    const m = document.createElement('mark'); m.textContent = t.slice(at, at + q.length);
    el.replaceChildren(t.slice(0, at), m, t.slice(at + q.length)); return true;
  };
  const walk = (li, q, inHit) => {
    const hit = mark(li.querySelector('[data-label]'), q), d = li.querySelector(':scope > details');
    let found = hit ? 1 : 0, below = false;
    if (d) for (const kid of d.querySelectorAll(':scope > ul > li')) { const n = walk(kid, q, inHit || hit); found += n; below = below || n > 0; }
    li.hidden = !!q && !hit && !below && !inHit;
    if (d && q) d.open = below;
    return found;
  };
  const run = () => {
    const q = input.value.trim().toLowerCase(), details = [...list.querySelectorAll('details')];
    if (q && !saved) saved = details.map(d => d.open);
    let found = 0;
    for (const li of list.querySelectorAll(':scope > li')) found += walk(li, q, false);
    if (!q && saved) { details.forEach((d, i) => { d.open = saved[i]; }); saved = null; }
    clearTimeout(timer);
    timer = setTimeout(() => {
      status.textContent = !q ? total + ' ' + noun
        : found ? found + (found === 1 ? ' match' : ' matches') + ' for \u201c' + input.value.trim() + '\u201d'
        : 'No ' + noun + ' match \u201c' + input.value.trim() + '\u201d';
    }, 250);
  };
  input.addEventListener('input', run);
  input.addEventListener('keydown', e => { if (e.key === 'Escape' && input.value) { e.preventDefault(); input.value = ''; run(); } });
  if (input.value) run();
});

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="grid">
<div><span class="lbl">Component library, empty search</span>
<div class="sf card rounded-4" data-filter-tree><div class="card-body">
  <label class="sf-label form-label fw-semibold" for="sfa">Search components</label>
  <div class="sf-field position-relative"><svg class="sf-icon position-absolute" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="sf-input form-control" id="sfa" type="search" placeholder="Try date or toast" autocomplete="off" spellcheck="false" data-filter-input></div>
  <p class="sf-status text-body-secondary" role="status" data-filter-status data-noun="components">29 components</p>
  <ul class="sf-tree list-unstyled mb-0" data-filter-list aria-label="components">
    <li><details open><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Forms</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">9</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#button"><span class="sf-name" data-label>Button</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#checkbox"><span class="sf-name" data-label>Checkbox</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#date-picker"><span class="sf-name" data-label>Date picker</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#input"><span class="sf-name" data-label>Input</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#radio-group"><span class="sf-name" data-label>Radio group</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#select"><span class="sf-name" data-label>Select</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#slider"><span class="sf-name" data-label>Slider</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#switch"><span class="sf-name" data-label>Switch</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#text-area"><span class="sf-name" data-label>Text area</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Overlays</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">5</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#dialog"><span class="sf-name" data-label>Dialog</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#drawer"><span class="sf-name" data-label>Drawer</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#popover"><span class="sf-name" data-label>Popover</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#toast"><span class="sf-name" data-label>Toast</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#tooltip"><span class="sf-name" data-label>Tooltip</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Navigation</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">5</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#breadcrumb"><span class="sf-name" data-label>Breadcrumb</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#menu"><span class="sf-name" data-label>Menu</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#pagination"><span class="sf-name" data-label>Pagination</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#tabs"><span class="sf-name" data-label>Tabs</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#tree-view"><span class="sf-name" data-label>Tree view</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Data display</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">6</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#avatar"><span class="sf-name" data-label>Avatar</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#badge"><span class="sf-name" data-label>Badge</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#card"><span class="sf-name" data-label>Card</span></a></li>
        <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Table</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">2</span></summary>
          <ul class="sf-sub list-unstyled mb-0">
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#sortable-table"><span class="sf-name" data-label>Sortable table</span></a></li>
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#data-grid"><span class="sf-name" data-label>Data grid</span></a></li>
          </ul>
        </details></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#timeline"><span class="sf-name" data-label>Timeline</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Feedback</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">4</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#alert"><span class="sf-name" data-label>Alert</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#progress-bar"><span class="sf-name" data-label>Progress bar</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#skeleton"><span class="sf-name" data-label>Skeleton</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#spinner"><span class="sf-name" data-label>Spinner</span></a></li>
      </ul>
    </details></li>
  </ul>
</div></div>
</div>
<div><span class="lbl">Staff directory, filtered</span>
<div class="sf card rounded-4" data-filter-tree><div class="card-body">
  <label class="sf-label form-label fw-semibold" for="sfb">Search people</label>
  <div class="sf-field position-relative"><svg class="sf-icon position-absolute" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="sf-input form-control" id="sfb" type="search" placeholder="Name" autocomplete="off" spellcheck="false" value="ma" data-filter-input></div>
  <p class="sf-status text-body-secondary" role="status" data-filter-status data-noun="people">10 people</p>
  <ul class="sf-tree list-unstyled mb-0" data-filter-list aria-label="people">
    <li><details open><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Engineering</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">5</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><details open><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Platform</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">3</span></summary>
          <ul class="sf-sub list-unstyled mb-0">
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#lena-fischer"><span class="sf-name" data-label>Lena Fischer</span><span class="sf-meta text-body-secondary ms-auto">Staff Engineer</span></a></li>
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#tomasz-wolny"><span class="sf-name" data-label>Tomasz Wolny</span><span class="sf-meta text-body-secondary ms-auto">Site Reliability</span></a></li>
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#ama-mensah"><span class="sf-name" data-label>Ama Mensah</span><span class="sf-meta text-body-secondary ms-auto">Backend Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Mobile</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">2</span></summary>
          <ul class="sf-sub list-unstyled mb-0">
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#sara-lindqvist"><span class="sf-name" data-label>Sara Lindqvist</span><span class="sf-meta text-body-secondary ms-auto">iOS Engineer</span></a></li>
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#mateo-ruiz"><span class="sf-name" data-label>Mateo Ruiz</span><span class="sf-meta text-body-secondary ms-auto">Android Engineer</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Sales</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">3</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Enterprise</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">2</span></summary>
          <ul class="sf-sub list-unstyled mb-0">
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#amara-osei"><span class="sf-name" data-label>Amara Osei</span><span class="sf-meta text-body-secondary ms-auto">Account Executive</span></a></li>
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#jonas-weber"><span class="sf-name" data-label>Jonas Weber</span><span class="sf-meta text-body-secondary ms-auto">Solutions Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Partnerships</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">1</span></summary>
          <ul class="sf-sub list-unstyled mb-0">
            <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#marta-silva"><span class="sf-name" data-label>Marta Silva</span><span class="sf-meta text-body-secondary ms-auto">Partner Manager</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="sf-sum d-flex align-items-center gap-2 fw-semibold rounded-2"><svg class="sf-chev" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>People</span><span class="sf-count badge rounded-pill ms-auto" aria-hidden="true">2</span></summary>
      <ul class="sf-sub list-unstyled mb-0">
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#hannah-kim"><span class="sf-name" data-label>Hannah Kim</span><span class="sf-meta text-body-secondary ms-auto">People Partner</span></a></li>
        <li><a class="sf-leaf d-flex align-items-baseline gap-2 rounded-2 text-decoration-none" href="#omar-farouk"><span class="sf-name" data-label>Omar Farouk</span><span class="sf-meta text-body-secondary ms-auto">Recruiter</span></a></li>
      </ul>
    </details></li>
  </ul>
</div></div>
</div>
</div>
CSS
/* Filterable tree: a search field above nested <details> branches. Matches get <mark>, their ancestors open. */
.sf{--sf-accent:#0369a1;--sf-text:#1f2937;--sf-muted:#5b6577;--sf-line:#e3e7ed;--sf-hover:#f0f6fb;--sf-mark:#fde68a}
.sf-icon{width:18px;height:18px;top:50%;left:12px;transform:translateY(-50%);color:var(--sf-muted);pointer-events:none}
.sf-input::placeholder{color:#6b7280}
.sf-tree mark{background:var(--sf-mark);color:inherit;border-radius:3px;padding:0 1px}
.sf summary{list-style:none}
.sf summary::-webkit-details-marker{display:none}
.sf-sum{cursor:pointer;min-height:32px;padding:0 6px}
.sf-sum:hover,.sf-leaf:hover{background:var(--sf-hover)}
.sf-sum:focus-visible,.sf-leaf:focus-visible{outline:2px solid var(--sf-accent);outline-offset:1px}
.sf-chev{width:14px;height:14px;flex:none;color:var(--sf-muted);transition:transform .15s ease}
.sf details[open]>summary .sf-chev{transform:rotate(90deg)}
.sf-sub{margin-left:13px!important;padding-left:8px!important;border-left:1px solid var(--sf-line)}
.sf-leaf{min-height:30px;padding:5px 8px;color:#334155}
.sf-leaf:hover{color:#0c4a6e}
.sf-meta{font-size:.78rem}
@media (prefers-reduced-motion:reduce){.sf-chev{transition:none}}
.sf{--bs-card-border-color:#e2e7ee;--bs-card-spacer-y:20px;--bs-card-spacer-x:20px;box-shadow:0 10px 30px -22px rgba(15,23,42,.3)}
.sf-label{margin-bottom:8px;font-size:.85rem}
.sf-input{padding:10px 12px 10px 40px;border-color:#8a94a6;border-radius:10px;font-size:.92rem;line-height:normal}
.sf-input:focus{box-shadow:none;outline:3px solid #bae0f7;outline-offset:0;border-color:var(--sf-accent)}
.sf-status{min-height:1.2em;margin:10px 0 8px;font-size:.8rem}
.sf-tree{font-size:.9rem;color:var(--sf-text)}
.sf-count{--bs-badge-padding-x:8px;--bs-badge-padding-y:2px;--bs-badge-font-size:.72rem;--bs-badge-color:#475569;background:#eef2f6}
.sf-leaf{color:#334155}
JavaScript
/* Filterable tree: typing hides branches without a match, opens the ancestors of every match, wraps the
   matching text in <mark> and announces the result count through a polite role="status" line. */
document.querySelectorAll('[data-filter-tree]').forEach(box => {
  const input = box.querySelector('[data-filter-input]');
  const status = box.querySelector('[data-filter-status]');
  const list = box.querySelector('[data-filter-list]');
  const noun = status.dataset.noun;
  const labels = [...list.querySelectorAll('[data-label]')];
  labels.forEach(l => { l.dataset.text = l.textContent; });
  const total = [...list.querySelectorAll('li')].filter(li => !li.querySelector(':scope > details')).length;
  let saved = null, timer;
  const mark = (el, q) => {
    const t = el.dataset.text, at = q ? t.toLowerCase().indexOf(q) : -1;
    if (at < 0) { el.textContent = t; return false; }
    const m = document.createElement('mark'); m.textContent = t.slice(at, at + q.length);
    el.replaceChildren(t.slice(0, at), m, t.slice(at + q.length)); return true;
  };
  const walk = (li, q, inHit) => {
    const hit = mark(li.querySelector('[data-label]'), q), d = li.querySelector(':scope > details');
    let found = hit ? 1 : 0, below = false;
    if (d) for (const kid of d.querySelectorAll(':scope > ul > li')) { const n = walk(kid, q, inHit || hit); found += n; below = below || n > 0; }
    li.hidden = !!q && !hit && !below && !inHit;
    if (d && q) d.open = below;
    return found;
  };
  const run = () => {
    const q = input.value.trim().toLowerCase(), details = [...list.querySelectorAll('details')];
    if (q && !saved) saved = details.map(d => d.open);
    let found = 0;
    for (const li of list.querySelectorAll(':scope > li')) found += walk(li, q, false);
    if (!q && saved) { details.forEach((d, i) => { d.open = saved[i]; }); saved = null; }
    clearTimeout(timer);
    timer = setTimeout(() => {
      status.textContent = !q ? total + ' ' + noun
        : found ? found + (found === 1 ? ' match' : ' matches') + ' for \u201c' + input.value.trim() + '\u201d'
        : 'No ' + noun + ' match \u201c' + input.value.trim() + '\u201d';
    }, 250);
  };
  input.addEventListener('input', run);
  input.addEventListener('keydown', e => { if (e.key === 'Escape' && input.value) { e.preventDefault(); input.value = ''; run(); } });
  if (input.value) run();
});

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="grid grid-cols-[repeat(auto-fit,minmax(min(100%,300px),1fr))] items-start gap-7">
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5b6577] uppercase">Component library, empty search</span>
<div class="rounded-[14px] border border-[#e2e7ee] bg-white p-5 shadow-[0_10px_30px_-22px_rgba(15,23,42,.3)]" data-filter-tree>
  <label class="mb-2 block text-[.85rem] font-semibold text-[#1f2937]" for="sfa">Search components</label>
  <div class="relative"><svg class="pointer-events-none absolute top-1/2 left-3 size-[18px] -translate-y-1/2 text-[#5b6577]" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="block w-full appearance-none rounded-[10px] border border-[#8a94a6] bg-white py-2.5 pr-3 pl-10 text-[.92rem] text-[#1f2937] placeholder:text-[#6b7280] focus:border-[#0369a1] focus:outline-3 focus:outline-offset-0 focus:outline-[#bae0f7]" id="sfa" type="search" placeholder="Try date or toast" autocomplete="off" spellcheck="false" data-filter-input></div>
  <p class="m-0 mt-2.5 mb-2 min-h-[1.2em] text-[.8rem] text-[#5b6577]" role="status" data-filter-status data-noun="components">29 components</p>
  <ul class="m-0 list-none p-0 text-[.9rem] text-[#1f2937] [&_mark]:rounded-[3px] [&_mark]:bg-[#fde68a] [&_mark]:px-px [&_mark]:text-inherit" data-filter-list aria-label="components">
    <li><details open><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Forms</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">9</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#button"><span data-label>Button</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#checkbox"><span data-label>Checkbox</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#date-picker"><span data-label>Date picker</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#input"><span data-label>Input</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#radio-group"><span data-label>Radio group</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#select"><span data-label>Select</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#slider"><span data-label>Slider</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#switch"><span data-label>Switch</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#text-area"><span data-label>Text area</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Overlays</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">5</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#dialog"><span data-label>Dialog</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#drawer"><span data-label>Drawer</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#popover"><span data-label>Popover</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#toast"><span data-label>Toast</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#tooltip"><span data-label>Tooltip</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Navigation</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">5</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#breadcrumb"><span data-label>Breadcrumb</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#menu"><span data-label>Menu</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#pagination"><span data-label>Pagination</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#tabs"><span data-label>Tabs</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#tree-view"><span data-label>Tree view</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Data display</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">6</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#avatar"><span data-label>Avatar</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#badge"><span data-label>Badge</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#card"><span data-label>Card</span></a></li>
        <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Table</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">2</span></summary>
          <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#sortable-table"><span data-label>Sortable table</span></a></li>
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#data-grid"><span data-label>Data grid</span></a></li>
          </ul>
        </details></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#timeline"><span data-label>Timeline</span></a></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Feedback</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">4</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#alert"><span data-label>Alert</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#progress-bar"><span data-label>Progress bar</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#skeleton"><span data-label>Skeleton</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#spinner"><span data-label>Spinner</span></a></li>
      </ul>
    </details></li>
  </ul>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5b6577] uppercase">Staff directory, filtered</span>
<div class="rounded-[14px] border border-[#e2e7ee] bg-white p-5 shadow-[0_10px_30px_-22px_rgba(15,23,42,.3)]" data-filter-tree>
  <label class="mb-2 block text-[.85rem] font-semibold text-[#1f2937]" for="sfb">Search people</label>
  <div class="relative"><svg class="pointer-events-none absolute top-1/2 left-3 size-[18px] -translate-y-1/2 text-[#5b6577]" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><input class="block w-full appearance-none rounded-[10px] border border-[#8a94a6] bg-white py-2.5 pr-3 pl-10 text-[.92rem] text-[#1f2937] placeholder:text-[#6b7280] focus:border-[#0369a1] focus:outline-3 focus:outline-offset-0 focus:outline-[#bae0f7]" id="sfb" type="search" placeholder="Name" autocomplete="off" spellcheck="false" value="ma" data-filter-input></div>
  <p class="m-0 mt-2.5 mb-2 min-h-[1.2em] text-[.8rem] text-[#5b6577]" role="status" data-filter-status data-noun="people">10 people</p>
  <ul class="m-0 list-none p-0 text-[.9rem] text-[#1f2937] [&_mark]:rounded-[3px] [&_mark]:bg-[#fde68a] [&_mark]:px-px [&_mark]:text-inherit" data-filter-list aria-label="people">
    <li><details open><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Engineering</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">5</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><details open><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Platform</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">3</span></summary>
          <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#lena-fischer"><span data-label>Lena Fischer</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Staff Engineer</span></a></li>
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#tomasz-wolny"><span data-label>Tomasz Wolny</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Site Reliability</span></a></li>
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#ama-mensah"><span data-label>Ama Mensah</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Backend Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Mobile</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">2</span></summary>
          <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#sara-lindqvist"><span data-label>Sara Lindqvist</span><span class="ml-auto text-[.78rem] text-[#5b6577]">iOS Engineer</span></a></li>
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#mateo-ruiz"><span data-label>Mateo Ruiz</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Android Engineer</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Sales</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">3</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Enterprise</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">2</span></summary>
          <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#amara-osei"><span data-label>Amara Osei</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Account Executive</span></a></li>
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#jonas-weber"><span data-label>Jonas Weber</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Solutions Engineer</span></a></li>
          </ul>
        </details></li>
        <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>Partnerships</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">1</span></summary>
          <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
            <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#marta-silva"><span data-label>Marta Silva</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Partner Manager</span></a></li>
          </ul>
        </details></li>
      </ul>
    </details></li>
    <li><details><summary class="flex min-h-[32px] cursor-pointer list-none items-center gap-2 rounded-md px-1.5 font-semibold hover:bg-[#f0f6fb] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1] [&::-webkit-details-marker]:hidden"><svg class="size-3.5 flex-none text-[#5b6577] transition-transform duration-150 motion-reduce:transition-none [details[open]>summary>&]:rotate-90" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"/></svg><span data-label>People</span><span class="ml-auto rounded-full bg-[#eef2f6] px-2 py-px text-[.72rem] font-semibold text-[#475569] tabular-nums" aria-hidden="true">2</span></summary>
      <ul class="m-0 ml-[13px] list-none border-l border-[#e3e7ed] p-0 pl-2">
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#hannah-kim"><span data-label>Hannah Kim</span><span class="ml-auto text-[.78rem] text-[#5b6577]">People Partner</span></a></li>
        <li><a class="flex min-h-[30px] items-baseline gap-2 rounded-md px-2 py-[5px] text-[#334155] no-underline hover:bg-[#f0f6fb] hover:text-[#0c4a6e] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#0369a1]" href="#omar-farouk"><span data-label>Omar Farouk</span><span class="ml-auto text-[.78rem] text-[#5b6577]">Recruiter</span></a></li>
      </ul>
    </details></li>
  </ul>
</div>
</div>
</div>
JavaScript
/* Filterable tree: typing hides branches without a match, opens the ancestors of every match, wraps the
   matching text in <mark> and announces the result count through a polite role="status" line. */
document.querySelectorAll('[data-filter-tree]').forEach(box => {
  const input = box.querySelector('[data-filter-input]');
  const status = box.querySelector('[data-filter-status]');
  const list = box.querySelector('[data-filter-list]');
  const noun = status.dataset.noun;
  const labels = [...list.querySelectorAll('[data-label]')];
  labels.forEach(l => { l.dataset.text = l.textContent; });
  const total = [...list.querySelectorAll('li')].filter(li => !li.querySelector(':scope > details')).length;
  let saved = null, timer;
  const mark = (el, q) => {
    const t = el.dataset.text, at = q ? t.toLowerCase().indexOf(q) : -1;
    if (at < 0) { el.textContent = t; return false; }
    const m = document.createElement('mark'); m.textContent = t.slice(at, at + q.length);
    el.replaceChildren(t.slice(0, at), m, t.slice(at + q.length)); return true;
  };
  const walk = (li, q, inHit) => {
    const hit = mark(li.querySelector('[data-label]'), q), d = li.querySelector(':scope > details');
    let found = hit ? 1 : 0, below = false;
    if (d) for (const kid of d.querySelectorAll(':scope > ul > li')) { const n = walk(kid, q, inHit || hit); found += n; below = below || n > 0; }
    li.hidden = !!q && !hit && !below && !inHit;
    if (d && q) d.open = below;
    return found;
  };
  const run = () => {
    const q = input.value.trim().toLowerCase(), details = [...list.querySelectorAll('details')];
    if (q && !saved) saved = details.map(d => d.open);
    let found = 0;
    for (const li of list.querySelectorAll(':scope > li')) found += walk(li, q, false);
    if (!q && saved) { details.forEach((d, i) => { d.open = saved[i]; }); saved = null; }
    clearTimeout(timer);
    timer = setTimeout(() => {
      status.textContent = !q ? total + ' ' + noun
        : found ? found + (found === 1 ? ' match' : ' matches') + ' for \u201c' + input.value.trim() + '\u201d'
        : 'No ' + noun + ' match \u201c' + input.value.trim() + '\u201d';
    }, 250);
  };
  input.addEventListener('input', run);
  input.addEventListener('keydown', e => { if (e.key === 'Escape' && input.value) { e.preventDefault(); input.value = ''; run(); } });
  if (input.value) run();
});

About this set

Two trees with a search field that filters them as you type. Component library, empty search lists 29 components of a design system in five folded groups with a count on each group. Staff directory, filtered shows a company directory by department and team, with each person’s role, and loads with the query ma already applied so you can see the filtered state. A short script walks the tree on every keystroke. Any item whose name contains the query stays visible and gets the matching letters wrapped in a mark element; every branch above a match is opened and every branch without one is hidden. When a group name itself matches, its contents stay available underneath it. The number of matches is written to a role status line, after a short pause so screen readers are not interrupted on every letter. Escape or clearing the field brings back the tree exactly as it was, including which branches the reader had opened. Without JavaScript the tree still works as an ordinary collapsible list.

What’s included

  • Filters as you type, opens the ancestors of every match
  • Matching text highlighted with a mark element
  • Result count announced through a polite role status line
  • Escape or an empty field restores the previous open branches
  • Group counts and secondary text such as job titles

Accessibility

The search field has a visible label, and the result count is a role status region updated after a 250 millisecond pause. Branches are summary elements and leaves are links, so the tree works with Tab, Enter and Space. Inputs, summaries and links show a visible focus style, the mark highlight keeps the text colour at 4.5:1 or more, and the chevron rotation stops under prefers-reduced-motion.

Customise it

Change --sf-accent, --sf-mark, --sf-hover and --sf-line on .sf for focus, highlight, hover and guide colours. In Tailwind the highlight is set by the [&_mark]: utilities on the list, and in Bootstrap the field is a .form-control restyled in the snippet CSS.