Open demo

CSS code

HTML
<div class="grid">
<div><span class="lbl">Animated open with ::details-content</span>
<div class="mx-card">
<p class="mx-head">Recipe box</p>
<p class="mx-note">Branches grow open with interpolate-size; older browsers open them instantly.</p>
<ul class="mx mx-anim">
  <li><details><summary>Breakfast</summary>
    <ul>
      <li><span class="mx-leaf">Shakshuka</span></li>
      <li><span class="mx-leaf">Overnight oats</span></li>
      <li><span class="mx-leaf">Buttermilk pancakes</span></li>
    </ul>
  </details></li>
  <li><details open><summary>Mains</summary>
    <ul>
      <li><details open><summary>Pasta</summary>
        <ul>
          <li><span class="mx-leaf">Cacio e pepe</span></li>
          <li><span class="mx-leaf">Lemon orzo</span></li>
        </ul>
      </details></li>
      <li><details><summary>Curries</summary>
        <ul>
          <li><span class="mx-leaf">Chickpea curry</span></li>
          <li><span class="mx-leaf">Thai green curry</span></li>
        </ul>
      </details></li>
      <li><span class="mx-leaf">Roast chicken</span></li>
    </ul>
  </details></li>
  <li><details><summary>Desserts</summary>
    <ul>
      <li><span class="mx-leaf">Olive oil cake</span></li>
      <li><span class="mx-leaf">Pear tart</span></li>
    </ul>
  </details></li>
</ul>
</div>
</div>
<div><span class="lbl">Open branches and current trail with :has()</span>
<div class="mx-card">
<p class="mx-head">Data Visualisation 101</p>
<p class="mx-note">Open modules get a tinted panel; the module holding the current lesson is marked, with no classes.</p>
<ul class="mx mx-anim mx-trail">
  <li><details><summary>1 Foundations</summary>
    <ul>
      <li><a class="mx-leaf" href="#1-1-why-charts-mislead">1.1 Why charts mislead</a></li>
      <li><a class="mx-leaf" href="#1-2-choosing-a-chart">1.2 Choosing a chart</a></li>
    </ul>
  </details></li>
  <li><details open><summary>2 Colour</summary>
    <ul>
      <li><a class="mx-leaf" href="#2-1-palettes">2.1 Palettes</a></li>
      <li><a class="mx-leaf" href="#2-2-contrast-and-accessibility" aria-current="page">2.2 Contrast and accessibility</a></li>
      <li><a class="mx-leaf" href="#2-3-dark-mode">2.3 Dark mode</a></li>
    </ul>
  </details></li>
  <li><details open><summary>3 Layout</summary>
    <ul>
      <li><a class="mx-leaf" href="#3-1-grids">3.1 Grids</a></li>
      <li><a class="mx-leaf" href="#3-2-small-multiples">3.2 Small multiples</a></li>
    </ul>
  </details></li>
  <li><details><summary>4 Storytelling</summary>
    <ul>
      <li><a class="mx-leaf" href="#4-1-annotation">4.1 Annotation</a></li>
      <li><a class="mx-leaf" href="#4-2-dashboards">4.2 Dashboards</a></li>
    </ul>
  </details></li>
</ul>
<p class="mx-hint">Every module is folded. Open one to see its lessons.</p>
</div>
</div>
<div><span class="lbl">One branch at a time with details name</span>
<div class="mx-card">
<p class="mx-head">Store settings</p>
<p class="mx-note">Each section shares one name attribute, so opening one closes the others.</p>
<ul class="mx mx-anim mx-one">
  <li><details name="store-settings"><summary>General</summary>
    <ul>
      <li><a class="mx-leaf" href="#store-name">Store name</a></li>
      <li><a class="mx-leaf" href="#time-zone">Time zone</a></li>
      <li><a class="mx-leaf" href="#currency">Currency</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings" open><summary>Shipping</summary>
    <ul>
      <li><a class="mx-leaf" href="#zones">Zones</a></li>
      <li><a class="mx-leaf" href="#rates">Rates</a></li>
      <li><a class="mx-leaf" href="#local-pickup">Local pickup</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary>Payments</summary>
    <ul>
      <li><a class="mx-leaf" href="#cards">Cards</a></li>
      <li><a class="mx-leaf" href="#bank-transfer">Bank transfer</a></li>
      <li><a class="mx-leaf" href="#payouts">Payouts</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary>Tax</summary>
    <ul>
      <li><a class="mx-leaf" href="#rates">Rates</a></li>
      <li><a class="mx-leaf" href="#exemptions">Exemptions</a></li>
    </ul>
  </details></li>
</ul>
</div>
</div>
</div>
CSS
/* Modern CSS tree: native CSS nesting, :has() for state styling, ::details-content + interpolate-size for the
   open animation (other browsers open instantly), and <details name> for one open branch at a time. */
.mx{
  --mx-accent:#047857;--mx-text:#1c1917;--mx-muted:#57534e;--mx-line:#d6d3cb;--mx-hover:#f2f7f4;--mx-panel:#f4faf7;
  font-size:.9rem;line-height:1.35;color:var(--mx-text);
  & ul{margin:0 0 0 14px;padding:2px 0 4px 12px;border-left:1.5px solid var(--mx-line)}
  & summary{display:flex;align-items:center;gap:10px;min-height:32px;font-weight:600;padding:0 8px;border-radius:8px;cursor:pointer;list-style:none;
    &::-webkit-details-marker{display:none}
    &::before{content:"";flex:none;width:7px;height:7px;margin:0 2px;border-right:2px solid var(--mx-muted);border-bottom:2px solid var(--mx-muted);
      transform:rotate(-45deg);transition:transform .2s ease}
    &:hover{background:var(--mx-hover)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
  }
  & details[open]>summary{color:var(--mx-accent);&::before{transform:rotate(45deg);border-color:var(--mx-accent)}}
  & .mx-leaf{display:block;padding:6px 8px;border-radius:8px;color:#44403c}
  & a.mx-leaf{text-decoration:none;
    &:hover{background:var(--mx-hover);color:var(--mx-text)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
    &[aria-current="page"]{background:var(--mx-accent);color:#fff;font-weight:600}
  }
}
/* :has() — an open branch gets a tinted panel; the branch holding aria-current="page" gets a marker */
.mx-trail{
  & li:has(>details[open]){background:var(--mx-panel);border-radius:10px;margin-bottom:4px}
  & ul{border-left-color:#b7d8c9}
  & summary:has(+ul [aria-current="page"])::after{content:"";width:8px;height:8px;margin-left:auto;border-radius:50%;background:var(--mx-accent);box-shadow:0 0 0 3px #cfe8dc}
}
.mx-hint{display:none}
.mx-trail:not(:has(details[open]))+.mx-hint{display:block;margin-top:10px}
/* one open branch: the top-level details share name="store-settings" */
.mx-one>li+li{border-top:1px solid #ece9e2}
.mx-one>li>details>summary{min-height:40px}
/* the animation: only where interpolate-size and ::details-content both exist */
@supports (interpolate-size:allow-keywords) and selector(::details-content){
  .mx-anim{interpolate-size:allow-keywords;
    & details::details-content{block-size:0;overflow:clip;opacity:0;
      transition:block-size .28s ease,opacity .28s ease,content-visibility .28s allow-discrete}
    & details[open]::details-content{block-size:auto;opacity:1}
  }
}
@media (prefers-reduced-motion:reduce){
  .mx summary::before,.mx-anim details::details-content{transition:none}
}
.mx,.mx ul{list-style:none}
.mx{margin:0;padding:0}
.mx-card{background:#fff;border:1px solid #e4e1d8;border-radius:16px;padding:22px 20px;box-shadow:0 12px 32px -24px rgba(41,37,36,.45)}
.mx-head{margin:0 0 4px;font-size:.95rem;font-weight:700;color:#1c1917}
.mx-note{margin:0 0 14px;font-size:.8rem;line-height:1.45;color:#57534e}
.mx-hint{margin:0;font-size:.8rem;color:#57534e}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="grid">
<div><span class="lbl">Animated open with ::details-content</span>
<div class="card mx-card border-0 rounded-4"><div class="card-body">
<p class="mx-head fw-bold mb-1">Recipe box</p>
<p class="mx-note text-body-secondary mb-3">Branches grow open with interpolate-size; older browsers open them instantly.</p>
<ul class="mx mx-anim list-unstyled mb-0">
  <li><details><summary class="fw-semibold">Breakfast</summary>
    <ul class="list-unstyled mb-0">
      <li><span class="mx-leaf text-decoration-none">Shakshuka</span></li>
      <li><span class="mx-leaf text-decoration-none">Overnight oats</span></li>
      <li><span class="mx-leaf text-decoration-none">Buttermilk pancakes</span></li>
    </ul>
  </details></li>
  <li><details open><summary class="fw-semibold">Mains</summary>
    <ul class="list-unstyled mb-0">
      <li><details open><summary class="fw-semibold">Pasta</summary>
        <ul class="list-unstyled mb-0">
          <li><span class="mx-leaf text-decoration-none">Cacio e pepe</span></li>
          <li><span class="mx-leaf text-decoration-none">Lemon orzo</span></li>
        </ul>
      </details></li>
      <li><details><summary class="fw-semibold">Curries</summary>
        <ul class="list-unstyled mb-0">
          <li><span class="mx-leaf text-decoration-none">Chickpea curry</span></li>
          <li><span class="mx-leaf text-decoration-none">Thai green curry</span></li>
        </ul>
      </details></li>
      <li><span class="mx-leaf text-decoration-none">Roast chicken</span></li>
    </ul>
  </details></li>
  <li><details><summary class="fw-semibold">Desserts</summary>
    <ul class="list-unstyled mb-0">
      <li><span class="mx-leaf text-decoration-none">Olive oil cake</span></li>
      <li><span class="mx-leaf text-decoration-none">Pear tart</span></li>
    </ul>
  </details></li>
</ul>
</div></div>
</div>
<div><span class="lbl">Open branches and current trail with :has()</span>
<div class="card mx-card border-0 rounded-4"><div class="card-body">
<p class="mx-head fw-bold mb-1">Data Visualisation 101</p>
<p class="mx-note text-body-secondary mb-3">Open modules get a tinted panel; the module holding the current lesson is marked, with no classes.</p>
<ul class="mx mx-anim mx-trail list-unstyled mb-0">
  <li><details><summary class="fw-semibold">1 Foundations</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#1-1-why-charts-mislead">1.1 Why charts mislead</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#1-2-choosing-a-chart">1.2 Choosing a chart</a></li>
    </ul>
  </details></li>
  <li><details open><summary class="fw-semibold">2 Colour</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#2-1-palettes">2.1 Palettes</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#2-2-contrast-and-accessibility" aria-current="page">2.2 Contrast and accessibility</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#2-3-dark-mode">2.3 Dark mode</a></li>
    </ul>
  </details></li>
  <li><details open><summary class="fw-semibold">3 Layout</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#3-1-grids">3.1 Grids</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#3-2-small-multiples">3.2 Small multiples</a></li>
    </ul>
  </details></li>
  <li><details><summary class="fw-semibold">4 Storytelling</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#4-1-annotation">4.1 Annotation</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#4-2-dashboards">4.2 Dashboards</a></li>
    </ul>
  </details></li>
</ul>
<p class="mx-hint text-body-secondary mb-0">Every module is folded. Open one to see its lessons.</p>
</div></div>
</div>
<div><span class="lbl">One branch at a time with details name</span>
<div class="card mx-card border-0 rounded-4"><div class="card-body">
<p class="mx-head fw-bold mb-1">Store settings</p>
<p class="mx-note text-body-secondary mb-3">Each section shares one name attribute, so opening one closes the others.</p>
<ul class="mx mx-anim mx-one list-unstyled mb-0">
  <li><details name="store-settings"><summary class="fw-semibold">General</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#store-name">Store name</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#time-zone">Time zone</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#currency">Currency</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings" open><summary class="fw-semibold">Shipping</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#zones">Zones</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#rates">Rates</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#local-pickup">Local pickup</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary class="fw-semibold">Payments</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#cards">Cards</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#bank-transfer">Bank transfer</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#payouts">Payouts</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary class="fw-semibold">Tax</summary>
    <ul class="list-unstyled mb-0">
      <li><a class="mx-leaf text-decoration-none" href="#rates">Rates</a></li>
      <li><a class="mx-leaf text-decoration-none" href="#exemptions">Exemptions</a></li>
    </ul>
  </details></li>
</ul>
</div></div>
</div>
</div>
CSS
/* Modern CSS tree: native CSS nesting, :has() for state styling, ::details-content + interpolate-size for the
   open animation (other browsers open instantly), and <details name> for one open branch at a time. */
.mx{
  --mx-accent:#047857;--mx-text:#1c1917;--mx-muted:#57534e;--mx-line:#d6d3cb;--mx-hover:#f2f7f4;--mx-panel:#f4faf7;
  font-size:.9rem;line-height:1.35;color:var(--mx-text);
  & ul{margin:0 0 0 14px;padding:2px 0 4px 12px;border-left:1.5px solid var(--mx-line)}
  & summary{display:flex;align-items:center;gap:10px;min-height:32px;font-weight:600;padding:0 8px;border-radius:8px;cursor:pointer;list-style:none;
    &::-webkit-details-marker{display:none}
    &::before{content:"";flex:none;width:7px;height:7px;margin:0 2px;border-right:2px solid var(--mx-muted);border-bottom:2px solid var(--mx-muted);
      transform:rotate(-45deg);transition:transform .2s ease}
    &:hover{background:var(--mx-hover)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
  }
  & details[open]>summary{color:var(--mx-accent);&::before{transform:rotate(45deg);border-color:var(--mx-accent)}}
  & .mx-leaf{display:block;padding:6px 8px;border-radius:8px;color:#44403c}
  & a.mx-leaf{text-decoration:none;
    &:hover{background:var(--mx-hover);color:var(--mx-text)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
    &[aria-current="page"]{background:var(--mx-accent);color:#fff;font-weight:600}
  }
}
/* :has() — an open branch gets a tinted panel; the branch holding aria-current="page" gets a marker */
.mx-trail{
  & li:has(>details[open]){background:var(--mx-panel);border-radius:10px;margin-bottom:4px}
  & ul{border-left-color:#b7d8c9}
  & summary:has(+ul [aria-current="page"])::after{content:"";width:8px;height:8px;margin-left:auto;border-radius:50%;background:var(--mx-accent);box-shadow:0 0 0 3px #cfe8dc}
}
.mx-hint{display:none}
.mx-trail:not(:has(details[open]))+.mx-hint{display:block;margin-top:10px}
/* one open branch: the top-level details share name="store-settings" */
.mx-one>li+li{border-top:1px solid #ece9e2}
.mx-one>li>details>summary{min-height:40px}
/* the animation: only where interpolate-size and ::details-content both exist */
@supports (interpolate-size:allow-keywords) and selector(::details-content){
  .mx-anim{interpolate-size:allow-keywords;
    & details::details-content{block-size:0;overflow:clip;opacity:0;
      transition:block-size .28s ease,opacity .28s ease,content-visibility .28s allow-discrete}
    & details[open]::details-content{block-size:auto;opacity:1}
  }
}
@media (prefers-reduced-motion:reduce){
  .mx summary::before,.mx-anim details::details-content{transition:none}
}
.mx-card{--bs-card-spacer-y:22px;--bs-card-spacer-x:20px;border:1px solid #e4e1d8!important;box-shadow:0 12px 32px -24px rgba(41,37,36,.45)}
.mx-head{font-size:.95rem;color:#1c1917}
.mx-note{font-size:.8rem;line-height:1.45}
.mx-hint{font-size:.8rem}
.mx ul{margin-bottom:0}

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">Animated open with ::details-content</span>
<div class="rounded-2xl border border-[#e4e1d8] bg-white px-5 py-[22px] shadow-[0_12px_32px_-24px_rgba(41,37,36,.45)]">
<p class="m-0 mb-1 text-[.95rem] font-bold text-[#1c1917]">Recipe box</p>
<p class="m-0 mb-3.5 text-[.8rem] leading-[1.45] text-[#57534e]">Branches grow open with interpolate-size; older browsers open them instantly.</p>
<ul class="mx mx-anim m-0 list-none p-0">
  <li><details><summary class="font-semibold">Breakfast</summary>
    <ul class="m-0 list-none p-0">
      <li><span class="mx-leaf no-underline">Shakshuka</span></li>
      <li><span class="mx-leaf no-underline">Overnight oats</span></li>
      <li><span class="mx-leaf no-underline">Buttermilk pancakes</span></li>
    </ul>
  </details></li>
  <li><details open><summary class="font-semibold">Mains</summary>
    <ul class="m-0 list-none p-0">
      <li><details open><summary class="font-semibold">Pasta</summary>
        <ul class="m-0 list-none p-0">
          <li><span class="mx-leaf no-underline">Cacio e pepe</span></li>
          <li><span class="mx-leaf no-underline">Lemon orzo</span></li>
        </ul>
      </details></li>
      <li><details><summary class="font-semibold">Curries</summary>
        <ul class="m-0 list-none p-0">
          <li><span class="mx-leaf no-underline">Chickpea curry</span></li>
          <li><span class="mx-leaf no-underline">Thai green curry</span></li>
        </ul>
      </details></li>
      <li><span class="mx-leaf no-underline">Roast chicken</span></li>
    </ul>
  </details></li>
  <li><details><summary class="font-semibold">Desserts</summary>
    <ul class="m-0 list-none p-0">
      <li><span class="mx-leaf no-underline">Olive oil cake</span></li>
      <li><span class="mx-leaf no-underline">Pear tart</span></li>
    </ul>
  </details></li>
</ul>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5b6577] uppercase">Open branches and current trail with :has()</span>
<div class="rounded-2xl border border-[#e4e1d8] bg-white px-5 py-[22px] shadow-[0_12px_32px_-24px_rgba(41,37,36,.45)]">
<p class="m-0 mb-1 text-[.95rem] font-bold text-[#1c1917]">Data Visualisation 101</p>
<p class="m-0 mb-3.5 text-[.8rem] leading-[1.45] text-[#57534e]">Open modules get a tinted panel; the module holding the current lesson is marked, with no classes.</p>
<ul class="mx mx-anim mx-trail m-0 list-none p-0">
  <li><details><summary class="font-semibold">1 Foundations</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#1-1-why-charts-mislead">1.1 Why charts mislead</a></li>
      <li><a class="mx-leaf no-underline" href="#1-2-choosing-a-chart">1.2 Choosing a chart</a></li>
    </ul>
  </details></li>
  <li><details open><summary class="font-semibold">2 Colour</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#2-1-palettes">2.1 Palettes</a></li>
      <li><a class="mx-leaf no-underline" href="#2-2-contrast-and-accessibility" aria-current="page">2.2 Contrast and accessibility</a></li>
      <li><a class="mx-leaf no-underline" href="#2-3-dark-mode">2.3 Dark mode</a></li>
    </ul>
  </details></li>
  <li><details open><summary class="font-semibold">3 Layout</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#3-1-grids">3.1 Grids</a></li>
      <li><a class="mx-leaf no-underline" href="#3-2-small-multiples">3.2 Small multiples</a></li>
    </ul>
  </details></li>
  <li><details><summary class="font-semibold">4 Storytelling</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#4-1-annotation">4.1 Annotation</a></li>
      <li><a class="mx-leaf no-underline" href="#4-2-dashboards">4.2 Dashboards</a></li>
    </ul>
  </details></li>
</ul>
<p class="mx-hint m-0 text-[.8rem] text-[#57534e]">Every module is folded. Open one to see its lessons.</p>
</div>
</div>
<div><span class="mb-4 block text-[.72rem] tracking-[.12em] text-[#5b6577] uppercase">One branch at a time with details name</span>
<div class="rounded-2xl border border-[#e4e1d8] bg-white px-5 py-[22px] shadow-[0_12px_32px_-24px_rgba(41,37,36,.45)]">
<p class="m-0 mb-1 text-[.95rem] font-bold text-[#1c1917]">Store settings</p>
<p class="m-0 mb-3.5 text-[.8rem] leading-[1.45] text-[#57534e]">Each section shares one name attribute, so opening one closes the others.</p>
<ul class="mx mx-anim mx-one m-0 list-none p-0">
  <li><details name="store-settings"><summary class="font-semibold">General</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#store-name">Store name</a></li>
      <li><a class="mx-leaf no-underline" href="#time-zone">Time zone</a></li>
      <li><a class="mx-leaf no-underline" href="#currency">Currency</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings" open><summary class="font-semibold">Shipping</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#zones">Zones</a></li>
      <li><a class="mx-leaf no-underline" href="#rates">Rates</a></li>
      <li><a class="mx-leaf no-underline" href="#local-pickup">Local pickup</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary class="font-semibold">Payments</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#cards">Cards</a></li>
      <li><a class="mx-leaf no-underline" href="#bank-transfer">Bank transfer</a></li>
      <li><a class="mx-leaf no-underline" href="#payouts">Payouts</a></li>
    </ul>
  </details></li>
  <li><details name="store-settings"><summary class="font-semibold">Tax</summary>
    <ul class="m-0 list-none p-0">
      <li><a class="mx-leaf no-underline" href="#rates">Rates</a></li>
      <li><a class="mx-leaf no-underline" href="#exemptions">Exemptions</a></li>
    </ul>
  </details></li>
</ul>
</div>
</div>
</div>
CSS
/* Modern CSS tree: native CSS nesting, :has() for state styling, ::details-content + interpolate-size for the
   open animation (other browsers open instantly), and <details name> for one open branch at a time. */
.mx{
  --mx-accent:#047857;--mx-text:#1c1917;--mx-muted:#57534e;--mx-line:#d6d3cb;--mx-hover:#f2f7f4;--mx-panel:#f4faf7;
  font-size:.9rem;line-height:1.35;color:var(--mx-text);
  & ul{margin:0 0 0 14px;padding:2px 0 4px 12px;border-left:1.5px solid var(--mx-line)}
  & summary{display:flex;align-items:center;gap:10px;min-height:32px;font-weight:600;padding:0 8px;border-radius:8px;cursor:pointer;list-style:none;
    &::-webkit-details-marker{display:none}
    &::before{content:"";flex:none;width:7px;height:7px;margin:0 2px;border-right:2px solid var(--mx-muted);border-bottom:2px solid var(--mx-muted);
      transform:rotate(-45deg);transition:transform .2s ease}
    &:hover{background:var(--mx-hover)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
  }
  & details[open]>summary{color:var(--mx-accent);&::before{transform:rotate(45deg);border-color:var(--mx-accent)}}
  & .mx-leaf{display:block;padding:6px 8px;border-radius:8px;color:#44403c}
  & a.mx-leaf{text-decoration:none;
    &:hover{background:var(--mx-hover);color:var(--mx-text)}
    &:focus-visible{outline:2px solid var(--mx-accent);outline-offset:1px}
    &[aria-current="page"]{background:var(--mx-accent);color:#fff;font-weight:600}
  }
}
/* :has() — an open branch gets a tinted panel; the branch holding aria-current="page" gets a marker */
.mx-trail{
  & li:has(>details[open]){background:var(--mx-panel);border-radius:10px;margin-bottom:4px}
  & ul{border-left-color:#b7d8c9}
  & summary:has(+ul [aria-current="page"])::after{content:"";width:8px;height:8px;margin-left:auto;border-radius:50%;background:var(--mx-accent);box-shadow:0 0 0 3px #cfe8dc}
}
.mx-hint{display:none}
.mx-trail:not(:has(details[open]))+.mx-hint{display:block;margin-top:10px}
/* one open branch: the top-level details share name="store-settings" */
.mx-one>li+li{border-top:1px solid #ece9e2}
.mx-one>li>details>summary{min-height:40px}
/* the animation: only where interpolate-size and ::details-content both exist */
@supports (interpolate-size:allow-keywords) and selector(::details-content){
  .mx-anim{interpolate-size:allow-keywords;
    & details::details-content{block-size:0;overflow:clip;opacity:0;
      transition:block-size .28s ease,opacity .28s ease,content-visibility .28s allow-discrete}
    & details[open]::details-content{block-size:auto;opacity:1}
  }
}
@media (prefers-reduced-motion:reduce){
  .mx summary::before,.mx-anim details::details-content{transition:none}
}

About this set

Three trees that do with CSS what many tree plugins do with JavaScript. Animated open with ::details-content is a recipe box whose branches grow open and fold shut: the ::details-content pseudo-element transitions its block size from zero to auto, made possible by interpolate-size allow-keywords, together with a discrete transition on content-visibility. The whole rule sits inside @supports, so browsers without these features simply open the branch at once. Open branches and current trail with :has() is a course syllabus where any list item that holds an open details element gets a tinted panel, the module that contains the current lesson gets a dot through summary:has(+ul [aria-current]), and a hint appears when every module is folded, all without classes or script. One branch at a time with details name is a store settings menu where the top-level details share one name attribute, so the browser closes the open section when another opens, an exclusive accordion with no JavaScript. All three are written in native CSS nesting.

What’s included

  • Animated open and close with ::details-content and interpolate-size
  • Open branch panels and current-page trail styled with :has()
  • Exclusive branches with the details name attribute
  • Written in native CSS nesting, no JavaScript
  • Falls back to an instant open where the animation is not supported

Accessibility

Every branch is a summary element, announced as expandable with its state and operated with Tab, Enter and Space. The current lesson link carries aria-current page. Summaries and links show a 2px green outline on keyboard focus, the white current link on green meets 4.5:1, and under prefers-reduced-motion the open animation and chevron rotation are switched off.

Customise it

Set --mx-accent, --mx-panel, --mx-hover and --mx-line on .mx, and change the .28s durations in the @supports block for a faster or slower open. To make another group exclusive, give its details elements the same name value.