Browse Source

Doc: Replace contents frame's tables with list elements.

Should help screen readers better understand the hierarchy.
Also added ARIA attributes on the collapsible items.
Jordan Russell 3 days ago
parent
commit
494480a666
3 changed files with 80 additions and 70 deletions
  1. 12 12
      ISHelp/ISHelpGen/ISHelpGen.dpr
  2. 51 40
      ISHelp/Staging/contents.css
  3. 17 18
      ISHelp/Staging/contents.js

+ 12 - 12
ISHelp/ISHelpGen/ISHelpGen.dpr

@@ -15,7 +15,7 @@ uses
   PathFunc in '..\..\Components\PathFunc.pas';
 
 const
-  Version = '1.17';
+  Version = '1.18';
 
   XMLFileVersion = '1';
 
@@ -689,8 +689,8 @@ var
 
   procedure AddLeaf(const Title, TopicName: String);
   begin
-    SL.Add(Format('<tr><td><img src="images/contentstopic.svg" alt="" /></td>' +
-      '<td><a href="%s" target="bodyframe">%s</a></td></tr>',
+    SL.Add(Format('<li><a href="%s" target="bodyframe">' +
+      '<img src="images/contentstopic.svg" alt="" /><span>%s</span></a></li>',
       [EscapeHTML(GenerateTopicLink(TopicName, '')), EscapeHTML(Title)]));
   end;
 
@@ -698,17 +698,14 @@ var
   var
     I: Integer;
   begin
-    SL.Add('<table>');
     for I := 0 to SetupDirectives.Count-1 do
       AddLeaf(SetupDirectives[I], GenerateSetupDirectiveTopicName(SetupDirectives[I]));
-    SL.Add('</table>');
   end;
 
   procedure HandleNode(const ParentNode: IXMLNode);
   var
     Node: IXMLNode;
   begin
-    SL.Add('<table>');
     Node := ParentNode.FirstChild;
     while Assigned(Node) do begin
       if not IsWhitespace(Node) then begin
@@ -716,15 +713,17 @@ var
           elContentsHeading:
             begin
               Inc(CurHeadingID);
-              SL.Add(Format('<tr id="nodecaption_%d"><td><img id="nodeimg_%d" src="images/contentsheadopen.svg" alt="&gt;&nbsp;" onclick="toggle_node(%d);" /></td>' +
-                '<td><a href="javascript:toggle_node(%d);">%s</a></td></tr>',
-                [CurHeadingID, CurHeadingID, CurHeadingID, CurHeadingID, EscapeHTML(Node.Attributes['title'])]));
-              SL.Add(Format('<tr id="nodecontent_%d"><td></td><td>', [CurHeadingID]));
+              SL.Add(Format('<li>' +
+                '<a href="javascript:toggle_node(%d);" aria-controls="nodecontent_%d" aria-expanded="true">' +
+                '<img src="images/contentsheadopen.svg" alt="'#$25BC' " />' +
+                '<span>%s</span></a>',
+                [CurHeadingID, CurHeadingID, EscapeHTML(Node.Attributes['title'])]));
+              SL.Add(Format('<ul id="nodecontent_%d">', [CurHeadingID]));
               if Node.Attributes['title'] = '[Setup] section directives' then
                 HandleSetupDirectivesNode
               else
                 HandleNode(Node);
-              SL.Add('</td></tr>');
+              SL.Add('</ul></li>');
             end;
           elContentsTopic:
             AddLeaf(Node.Attributes['title'], Node.Attributes['topic']);
@@ -734,7 +733,6 @@ var
       end;
       Node := Node.NextSibling;
     end;
-    SL.Add('</table>');
   end;
 
 var
@@ -744,7 +742,9 @@ begin
   SL := TStringList.Create;
   try
     CurHeadingID := 0;
+    SL.Add('<ul>');
     HandleNode(ContentsNode);
+    SL.Add('</ul>');
 
     TemplateSL := TStringList.Create;
     try

+ 51 - 40
ISHelp/Staging/contents.css

@@ -1,6 +1,6 @@
 /*
   Inno Setup
-  Copyright (C) 1997-2024 Jordan Russell
+  Copyright (C) 1997-2025 Jordan Russell
   Portions by Martijn Laan
   For conditions of distribution and use, see LICENSE.TXT.
 
@@ -64,45 +64,56 @@ INPUT {
 
 #tabbody-contents {
 	overflow: auto;
-	padding: 4px;
-}
-#tabbody-contents A {
-	/* Using inline-block because otherwise, second line of wrapped text
-	   doesn't line up with first, and on IE6, the focus rect doesn't extend
-	   all the way to the edges of Highlight area */
-	display: inline-block;
-	padding: 1px 2px;
-}
-#tabbody-contents A:link,
-#tabbody-contents A:visited {
-	color: inherit;
-	background-color: transparent;
-	text-decoration: none;
-}
-#tabbody-contents A:hover {
-	color: var(--link-hover-fg-color);
-	text-decoration: underline;
-}
-#tabbody-contents A.selectedlink:link,
-#tabbody-contents A.selectedlink:visited,
-#tabbody-contents A.focusedlink:link,
-#tabbody-contents A.focusedlink:visited {
-	color: inherit;
-	background-color: var(--selected-bg-color);
-	text-decoration: none;
-	border-radius: 4px;
-}
-#tabbody-contents TABLE {
-	border-collapse: collapse;
-}
-#tabbody-contents TD {
-	padding: 0 0 1px 0;
-}
-#tabbody-contents IMG {
-	width: 16px;
-	height: 16px;
-	border-style: none;
-	padding-right: 2px;
+
+	& > ul {
+		/* A's margin-block-end collapses with this margin */
+		margin: 4px;
+		padding: 0;
+
+		& ul {
+			margin: 0;
+			padding: 0;
+			padding-inline-start: 20px;
+		}
+	}
+	& li {
+		display: block;
+	}
+	& a:any-link {
+		display: flex;
+		inline-size: fit-content;
+		align-items: center;
+		gap: 2px;
+		margin-block-end: calc(2rem/16);
+		color: inherit;
+		background-color: transparent;
+		text-decoration: none;
+
+		&:hover {
+			color: var(--link-hover-fg-color);
+			text-decoration: underline;
+		}
+		&.selectedlink, &.focusedlink {
+			color: inherit;
+			text-decoration: none;
+
+			& > span {
+				background-color: var(--selected-bg-color);
+				border-radius: 4px;
+			}
+		}
+		& > img {
+			/* The images are 16x16, but using "min-" in case the alt text is shown and it's larger */
+			min-width: 16px;
+			min-height: 16px;
+			/* If the alt text is wider than 16px, don't allow the element to be shrunk down to min-width */
+			flex-shrink: 0;
+		}
+		& > span {
+			padding-inline: 2px;
+			padding-block: 1px;
+		}
+	}
 }
 
 

+ 17 - 18
ISHelp/Staging/contents.js

@@ -47,14 +47,9 @@ function ensure_elements_visible(elementTop, elementBottom)
 
 	const scrollerElement = document.getElementById("tabbody-contents");
 
-	let yTop = get_absolute_top(elementTop);
-	let yBottom = get_absolute_top(elementBottom) + elementBottom.offsetHeight;
-
-	// Subtract 1 from yTop so that if elementTop contains a link with text that starts at
-	// exactly yTop, the link's focus rectangle won't get chopped off on Firefox (3.x),
-	// where focus rectangles are inflated by 1px (unlike IE).
-	// (Adding 1 to yBottom isn't necessary since our TDs have 1px of bottom padding.)
-	if (yTop > 0) yTop--;
+	// Inflate by 2 pixels to ensure that focus rectangles are fully visible
+	let yTop = get_absolute_top(elementTop) - 2;
+	let yBottom = get_absolute_top(elementBottom) + elementBottom.offsetHeight + 2;
 
 	// Make yTop and yBottom relative to the top of the visible client area
 	const scrollerTop = get_absolute_top(scrollerElement) + scrollerElement.scrollTop;
@@ -75,19 +70,24 @@ function ensure_elements_visible(elementTop, elementBottom)
 
 function toggle_node(id)
 {
-	var objContent = document.getElementById("nodecontent_" + id);
-	var expanding = (objContent.style.display == "none");
-	objContent.style.display = expanding ? "" : "none";
+	const contentElement = document.getElementById("nodecontent_" + id);
+	const itemElement = contentElement.parentElement;
+	const linkElement = itemElement.querySelector(":scope > a");
+	const imageElement = linkElement.querySelector(":scope > img");
+
+	const expanding = (contentElement.style.display === "none");
+	contentElement.style.display = expanding ? "" : "none";
+
+	linkElement.setAttribute("aria-expanded", expanding);
 
-	document.getElementById("nodeimg_" + id).src =
-		expanding ? "images/contentsheadopen.svg" : "images/contentsheadclosed.svg";
+	imageElement.src = expanding ? "images/contentsheadopen.svg" : "images/contentsheadclosed.svg";
+	imageElement.alt = expanding ? "\u25BC " : "\u25B6 ";
 
 	if (expanding) {
 		// Scroll expanded items into view. This is similar to calling scrollIntoView() but
 		// doesn't do any scrolling if the items are already fully visible.
 
-		var objCaption = document.getElementById("nodecaption_" + id);
-		ensure_elements_visible(objCaption, objContent);
+		ensure_elements_visible(itemElement, itemElement);
 	}
 }
 
@@ -141,9 +141,8 @@ function set_selected_node(newSel)
 			}
 		}
 
-		// Then scroll the node's parent TR into view
-		p = curSelectedNode.parentNode.parentNode;
-		ensure_elements_visible(p, p);
+		// Then scroll the node's A element into view
+		ensure_elements_visible(curSelectedNode, curSelectedNode);
 	}
 }