This is an automated email from the ASF dual-hosted git repository.

guoqqqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-website.git


The following commit(s) were added to refs/heads/master by this push:
     new cd935f62f43 fix(docs): improve sidebar hierarchy and version picker 
(#2142)
cd935f62f43 is described below

commit cd935f62f4369f831ef328e79c357a05cce12f82
Author: Zhi Yuan Ju <[email protected]>
AuthorDate: Fri Oct 23 09:18:06 2026 +0800

    fix(docs): improve sidebar hierarchy and version picker (#2142)
---
 next/src/components/SidebarNodes.astro | 12 ++++--
 next/src/layouts/DocPage.astro         | 20 ++++++++-
 next/src/styles/global.css             | 74 ++++++++++++++++++----------------
 3 files changed, 66 insertions(+), 40 deletions(-)

diff --git a/next/src/components/SidebarNodes.astro 
b/next/src/components/SidebarNodes.astro
index 230d74616fe..0b8c038b7e2 100644
--- a/next/src/components/SidebarNodes.astro
+++ b/next/src/components/SidebarNodes.astro
@@ -20,17 +20,23 @@ const renderable = (n: SidebarNode): boolean =>
 
 const hasChildren = (n: SidebarNode): boolean =>
   Array.isArray(n.items) && n.items.some(renderable);
+
+const containsActive = (n: SidebarNode): boolean =>
+  Boolean(active && (n.id === active || n.items?.some(containsActive)));
 ---
 <ul>
   {nodes.filter(renderable).map((node) => (
     <li class={hasChildren(node) ? 'cat' : ''}>
       {hasChildren(node) ? (
-        <details class="sidebar-category">
-          <summary>{node.label ?? titleById?.get(node.id ?? '') ?? 
node.id}</summary>
+        <details class="sidebar-category" open={containsActive(node)}>
+          <summary>
+            <span>{node.label ?? titleById?.get(node.id ?? '') ?? 
node.id}</span>
+            <svg class="sidebar-chevron" width="16" height="16" viewBox="0 0 
24 24" fill="none" stroke="currentColor" stroke-width="1.7" 
aria-hidden="true"><path d="m9 6 6 6-6 6" /></svg>
+          </summary>
           <Astro.self nodes={node.items!} active={active} 
titleById={titleById} urlById={urlById} />
         </details>
       ) : node.id ? (
-        <a href={urlById(node.id)} class={node.id === active ? 'active' : 
''}>{titleById?.get(node.id) ?? node.id}</a>
+        <a href={urlById(node.id)} class={node.id === active ? 'active' : ''} 
aria-current={node.id === active ? 'page' : undefined}>{titleById?.get(node.id) 
?? node.id}</a>
       ) : (
         <span>{node.label}</span>
       )}
diff --git a/next/src/layouts/DocPage.astro b/next/src/layouts/DocPage.astro
index b5aa76bd079..6aa5f84dac9 100644
--- a/next/src/layouts/DocPage.astro
+++ b/next/src/layouts/DocPage.astro
@@ -70,7 +70,10 @@ const hasAlternate = entry.hasTranslation && 
!canonicalOverride;
           versions.length > 1
             ? (
               <details class="version-picker">
-                <summary>{versionLabel}</summary>
+                <summary>
+                  <span>{versionLabel}</span>
+                  <svg class="version-chevron" width="16" height="16" 
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" 
aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
+                </summary>
                 <ul>
                   {versions.map((v) => (
                     <li><a href={v.href} aria-current={v.current ? 'true' : 
undefined}>{v.label}</a></li>
@@ -80,7 +83,9 @@ const hasAlternate = entry.hasTranslation && 
!canonicalOverride;
             )
             : <p style="font-weight:700;padding:.2rem 
.5rem;margin:0">{versionLabel}</p>
         )}
-        <SidebarNodes nodes={sidebar} active={active} titleById={titleById} 
urlById={urlById!} />
+        <div class="docs-sidebar-tree">
+          <SidebarNodes nodes={sidebar} active={active} titleById={titleById} 
urlById={urlById!} />
+        </div>
       </nav>
     )}
     <article class="docs-content prose">
@@ -93,6 +98,17 @@ const hasAlternate = entry.hasTranslation && 
!canonicalOverride;
     </article>
   </div>
   <script>
+    document.querySelectorAll<HTMLDetailsElement>('.docs-sidebar 
.version-picker').forEach((picker) => {
+      document.addEventListener('click', (event) => {
+        if (picker.open && event.target instanceof Node && 
!picker.contains(event.target)) picker.open = false;
+      });
+      document.addEventListener('keydown', (event) => {
+        if (event.key === 'Escape' && picker.open) {
+          picker.open = false;
+          picker.querySelector('summary')?.focus();
+        }
+      });
+    });
     document.querySelectorAll<HTMLElement>('.table-shell').forEach((shell) => {
       const scroller = shell.querySelector<HTMLElement>('.table-scroll');
       const table = scroller?.querySelector('table');
diff --git a/next/src/styles/global.css b/next/src/styles/global.css
index f04787654aa..39268dac36e 100644
--- a/next/src/styles/global.css
+++ b/next/src/styles/global.css
@@ -339,35 +339,31 @@ a.tag:focus-visible { outline: 2px solid 
var(--color-primary); outline-offset: 2
 
 .docs-sidebar {
   position: sticky; top: calc(var(--header-height) + 1rem);
+  display: flex; flex-direction: column; gap: 1rem; min-height: 0;
   max-height: calc(100vh - var(--header-height) - 2rem);
-  max-height: calc(100dvh - var(--header-height) - 2rem); overflow-y: auto;
-  font-size: .88rem; padding-right: .5rem;
-}
-.docs-sidebar ul { list-style: none; padding-left: .85rem; margin: .2rem 0; }
-.docs-sidebar > ul { padding-left: 0; }
-.docs-sidebar li { margin: .1rem 0; }
-.docs-sidebar a { display: block; color: var(--color-text-soft); padding: 
.22rem .5rem; border-radius: 6px; }
-.docs-sidebar a:hover { color: var(--color-primary); background: 
var(--color-surface-alt); text-decoration: none; }
-.docs-sidebar a.active { color: var(--color-primary); font-weight: 600; 
background: #fdeeee; }
+  max-height: calc(100dvh - var(--header-height) - 2rem);
+  border-right: 1px solid var(--color-border); padding-right: 1.25rem;
+  font-size: .875rem; line-height: 1.5;
+}
+.docs-sidebar-tree { min-height: 0; overflow-y: auto; scrollbar-width: thin; 
scrollbar-color: #d7dce2 transparent; }
+.docs-sidebar ul { list-style: none; margin: 0; padding: 0; }
+.docs-sidebar-tree ul ul { margin: .2rem 0 .35rem .8rem; padding-left: .65rem; 
border-left: 1px solid var(--color-border); }
+.docs-sidebar li { margin: .125rem 0; }
+.docs-sidebar-tree > ul > .cat + .cat { margin-top: .5rem; }
+.docs-sidebar a { display: block; color: var(--color-text-soft); padding: 
.45rem .65rem; border-radius: 6px; overflow-wrap: anywhere; }
+.docs-sidebar a:hover { color: var(--color-text); background: 
var(--color-surface-alt); text-decoration: none; }
+.docs-sidebar a.active { color: var(--color-primary-dark); font-weight: 600; 
background: #fff1f0; box-shadow: inset 2px 0 var(--color-primary); }
 .docs-sidebar .sidebar-category > summary {
-  display: flex;
-  align-items: center;
-  gap: .35rem;
-  padding: .5rem .5rem .2rem;
-  color: var(--color-text);
-  font-weight: 700;
-  cursor: pointer;
-  list-style: none;
+  display: flex; align-items: center; justify-content: space-between; gap: 
.5rem;
+  padding: .5rem .65rem; border-radius: 6px;
+  color: var(--color-text); font-weight: 600; cursor: pointer; list-style: 
none;
 }
+.docs-sidebar .sidebar-category .sidebar-category > summary { font-weight: 
500; }
 .docs-sidebar .sidebar-category > summary::-webkit-details-marker { display: 
none; }
-.docs-sidebar .sidebar-category > summary::before {
-  content: '▸';
-  color: var(--color-text-soft);
-  font-size: .8em;
-  transition: transform var(--dur) var(--ease);
-}
-.docs-sidebar .sidebar-category[open] > summary::before { transform: 
rotate(90deg); }
-.docs-sidebar .sidebar-category > summary:hover { color: var(--color-primary); 
}
+.docs-sidebar .sidebar-category > summary:hover { background: 
var(--color-surface-alt); }
+.sidebar-chevron { flex: none; color: #8b949f; transition: transform 
var(--dur) var(--ease); }
+.sidebar-category[open] > summary .sidebar-chevron { transform: rotate(90deg); 
}
+.docs-sidebar a:focus-visible, .docs-sidebar summary:focus-visible { outline: 
2px solid var(--color-primary); outline-offset: -2px; }
 
 /* stylelint-disable no-descending-specificity */
 .docs-content {
@@ -578,7 +574,7 @@ a.tag:focus-visible { outline: 2px solid 
var(--color-primary); outline-offset: 2
      there. Capping the box at 30vh keeps the article's own heading on the
      first screen. minmax(0,1fr) keeps long code lines from inflating the 
column. */
   .docs-layout { grid-template-columns: minmax(0, 1fr); }
-  .docs-sidebar { position: static; max-height: 30vh; max-height: 30dvh; 
overflow-y: auto; border: 1px solid var(--color-border); border-radius: 8px; 
padding: .75rem; }
+  .docs-sidebar { position: static; max-height: 30vh; max-height: 30dvh; 
border: 1px solid var(--color-border); border-radius: 8px; padding: .75rem; }
 }
 
 /* ---------- homepage extras ---------- */
@@ -696,15 +692,23 @@ h2:hover .anchor-link, h3:hover .anchor-link, 
.anchor-link:focus-visible { opaci
 @media (max-width: 996px) { .docsearch-slot { display: none; } }
 
 /* ---------- docs version picker ---------- */
-.version-picker { margin: 0 0 .5rem; }
+.version-picker { position: relative; flex: none; }
 .version-picker > summary {
-  cursor: pointer; font-weight: 700; padding: .35rem .5rem; border-radius: 6px;
-  list-style: none; display: flex; align-items: center; gap: .35rem;
+  display: flex; align-items: center; justify-content: space-between; gap: 
.5rem;
+  padding: .65rem .75rem; border: 1px solid #dde2e8; border-radius: 8px;
+  background: #f8fafc; color: var(--color-text); font-weight: 600; cursor: 
pointer; list-style: none;
 }
 .version-picker > summary::-webkit-details-marker { display: none; }
-.version-picker > summary::after { content: '▾'; font-size: .8em; color: 
var(--color-text-soft); }
-.version-picker > summary:hover { background: var(--color-surface-alt); }
-.version-picker ul { list-style: none; margin: .25rem 0 0; padding: 0 0 0 
.5rem; }
-.version-picker li a { display: block; padding: .3rem .5rem; border-radius: 
6px; color: var(--color-text-soft); font-size: .9rem; }
-.version-picker li a:hover { background: var(--color-surface-alt); color: 
var(--color-primary); text-decoration: none; }
-.version-picker li a[aria-current] { color: var(--color-primary); font-weight: 
600; }
+.version-picker > summary:hover { background: #f1f5f9; }
+.version-chevron { flex: none; color: #7b8794; transition: transform 
var(--dur) var(--ease); }
+.version-picker[open] .version-chevron { transform: rotate(180deg); }
+.version-picker[open] > summary { border-color: #c5ced8; }
+.version-picker > ul {
+  position: absolute; z-index: 110; top: calc(100% + .5rem); left: 0; right: 0;
+  max-height: min(24rem, 50dvh); overflow-y: auto; padding: .4rem;
+  border: 1px solid var(--color-border); border-radius: 10px;
+  background: #fff; box-shadow: 0 12px 32px rgb(15 23 42 / 12%);
+}
+.version-picker li a { display: flex; align-items: center; justify-content: 
space-between; gap: .5rem; padding: .5rem .65rem; font-size: .875rem; }
+.version-picker li a[aria-current] { color: var(--color-primary-dark); 
background: #fff1f0; font-weight: 600; }
+.version-picker li a[aria-current]::after { content: '✓'; }

Reply via email to