Repository: nodejs/node
Pull request: doc: improve accessibility of expandable lists

- Focus the first link in the expandable list – currently the focus stays on the opening link, which can be confusing for folks using a screen reader (if you don't have a visual clue, you don't know if the clicking link did anything).
- Remove focusability on the wrapping div – I don't think focusing it any purpose, so let's remove that additional keyboard and screen reader users have to make.
- Fix the keyboard navigation when JS is disabled.

<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: doc/api_assets/api.js
patch: @@ -41,6 +41,7 @@
     function closeAllPickers() {
       for (const picker of pickers) {
         picker.parentNode.classList.remove('expanded');
+        picker.ariaExpanded = false;
       }
 
       window.removeEventListener('click', closeAllPickers);
@@ -58,14 +59,15 @@
     for (const picker of pickers) {
       const parentNode = picker.parentNode;
 
+      picker.ariaExpanded = parentNode.classList.contains('expanded');
       picker.addEventListener('click', function(e) {
         e.preventDefault();
 
         /*
           closeAllPickers as window event trigger already closed all the pickers,
           if it already closed there is nothing else to do here
         */
-        if (parentNode.classList.contains('expanded')) {
+        if (picker.ariaExpanded === 'true') {
           return;
         }
 
@@ -75,9 +77,11 @@
         */
 
         requestAnimationFrame(function() {
+          picker.ariaExpanded = true;
           parentNode.classList.add('expanded');
           window.addEventListener('click', closeAllPickers);
           window.addEventListener('keydown', onKeyDown);
+          parentNode.querySelector('.picker a').focus();
         });
       });
     }

filename: doc/api_assets/style.css
patch: @@ -182,34 +182,29 @@ li.picker-header .picker-arrow {
   height: .6rem;
   border-top: .3rem solid transparent;
   border-bottom: .3rem solid transparent;
-  border-left: .6rem solid var(--color-links);
+  border-left: .6rem solid currentColor;
   border-right: none;
   margin: 0 .2rem .05rem 0;
 }
 
-li.picker-header a:focus .picker-arrow,
-li.picker-header a:active .picker-arrow,
-li.picker-header a:hover .picker-arrow {
-  border-left: .6rem solid var(--white);
-}
-
-li.picker-header.expanded a:focus .picker-arrow,
-li.picker-header.expanded a:active .picker-arrow,
-li.picker-header.expanded a:hover .picker-arrow,
+li.picker-header.expanded .picker-arrow,
+:root:not(.has-js) li.picker-header:focus-within .picker-arrow,
 :root:not(.has-js) li.picker-header:hover .picker-arrow  {
-  border-top: .6rem solid var(--white);
+  border-top: .6rem solid currentColor;
   border-bottom: none;
   border-left: .35rem solid transparent;
   border-right: .35rem solid transparent;
   margin-bottom: 0;
 }
 
 li.picker-header.expanded > a,
+:root:not(.has-js) li.picker-header:focus-within > a,
 :root:not(.has-js) li.picker-header:hover > a {
   border-radius: 2px 2px 0 0;
 }
 
 li.picker-header.expanded > .picker, 
+:root:not(.has-js) li.picker-header:focus-within > .picker,
 :root:not(.has-js) li.picker-header:hover > .picker {
   display: block;
   z-index: 1;

filename: doc/template.html
patch: @@ -59,13 +59,13 @@ <h1>Node.js __VERSION__ documentation</h1>
             __GTOC_PICKER__
             __ALTDOCS__
             <li class="picker-header">
-              <a href="#">
+              <a href="#options-picker" aria-controls="options-picker">
                 <span class="picker-arrow"></span>
                 Options
               </a>
         
-              <div class="picker">
-                <ul>
+              <div class="picker" tabindex="-1">
+                <ul id="options-picker">
                   <li>
                     <a href="all.html">View on single page</a>
                   </li>

filename: tools/doc/html.mjs
patch: @@ -527,11 +527,11 @@ function altDocs(filename, docCreated, versions) {
 
   return list ? `
     <li class="picker-header">
-      <a href="#">
+      <a href="#alt-docs" aria-controls="alt-docs">
         <span class="picker-arrow"></span>
         Other versions
       </a>
-      <div class="picker"><ol id="alt-docs">${list}</ol></div>
+      <div class="picker" tabindex="-1"><ol id="alt-docs">${list}</ol></div>
     </li>
   ` : '';
 }
@@ -557,12 +557,12 @@ function gtocPicker(id) {
 
   return `
     <li class="picker-header">
-      <a href="#">
+      <a href="#gtoc-picker" aria-controls="gtoc-picker">
         <span class="picker-arrow"></span>
         Index
       </a>
 
-      <div class="picker">${gtoc}</div>
+      <div class="picker" tabindex="-1" id="gtoc-picker">${gtoc}</div>
     </li>
   `;
 }
@@ -574,12 +574,12 @@ function tocPicker(id, content) {
 
   return `
     <li class="picker-header">
-      <a href="#">
+      <a href="#toc-picker" aria-controls="toc-picker">
         <span class="picker-arrow"></span>
         Table of contents
       </a>
 
-      <div class="picker">${content.tocPicker}</div>
+      <div class="picker" tabindex="-1">${content.tocPicker.replace('<ul', '<ul id="toc-picker"')}</div>
     </li>
   `;
 }