ade21ee4e0109c70e29860d5393306e48e6a44d3
lrnassar
  Mon Mar 16 16:38:58 2026 -0700
Adding keyboard navigation accessibility to the Genome Browser menu bar and all main CGI pages. Converts non-focusable menu bar span elements to keyboard-accessible buttons using the W3C disclosure navigation pattern, adds aria-expanded state for screen readers, skip navigation link, and main content landmark. Also adds a Keyboard Navigation section to the accessibility page with screenshots. refs #37252

diff --git src/hg/lib/web.c src/hg/lib/web.c
index b614bba99d1..829fe9d9214 100644
--- src/hg/lib/web.c
+++ src/hg/lib/web.c
@@ -233,30 +233,31 @@
 
     printf("</HEAD>\n");
     printBodyTag(stdout);
     htmlWarnBoxSetup(stdout);// Sets up a warning box which can be filled with errors as they occur
     puts(commonCssStyles());
     }
 
 /* Put up the hot links bar. */
 
 char *menuStr = menuBar(theCart, db);
 if(menuStr)
     {
     puts(menuStr);
     }
 
+puts("<main id=\"mainContent\">");
 webStartSectionTables();
 
 if (withLogo)
     {
     puts("<TR><TH COLSPAN=1 ALIGN=\"left\">");
     if (isEncode)
 	{
 	puts("<A HREF=\"http://www.genome.gov/10005107\" TARGET=\"_BLANK\">"
 	     "<IMG SRC=\"../images/ENCODE_scaleup_logo.png\" height=50 ALT=\"ENCODE Project at NHGRI\">"
 	     "</A>");
 	puts("<IMG SRC=\"../images/encodeDcc.jpg\" ALT=\"ENCODE Project at UCSC\">");
 	}
     else
 	{
 	puts("<IMG SRC=\"../images/title.jpg\">");
@@ -470,45 +471,47 @@
 
 void webEndSectionTables()
 /* Finish with section tables (but don't do /BODY /HTML like
  * webEnd does. */
 {
 webEndSection();
 puts("</TD></TR></TABLE>\n");
 }
 
 void webEnd()
 /* output the footer of the HTML page */
 {
 if(!webInTextMode)
     {
     webEndSectionTables();
+    puts("</main>");
 #ifndef GBROWSE
     googleAnalytics();
 #endif /* GBROWSE */
     jsInlineFinish();
     puts( "</BODY></HTML>");
     webPopErrHandlers();
     }
 }
 
 void webEndExtra(char *footer)
 /* output the footer of the HTML page with extra footer as desired */
 {
 if(!webInTextMode)
     {
     webEndSectionTables();
+    puts("</main>");
 #ifndef GBROWSE
     googleAnalytics();
 #endif /* GBROWSE */
     jsInlineFinish();
     if (footer)
 	puts(footer);
     puts( "</BODY></HTML>");
     webPopErrHandlers();
     }
 }
 
 static void webStartGbOptionalBanner(struct cart *cart, char *db, char *title, boolean doBanner, 
                                 boolean hgGateway)
 /* Start HTML with new header and footer design by JWest.  
    Optionally display banner above menubar.  Use flag with hgGateway, till that is migrated.
@@ -539,43 +542,45 @@
           , title);
     }
 freeMem(csp);
 
 webPushErrHandlersCartDb(cart, db);
 htmlWarnBoxSetup(stdout);
 
 // Add hotlinks bar
 char *navBar = menuBar(cart, db);
 if (navBar)
     {
     puts(navBar);
     // Override nice-menu.css's menu background and fonts:
     webIncludeResourceFile("gbAfterMenu.css");
     }
+puts("<main id=\"mainContent\">");
 webHeadAlreadyOutputed = TRUE;
 errAbortSetDoContentType(FALSE);
 }
 
 void webStartGbNoBanner(struct cart *cart, char *db, char *title)
 /* Start HTML with new header and footer design by jWest, but no banner */
 {
 webStartGbOptionalBanner(cart, db, title, FALSE, FALSE);
 }
 
 void webEndGb()
 /* End HTML that was started with webStartJWest. */
 {
+puts("</main>");
 googleAnalytics();
 jsInlineFinish();
 puts("</body></html>");
 webPopErrHandlers();
 }
 
 void webStartJWest(struct cart *cart, char *db, char *title)
 /* Start HTML with new banner and footer design by jWest (with modifications). */
 {
 webStartGbOptionalBanner(cart, db, title, TRUE, TRUE);
 }
 
 void webEndJWest()
 /* End HTML that was started with webStartJWest. */
 {