Chapter 98: Capstone 1 — Production Documentation Site

Accessible Collapsible Sidebar Navigation

Building a keyboard-navigable documentation sidebar tree with semantic `<details>/<summary>` accordions, ARIA treeview semantics, state persistence, and active route markers.

LEARNING OBJECTIVES
  • Construct multi-level hierarchical navigation trees using native semantic HTML5 <details> and <summary> elements.
  • Implement ARIA active state semantics using aria-current="page" and aria-expanded.
  • Support complete keyboard navigation: Enter, Space, ArrowDown, ArrowUp, and focus restoration.
  • Persist sidebar accordion collapse/expansion states in localStorage across page navigations.
🎬 INTERACTIVE VISUAL PIPELINE Core Architecture Simulation
🌐
1. Input
Directives & Tags
⚙️
2. Parse
Tokenizer & AST
🌳
3. Layout
Box Model & Flow
🎨
4. Render
GPU Paint & Composite
PHASE 1: INPUT & DIRECTIVES
Browser receives declarative markup stream, parsing tag tokens and initializing component state.

📖 The Mental Model & Story (Intuitive Foundation)

Imagine browsing through a deep 500-page API documentation manual with dozens of nested modules, classes, methods, and tutorials. If every single item is expanded all at once, you face a terrifying "wall of links" spanning 10 screens of vertical scrolling.

Conversely, if everything is collapsed behind custom JavaScript <div> buttons that do not support native keyboard shortcuts or screen readers, the documentation becomes unusable for non-mouse users.

The mental model of an accessible sidebar is a hierarchical tree of disclosure widgets. In native HTML5, <details> and <summary> are designed exactly for this purpose. They offer zero-JS built-in disclosure, native keyboard interaction (Enter / Space toggles), and accessibility attributes baked right into the browser engine. When enhanced with lightweight state persistence and active link indicators (aria-current="page"), we create a high-performance, rock-solid navigation tree.


Technical Deep Dive & Specifications

2.1 Sidebar Hierarchy & DOM Structure

A modern documentation sidebar consists of category groups containing nested lists of topic links:

+-------------------------------------------------------------+
| <nav aria-label="Documentation Sidebar">                    |
|   <ul class="nav-tree">                                     |
|     <!-- Group 1: Native HTML5 Details Disclosure -->       |
|     <li>                                                    |
|       <details open data-group-id="getting-started">        |
|         <summary>                                           |
|           <span class="folder-icon">📁</span>               |
|           <span class="group-title">Getting Started</span>  |
|           <span class="chevron" aria-hidden="true">▶</span> |
|         </summary>                                          |
|         <ul class="sub-nav">                                |
|           <li><a href="/intro">Introduction</a></li>        |
|           <li><a href="/install">Installation</a></li>      |
|           <li>                                              |
|             <a href="/arch" aria-current="page">            |
|               Architecture (Active)                         |
|             </a>                                            |
|           </li>                                             |
|         </ul>                                               |
|       </details>                                            |
|     </li>                                                   |
|     <!-- Group 2: Advanced Topics -->                       |
|     <li>                                                    |
|       <details data-group-id="advanced">                    |
|         <summary>...</summary>                              |
|         <ul class="sub-nav">...</ul>                        |
|       </details>                                            |
|     </li>                                                   |
|   </ul>                                                     |
| </nav>                                                      |
+-------------------------------------------------------------+

2.2 Semantic Requirements & WAI-ARIA Specifications

HTML / ARIA Attribute Target Element Purpose & Accessibility Function
<nav aria-label="..."> Root container Distinguishes the sidebar navigation landmark from header/breadcrumb landmarks.
<details [open]> Accordion group Native disclosure element; the open boolean attribute determines visibility.
<summary> Group trigger Native focusable header that toggles the parent <details> on Click, Enter, or Space.
aria-current="page" Active <a> link Informs screen reader users that this link matches the currently displayed page.
aria-hidden="true" Chevron icons / SVGs Prevents decorative visual indicators from cluttering screen reader announcements.

2.3 Eliminating Summary Markers and Customizing Chevrons

Browsers natively render a disclosure triangle on <summary>. To replace this with a smooth rotating custom SVG chevron:

/* Remove native browser disclosure triangles across engines */
summary {
  list-style: none; /* Modern standard */
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  user-select: none;
}
summary::-webkit-details-marker {
  display: none; /* Legacy Safari / WebKit */
}

/* Custom animated chevron */
.chevron {
  transition: transform 200ms cubic-bezier(0.4, 0, 0.2, 1);
}
details[open] > summary .chevron {
  transform: rotate(90deg);
}

2.4 State Persistence Strategy

When navigating between pages in a multi-page documentation portal (MPA), all <details> elements reset to their default HTML state unless synchronized. We persist states using localStorage:

  1. Assign each <details> a unique data-group-id.
  2. On toggle event, write { [groupId]: details.open } to localStorage.
  3. Auto-expand any <details> containing the active link ([aria-current="page"]) regardless of saved state.

💻 Interactive Code Playground

Starter Code

Line-by-Line Code Breakdown

  • Line 33–48: CSS removes default user-agent disclosure triangles via list-style: none and ::-webkit-details-marker.
  • Lines 49–54: .chevron rotates 90 degrees cleanly when details[open] attribute is toggled.
  • Lines 73–77: .sub-nav a[aria-current="page"] applies prominent visual highlighting to the active page while providing semantic cues for screen readers.
  • Lines 84–97: <details open data-group="getting-started"> provides native accessible disclosure without custom ARIA hacks.
  • Lines 135–158: JavaScript restores persisted accordion state from localStorage on page load, automatically forces open any group containing [aria-current="page"], and updates storage on the native toggle event.

Expected Browser Render Output


SYS: ACTIVE
HULL: 98%
CORE: STABLE
NET: ONLINE
HTML STARSHIP CODE TERMINAL playground.html
LIVE RENDER & DIAGNOSTICS CORE TEMP: 45°C
INSPECTING DOM: VALID
TAGS: SCANNING...
+------------------------------------+--------------------------------------------+
| [Documentation Navigation]         | # Quickstart Guide                         |
| ▼ 🚀 Getting Started               |                                            |
|   |  Overview                      | This active page is highlighted using      |
|   |  [Quickstart Guide (Active)]   | aria-current="page".                       |
|   |  CLI Installation              |                                            |
|                                    |                                            |
| ▼ 🧩 Core Concepts                 |                                            |
|   |  Semantic HTML                 |                                            |
|   |  ARIA Patterns                 |                                            |
|   |  Focus Management              |                                            |
|                                    |                                            |
| ▶ ⚡ API Reference                 |                                            |
+------------------------------------+--------------------------------------------+

🏋️ Hands-On Exercise

🎯 The Challenge: Add Keyboard Tree Navigation (Arrow Keys)

Instructions:

  1. Enhance the sidebar so that when a <summary> or <a> link has focus, pressing ArrowDown moves focus to the next visible focusable item, and ArrowUp moves to the previous visible item.
  2. Pressing ArrowRight on a collapsed <summary> must expand it (open = true).
  3. Pressing ArrowLeft on an expanded <summary> must collapse it (open = false).

🏁 Starter Code Sandbox

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Sidebar Keyboard Challenge</title>
</head>
<body>
  <nav class="doc-sidebar" aria-label="Documentation">
    <ul class="nav-tree">
      <li>
        <details open>
          <summary>Category A</summary>
          <ul>
            <li><a href="#a1">Item A.1</a></li>
            <li><a href="#a2">Item A.2</a></li>
          </ul>
        </details>
      </li>
      <li>

⚠️ Common Pitfalls

  1. Using role="tree" Without Implementing Full WAI-ARIA Tree Mechanics: Adding role="tree", role="treeitem", and role="group" forces assistive technologies into Tree Mode, requiring manual management of aria-expanded, aria-selected, tabindex="0/-1", and arrow key dispatching. If you don't implement the full 15-rule APG specification, use native <details>/<summary> instead!
  2. Forgetting to Persist Open State on Direct Page Links: When a user clicks a deep link, the target page reloads. If your state script doesn't force the active group open (details.querySelector('[aria-current="page"]')), the user loses visual context of where they are.
  3. Blocking <summary> Default Click Event: Never call e.preventDefault() on <summary> click events without manually toggling the open property; doing so breaks native browser accessibility.

💡 Pro Tips

  1. Auto-Scroll Active Item into View: In tall sidebars with hundreds of items, the active item may be scrolled off-screen. Add document.querySelector('[aria-current="page"]')?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); on DOM load.
  2. Zero-Layout-Shift Animation with CSS Grid: Animate <details> expansion smoothly by setting the child content to display: grid; grid-template-rows: 0fr; transition: grid-template-rows 200ms ease; and on details[open] expanding to grid-template-rows: 1fr;.

📌 Key Takeaways

  • Native HTML5 <details> and <summary> elements provide accessible, zero-JavaScript disclosure widgets.
  • Hide browser default summary markers with summary { list-style: none; } and summary::-webkit-details-marker { display: none; }.
  • Use aria-current="page" on the active documentation link to provide essential context to assistive technologies.
  • Store collapsed/expanded group state in localStorage keyed by unique data-group IDs, but always force open groups containing the active page.
  • Support keyboard roving (ArrowDown, ArrowUp, ArrowLeft, ArrowRight) for professional developer documentation ergonomics.
  • --
⭐ LEARN: HTML 🌟 ⚔️ QUIZ BATTLE ARENA // ACTIVE
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 1 / 3

What happens to assistive technologies when you apply role="tree" to an unmanaged HTML <ul> list?

Question 1 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 2 / 3

Which native DOM event fires whenever an HTML5 <details> element is opened or closed?

Question 2 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP
3x
STREAK!
BONUS ACTIVE
COMBO
? Question 3 / 3

Why should SVG chevrons inside <summary> elements always have aria-hidden="true"?

Question 3 / 3 Topic: HTML Fundamentals
00:45 REMAINING
XP REWARD
+250 XP