<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>Peter Krautzberger</title>
	<subtitle> · on the web</subtitle>
	<link href="https://www.peterkrautzberger.org/atom.xml" rel="self"/>
	<link href="https://www.peterkrautzberger.org/"/>
	<updated>2026-09-16T00:00:00Z</updated>
	<id>https://www.peterkrautzberger.org/</id>
	<author>
		<name>Peter Krautzberger</name>
		<email>p.krautzberger@gmail.com</email>
	</author>
	
	<entry>
		<title>(Print) &quot;Algorithm Layout&quot; in HTML and CSS</title>
		<link href="https://www.peterkrautzberger.org/0217/"/>
		<updated>2022-07-26T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0217/</id>
		<content type="html">&lt;p&gt;July 2022. I started this piece well over three years ago (some time before June 2019 in any case). As it &lt;s&gt;came along&lt;/s&gt; grew out of proportion, I had tried turning it into three pieces but never got far enough to start publishing it. Then I tried to recombine them (some time in 2020?) in the hopes of finally pushing them out but didn&#39;t finish the last few pieces. Then 2021 was worse than 2020 and now it&#39;s 2022. So I&#39;m thinking I will make this a new kind of piece around here: a partially complete piece of writing that may (or more likely: may not) get edits and updates. Let&#39;s jump in then.&lt;/p&gt;
&lt;p&gt;A while ago I started to think about &amp;quot;algorithm layout&amp;quot;. Not syntax highlighting of source code (which &lt;a href=&quot;https://prismjs.com/&quot;&gt;prism.js&lt;/a&gt; handles nicely) but the kind of print traditions captured in the following image.&lt;/p&gt;
&lt;img src=&quot;https://www.peterkrautzberger.org/assets/2020/euclid.png&quot; alt=&quot;Euclid&#39;s algorithm typeset using LaTeX&#39;s algorithmicx package&quot; /&gt;
&lt;h2&gt;Part 1. Prior art and the problem of content conversion&lt;/h2&gt;
&lt;p&gt;If you&#39;re familiar with LaTeX packages such as algorithmicx or algorithm2e you&#39;ll recognize this particular style of layout. As the names of those packages imply, this type of content is more about the abstract algorithms and less about implementation details in a specific language.&lt;/p&gt;
&lt;p&gt;Realizing this specific style on the web is an interesting challenge. On the one hand, it&#39;s a problem of realizing print layout traditions on the web, an area I deal with professionally on a daily basis. On the other hand, there&#39;s an authoring problem behind this: realizing print layout is one thing, realizing it in a way that allows for a good (web!) reading &lt;em&gt;and&lt;/em&gt; authoring experience is another.&lt;/p&gt;
&lt;p&gt;When I started this side project, I had only had a quick look at some tools that solve this problem and before I wanted to write about what I did, I thought I should take another, closer look at them to provide context (and check my own work). I&#39;m glad I didn&#39;t look too closely earlier since that may have sent me off in a completely different direction. It also reminded me of the pitfalls of authoring, in particular print content and conversion.&lt;/p&gt;
&lt;h3&gt;Prior art 1: algorithmicx&lt;/h3&gt;
&lt;p&gt;To pick a starting point, let&#39;s use a classic in every sense of the word: Euclid&#39;s algorithm.&lt;/p&gt;
&lt;p&gt;Here&#39;s what you&#39;d write using the &lt;a href=&quot;https://ctan.org/pkg/algorithmicx&quot;&gt;algorithmicx&lt;/a&gt; LaTeX package.&lt;/p&gt;
&lt;pre class=&quot;language-latex&quot;&gt;&lt;code class=&quot;language-latex&quot;&gt;&lt;span class=&quot;token function selector&quot;&gt;\begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\caption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Euclid&#39;s algorithm&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;euclid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function selector&quot;&gt;\Procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Euclid&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token equation string&quot;&gt;$a,b$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;The g.c.d. of a and b&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; a&lt;span class=&quot;token equation-command regex&quot;&gt;\bmod&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\While&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\not&lt;/span&gt;=0$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;We have the answer if r is 0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$a&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$b&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; r$&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; a&lt;span class=&quot;token equation-command regex&quot;&gt;\bmod&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\EndWhile&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;euclidendwhile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token function selector&quot;&gt;\textbf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;return&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$b$&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;The gcd is b&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function selector&quot;&gt;\EndProcedure&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function selector&quot;&gt;\end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and you&#39;d get the image from earlier. It&#39;s fun to note that this source is straight from the package documentation, including inconsistencies (such as a mismatch of math mode in states and comments).&lt;/p&gt;
&lt;p&gt;Let&#39;s naively HTML-ify this:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;caption&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;euclid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Euclid&#39;s algorithm&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;caption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;algorithmic&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;numberLineSkip&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Euclid&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a,b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The g.c.d. of a and b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a mod b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ≠ 0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We have the answer if r is 0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a ← b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b ← r&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a mod b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;return&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The gcd is b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That seems fairly straight forward even if the label &amp;quot;euclidendwhile&amp;quot; has been lost.&lt;/p&gt;
&lt;p&gt;I&#39;d like to focus on two problems&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how to get from here to something we can style easily (for the web)&lt;/li&gt;
&lt;li&gt;while keeping things relatively easy to style (and customize/extend)&lt;/li&gt;
&lt;li&gt;while keeping things relatively easy to author&lt;/li&gt;
&lt;li&gt;while making this accessible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or put differently: how to move from content conversion to content authoring.&lt;/p&gt;
&lt;h3&gt;Pior art 2: latexml&lt;/h3&gt;
&lt;p&gt;While I personally wouldn&#39;t recommend it, latexml is an interesting option to consider because it is so heavily focuses on extracting semantic structure from print documents.&lt;/p&gt;
&lt;p&gt;Here&#39;s a codepen embedding of what you&#39;d get from it:&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-user=&quot;pkra&quot; data-slug-hash=&quot;8c2d4358f4fd67d595c78e3e287af6e8&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-pen-title=&quot;Algorithms via LaTeXML&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/pkra/pen/8c2d4358f4fd67d595c78e3e287af6e8&quot;&gt;
  Algorithms via LaTeXML&lt;/a&gt; by Peter Krautzberger (&lt;a href=&quot;https://codepen.io/pkra&quot;&gt;@pkra&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;So to me this is a terrible mess of markup and layout but it&#39;s not surprising given what latexml focuses on. When you get into the business of generating HTML and CSS from things that are fairly far removed from HTML and CSS (here: long form print documents) you are bound to create such a mess of a markup. That&#39;s not a criticism; I think it&#39;s simply inevitable.&lt;/p&gt;
&lt;p&gt;After all, an author who uses latexml was not interested in authoring web content. Instead, they authored print content using LaTeX and then used a second piece of software (filled to the brim with heuristics) to generate a close approximation of the print product in a custom XML. In a third step, they created HTML and CSS from that.&lt;/p&gt;
&lt;p&gt;Nevertheless, there is a lot of noise in this which may make it hard to explore the example. Here&#39;s another codepen embedding with a more minimal version of the latexml output that has (hopefully) less noise and still exhibits the inherent issues I see with this approach towards authoring of web content.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-user=&quot;pkra&quot; data-slug-hash=&quot;f6c5ff9db52d4e0f6cc903b288c21741&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-pen-title=&quot;Algorithms via LaTeXML, reduced&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/pkra/pen/f6c5ff9db52d4e0f6cc903b288c21741&quot;&gt;
  Algorithms via LaTeXML, reduced&lt;/a&gt; by Peter Krautzberger (&lt;a href=&quot;https://codepen.io/pkra&quot;&gt;@pkra&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;A quick list of things that jump out when going through this.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;functional/atomic CSS (in the tailwind or tachyon sense), e.g., a border-top class, a small-caps class, a bold class - a natural concept for content conversion from an only potentially structured format like TeX.&lt;/li&gt;
&lt;li&gt;inline styles - usually necessary when print layout is mimicked in detail (though somewhat surprising in this particular case which looks like it could be replaced by a functional class if you think about it from a TeX point of view)&lt;/li&gt;
&lt;li&gt;the weird case of line 3 - it looks like a bug that the comment is wrapped in the bold part of the &lt;code&gt;while...do&lt;/code&gt; construct and thus needs a font weight override (but maybe it&#39;s a weird thing in the LaTeX package)&lt;/li&gt;
&lt;li&gt;right-floating comments can overlap on small viewports&lt;/li&gt;
&lt;li&gt;line counters and comment markers are yucky (much worse in the original)&lt;/li&gt;
&lt;li&gt;divs and spans galore. Each &amp;quot;line&amp;quot; is a div but the line counter and state are wrapped together&lt;/li&gt;
&lt;li&gt;and the &amp;quot;highlight&amp;quot;: repeated nbsp&#39;s to create indentation. Yikes, the 90s want their spacer GIFs back. (But again, this might be a faithful conversion from how the LaTeX package realizes the print layout.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;m not trying to complain too much here. Again, this is mostly what you&#39;d expect when you build a tool like latexml. Print is just a crappy format to generate web content from and even good LaTeX can&#39;t save you from that fact.&lt;/p&gt;
&lt;h3&gt;Prior art 2: pseudocode&lt;/h3&gt;
&lt;p&gt;The next example is a probably less known JS library called pseudocode.js.&lt;/p&gt;
&lt;p&gt;Here&#39;s again a codepen embedding of what we might get.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-user=&quot;pkra&quot; data-slug-hash=&quot;0d056d577e3891483c4e60adf83db41c&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-pen-title=&quot;pseudocode.js&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/pkra/pen/0d056d577e3891483c4e60adf83db41c&quot;&gt;
  pseudocode.js&lt;/a&gt; by Peter Krautzberger (&lt;a href=&quot;https://codepen.io/pkra&quot;&gt;@pkra&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;Cleaning it up a bit seems harder given. Maybe that&#39;s because it is closer to thinking in terms of web design (as opposed to print-reproduction). But we can delete the equation layout mess (which is weirdly inconsistent in the algorithmicx source anyway).&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;html,result&quot; data-user=&quot;pkra&quot; data-slug-hash=&quot;5d3a2662d84306e0dac071528e48d8ca&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-pen-title=&quot;pseudocode.js, reduced&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/pkra/pen/5d3a2662d84306e0dac071528e48d8ca&quot;&gt;
  pseudocode.js, reduced&lt;/a&gt; by Peter Krautzberger (&lt;a href=&quot;https://codepen.io/pkra&quot;&gt;@pkra&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;Again some random observations.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;moar divs. Not even a figure/figcaption (but a class &lt;code&gt;with-caption&lt;/code&gt;); that&#39;s a bit disappointing.&lt;/li&gt;
&lt;li&gt;decent line-wrapping on small screens is nice&lt;/li&gt;
&lt;li&gt;class names are less functional and more semantic (in the sense of layout), e.g., ps-line. That kind of intent seems appropriate.&lt;/li&gt;
&lt;li&gt;odd inline styles
&lt;ul&gt;
&lt;li&gt;canceling things out: &lt;code&gt;style=&amp;quot;text-indent:-1.2em;padding-left:1.2em;&amp;quot;&lt;/code&gt; is a funny extreme but probably just a side effect of the overall strategy to combine margin-right with negative text-indent.&lt;/li&gt;
&lt;li&gt;rounding issues &lt;code&gt;style=&amp;quot;margin-left:1.7999999999999998em;&amp;quot;&lt;/code&gt; - just a bug but makes me wonder about code quality (ok, not really).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;relative positioning. Feels weird to me for this kind of layout (of course it&#39;s at the heart of how this particular approach works)&lt;/li&gt;
&lt;li&gt;nested substructures - that is right up my alley (more on that later hopefully). Stacking margin-left is neat but comes at the price of negative text-indent shenanigans for the line counters; I don&#39;t get why the margin is inline CSS rather than a class but there&#39;s possibly a decent reason for that (e.g., authoring).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Coda Part 1.&lt;/h3&gt;
&lt;p&gt;In both cases, the main reason why I find these approaches lacking lies, I think, in the fact that they are the result of (print) content conversion. The disconnect between the authoring and the layout is too large to get one in touch with the other. This is a loss for both sides: the layout gets messy and the author comes no closer to understanding the medium they author for.&lt;/p&gt;
&lt;p&gt;A typical side effect of this shows when you want to modify the output: neither output can be re-arranged efficiently. Usually, all you can do is go to the source, change it, and convert it again. We&#39;llcome back to that later.&lt;/p&gt;
&lt;p&gt;To be clear, I&#39;m not saying that some of the &amp;quot;bad&amp;quot; design decisions could be avoided; I definitely haven&#39;t found a way around every pain point. But my experience has been that the &lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_the_instrument&quot;&gt;law of the instrument&lt;/a&gt; is at the heart of the problem. Content conversion inevitably leads to certain types of hacks (such as meaningless DOM structures, class names and inline styles) to allow authors customizations without them having to think about the actual medium. If it&#39;s content conversion from a format meant for print, this is problem exacerbated because the media and their traditions often match poorly. But once those hacky instruments are in the tool belt, it&#39;s hard not to use them and you end up with hacks all the way down.&lt;/p&gt;
&lt;p&gt;In the next part, I&#39;ll explore a different approach, also thinking about it from the perspective of the source material but hopefully with some level of detachment. After that, perhaps I&#39;ll find time to jot down some ideas on how to move towards authoring. But this piece has been more fun than I thought it would be; it&#39;s been interesting to dive a little deeper into these two after trying things on my own; good to see similarities and differences.&lt;/p&gt;
&lt;h2&gt;Part 2. A first draft&lt;/h2&gt;
&lt;p&gt;Let&#39;s go back to the beginning.&lt;/p&gt;
&lt;img src=&quot;https://www.peterkrautzberger.org/assets/2020/euclid.png&quot; alt=&quot;Euclid&#39;s algorithm typeset using LaTeX&#39;s algorithmicx package&quot; /&gt;
&lt;p&gt;In Part 1 I started at the wrong end, so to speak, and I looked at solutions that try to be keep the spirit of the print source, both in authoring &amp;quot;convenience&amp;quot; and layout design. I also outlined what I think of as issues and limitations of such (print) content conversion.&lt;/p&gt;
&lt;p&gt;Still, it is painfully fascinating. I wanted to find another way of dealing with this problem and, ideally, learning (with detachement) from the print authoring experience to enable web authoring.&lt;/p&gt;
&lt;h3&gt;Back to the source: algorithmicx (again)&lt;/h3&gt;
&lt;p&gt;Again, we keep to the classics and Euclid&#39;s algorithm.&lt;/p&gt;
&lt;p&gt;Here&#39;s what you&#39;d write in algorithmicx.&lt;/p&gt;
&lt;pre class=&quot;language-latex&quot;&gt;&lt;code class=&quot;language-latex&quot;&gt;&lt;span class=&quot;token function selector&quot;&gt;\begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\caption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Euclid&#39;s algorithm&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;euclid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\begin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function selector&quot;&gt;\Procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Euclid&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token equation string&quot;&gt;$a,b$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;The g.c.d. of a and b&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; a&lt;span class=&quot;token equation-command regex&quot;&gt;\bmod&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\While&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\not&lt;/span&gt;=0$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;We have the answer if r is 0&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$a&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$b&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; r$&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$r&lt;span class=&quot;token equation-command regex&quot;&gt;\gets&lt;/span&gt; a&lt;span class=&quot;token equation-command regex&quot;&gt;\bmod&lt;/span&gt; b$&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\EndWhile&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;euclidendwhile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token function selector&quot;&gt;\State&lt;/span&gt; &lt;span class=&quot;token function selector&quot;&gt;\textbf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;return&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token equation string&quot;&gt;$b$&lt;/span&gt;&lt;span class=&quot;token function selector&quot;&gt;\Comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;The gcd is b&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function selector&quot;&gt;\EndProcedure&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function selector&quot;&gt;\end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token function selector&quot;&gt;\end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and you&#39;d get the image from earlier. And here&#39;s our naive HTML-ification again:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;caption&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;euclid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Euclid&#39;s algorithm&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;caption&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;algorithmic&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;numberLineSkip&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Euclid&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a,b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The g.c.d. of a and b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a mod b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ≠ 0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We have the answer if r is 0&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a ← b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b ← r&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a mod b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;while&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;return&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The gcd is b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;comment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;procedure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;algorithmic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;algorithm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some random thoughts when looking at this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;we should obviously use a figure with figcaption element for HTML; maybe sprinkle some aria-roledescription in there.&lt;/li&gt;
&lt;li&gt;CSS pseudo-content seems an obvious choice for &amp;quot;Procedure&amp;quot; and &amp;quot;While...do / done&amp;quot;.&lt;/li&gt;
&lt;li&gt;why don&#39;t those comments align? Seems worth changing.&lt;/li&gt;
&lt;li&gt;As I mentioned already, I like the nesting (Procedure/EndProcedure, While/EndWhile). We should find a way to keep it. But that also seems hard thanks to those line counters.&lt;/li&gt;
&lt;li&gt;This layout is obviously a case for grid&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;head on&lt;/h3&gt;
&lt;p&gt;So let&#39;s try not to overthink this, let&#39;s try not to take more than inspiration from the source, let&#39;s try to think about the layout, its meaning, and how it could work &lt;a href=&quot;https://frankchimero.com/blog/2015/the-webs-grain/&quot;&gt;with the web&#39;s grain&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What do we need at first sight?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;multiple lines of 3 columns: counter, content, comment
&lt;ul&gt;
&lt;li&gt;get nice alignment&lt;/li&gt;
&lt;li&gt;try to avoid tables (for both layout and accessibility but it&#39;s complicated; maybe more on that later)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;a way to handle begin/end type of things (subroutines, loops etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, the obvious modern choice for two dimensional layouts is CSS grid. And CSS counters should help as well:&lt;/p&gt;
&lt;style&gt;
section {
  margin-bottom: 1em;
  overflow: auto;
}

.sc {
  font-variant: small-caps;
}
&lt;/style&gt;
&lt;style&gt;.algorithm {
  counter-reset: algolinecounter;
  display: grid;
  grid-template-columns: repeat(3, max-content);
  grid-column-gap: 1em;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.algorithm&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;counter-reset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; algolinecounter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;3&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; max-content&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;grid-column-gap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To try it out, here&#39;s a dummy set of two &amp;quot;lines&amp;quot;, first as part of the page then as code.&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
    &lt;div&gt;Counter&lt;/div&gt;
    &lt;div&gt;Content&lt;/div&gt;
    &lt;div&gt;Comment&lt;/div&gt;
    &lt;div&gt;Counter&lt;/div&gt;
    &lt;div&gt;Content&lt;/div&gt;
    &lt;div&gt;Comment&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Counter&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Comment&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Counter&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Comment&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing really to see yet, so let&#39;s move on.&lt;/p&gt;
&lt;p&gt;To handle our line counters, we use the counters (obviously) but we also get some critical help from grid: since we might not have comments, we don&#39;t know whether the line is full. But that&#39;s not a problem with grid - we can just force our counter to always start in the first column&lt;/p&gt;
&lt;style&gt;
.counter {
  grid-column-start: 1;
  counter-increment: algolinecounter;
}

.counter::before {
  content: counter(algolinecounter) &#39;: &#39;;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.counter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;grid-column-start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;counter-increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; algolinecounter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.counter::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;algolinecounter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;: &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To try it out, here&#39;s two lines with only the second on having a comment. Again, first as part of the page, then as source.&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div&gt;Content&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div&gt;Content&lt;/div&gt;
&lt;div&gt;Comment&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Comment&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a good point to think about accessiblity. CSS generated content is of course ok but we might want to add some ARIA attributes to indicate the visual effect of starting a line; then again, maybe that tends to be clear from context and we shouldn&#39;t add noise. It&#39;s tempting to aria-hide the counters and use table roles which would gives AT users the advantage of table walkers - a grid role would be more appropriate but then we&#39;d have to write our own walker so definitely out of scope for a first draft. Anyway, we don&#39;t have to do this right now, we just have to keep it in mind.&lt;/p&gt;
&lt;p&gt;Next up, let&#39;s deal with the other fixed property: comments. This is simple anyway:&lt;/p&gt;
&lt;style&gt;
.comment {
  margin-left: 3em;
}
.comment::before {
  content: &#39;▷ &#39;;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.comment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.comment::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;▷ &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks nice enough but we should probably hide that from non-visual users. We could indicate that it&#39;s a comment but again it might be enough to grok this from context. After all, context is all we have visually. Let&#39;s take a look, in the page and source:&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div&gt;Content&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;Comment, longer&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div&gt;Content&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;Comment&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Comment, longer&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Content&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Comment&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oooh, look at that alignment! Isn&#39;t it pretty?&lt;/p&gt;
&lt;p&gt;So, 9 lines of CSS and our basic layout is done. Nice.&lt;/p&gt;
&lt;p&gt;Since this is a first draft, let&#39;s continue not to over think this. The begin/end routines seem to always have, well, a beginning and an end. So let&#39;s just make that happen.&lt;/p&gt;
&lt;style&gt;
.while::before,
.while::after {
  font-weight: bolder;
}
.while__start::before {
  content: &#39;while &#39;;
}
.while__start::after {
  content: &#39; do&#39;;
}
.while__end::before {
  content: &#39;end while&#39;;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.while::before,&lt;br /&gt;.while::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bolder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.while__start::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;while &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.while__start::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; do&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.while__end::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;end while&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In action and as source:&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div&gt;We do some work&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end&quot;&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We do some work&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final piece doesn&#39;t look very nice but neither does the original source.&lt;/p&gt;
&lt;p&gt;There&#39;s a worse glitch though: if you look at the original, the part &amp;quot;inside&amp;quot; the loop is indented. This is slightly tricky since we don&#39;t have a good way of writing a selector for those kinds of elements. Luckily, this is a first draft and we&#39;re lazy, so let&#39;s just add a helper class.&lt;/p&gt;
&lt;style&gt;
.ml1 {
  margin-left: 1em;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.ml1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Problem solved - let&#39;s have a look:&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml1&quot;&gt;We do some work&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end&quot;&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We do some work&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Moving on to the last issue, nested subroutines, so let&#39;s copy and paste our loop.&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml1&quot;&gt;We do some work&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end&quot;&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We do some work&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But of course now we have the same margin problem all over again, so we don&#39;t think too hard and add another helper.&lt;/p&gt;
&lt;style&gt;
.ml2 {
  margin-left: 2em;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.ml2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can make it:&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml1 while while__start&quot;&gt;
    Some condition
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    Otherwise, we&#39;re done.
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml2&quot;&gt;We do some work&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml1 while while__end&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end&quot;&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml1 while while__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Some condition&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;        Otherwise, we&#39;re done.&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;We do some work&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml1 while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, it&#39;s clear that we have a lurking problem but luckily this was just a first draft and we can finish our example. Let&#39;s just add a procedure class as follows.&lt;/p&gt;
&lt;style&gt;
.procedure::before {
  font-weight: bolder;
}
.procedure__start::before {
  content: &#39;procedure &#39;;
}
.procedure__end::before {
  content: &#39;end procedure&#39;;
}
&lt;/style&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.procedure::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bolder&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.procedure__start::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;procedure &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;.procedure__end::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;end procedure&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it looks like we&#39;re there.&lt;/p&gt;
&lt;section class=&quot;algorithm&quot;&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;procedure procedure__start&quot;&gt;
    &lt;span class=&quot;sc&quot;&gt;Euclid&lt;/span&gt;(&lt;i&gt;a, b&lt;/i&gt;)
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    The g.c.d. of &lt;i&gt;a&lt;/i&gt; and &lt;i&gt;b&lt;/i&gt;
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;state ml1&quot;&gt;&lt;i&gt;r ← a &lt;/i&gt;mod&lt;i&gt; b&lt;/i&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__start ml1&quot;&gt;
    &lt;i&gt;r ≠ &lt;/i&gt;0
&lt;/div&gt;
&lt;div class=&quot;comment&quot;&gt;
    We have the answer if &lt;i&gt;r&lt;/i&gt; is 0
&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml2&quot;&gt;&lt;i&gt;a ← b&lt;/i&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;state ml2&quot;&gt;&lt;i&gt;b ← r&lt;/i&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml2&quot;&gt;&lt;i&gt;r ← a &lt;/i&gt;mod&lt;i&gt; b&lt;/i&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;while while__end  ml1&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;ml1&quot;&gt;&lt;strong&gt;return&lt;/strong&gt; &lt;i&gt;b&lt;/i&gt;&lt;/div&gt;
&lt;div role=&quot;region&quot; aria-label=&quot;comment&quot; class=&quot;comment&quot;&gt;
    The gcd is &lt;i&gt;b&lt;/i&gt;
&lt;/div&gt;
&lt;div role=&quot;region&quot; aria-label=&quot;line&quot; class=&quot;counter&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;procedure procedure__end&quot;&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;section&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;algorithm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;procedure procedure__start&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;sc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Euclid&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;(&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a, b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    The g.c.d. of &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; and &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;mod&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__start ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ≠ &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;0&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    We have the answer if &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; is 0&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;a ← b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state ml2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b ← r&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;r ← a &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;mod&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;while while__end  ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ml1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;return&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;strong&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;comment&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    The gcd is &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;b&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;region&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;line&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;procedure procedure__end&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;section&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Coda Part 2.&lt;/h3&gt;
&lt;p&gt;Not bad for a first draft. Grid makes so many things just easy, sprinkle some counters.&lt;/p&gt;
&lt;p&gt;There are a couple of glaring issues at this point&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;those helper classes feel like content conversion. Can we do better?&lt;/li&gt;
&lt;li&gt;grid soup - could subroutines be wrapped in a single element?&lt;/li&gt;
&lt;li&gt;responsivity - scrolling is not awesome.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inevitably, these will lead us into the weeds. But at least the web&#39;s weeds.&lt;/p&gt;
&lt;h2&gt;Part 3. But what is it&lt;/h2&gt;
&lt;p&gt;Let&#39;s go back to our starting point.&lt;/p&gt;
&lt;img src=&quot;https://www.peterkrautzberger.org/assets/2020/euclid.png&quot; alt=&quot;Euclid&#39;s algorithm typeset using LaTeX&#39;s algorithmicx package&quot; /&gt;
&lt;p&gt;In Part 1, I started at the wrong end, if you will: I looked at a TeX source and then at existing solutions that aim to stay close to print, both in authoring &amp;quot;convenience&amp;quot; and layout design. In Part 2, I went through a rought draft of realizing a simlar layout in CSS &amp;quot;properly&amp;quot;.&lt;/p&gt;
&lt;p&gt;Now, let&#39;s try to let go.&lt;/p&gt;
&lt;h3&gt;Forget the source, Luke.&lt;/h3&gt;
&lt;p&gt;What can we observe for this layout?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a title&lt;/li&gt;
&lt;li&gt;a bunch of numbered lines&lt;/li&gt;
&lt;li&gt;containing three distinct parts per line - counter, main block, comment block&lt;/li&gt;
&lt;li&gt;indentation where subgroups of lines have their main blocks indented&lt;/li&gt;
&lt;li&gt;some bold key words that match those subgroups&lt;/li&gt;
&lt;li&gt;some decorative top/bottom borders around the title and whole algorithm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first draft was trying to be so minimalistic that it skipped on the two primary semantic pieces: lines and subgroups of lines. Isn&#39;t that odd?&lt;/p&gt;
&lt;p&gt;So thinking about those two, where would we start?&lt;/p&gt;
&lt;h3&gt;Authoring with batteries included&lt;/h3&gt;
&lt;p&gt;There are a couple of conveniences when it comes to authoring that I&#39;d like to see preserved. The core problem is change: removing and injecting content into other content. You want it to magically work and of course it often doesn&#39;t. In particular &lt;strong&gt;copy&amp;amp;paste&lt;/strong&gt; is a key usability challenge that good authoring tools try to solve. Obviously, this is is a hard problem in general.&lt;/p&gt;
&lt;p&gt;It also leads to other factors such as good (automated) default behavior alongside customizability aka &amp;quot;macros&amp;quot;.&lt;/p&gt;
&lt;p&gt;The first challenge from the first draft is: how do we solve the indentation problem, i.e., how can we avoid the &lt;code&gt;.ml1&lt;/code&gt;, &lt;code&gt;.ml2&lt;/code&gt; etc classes. Because if we cannot solve that, authors will have to a) add those classes themselves and b) update them whenever they move or add something to an existing structure.&lt;/p&gt;
&lt;h3&gt;Coda&lt;/h3&gt;
&lt;p&gt;This coda marks where the &amp;quot;original&amp;quot; draft ended, i.e, the draft from which I started in June 2022 (though many edits were done in June / July 2022). Handling the indentation is really the only &amp;quot;hard&amp;quot; problem for this layout so I&#39;m not sure why I stopped here.&lt;/p&gt;
&lt;p&gt;I have a couple of codepens from that time that might be worth writing about. And then there&#39;s the solution I adopted at work to talk about (which, inevitably, is different). Let&#39;s stop here and see if I get around to those updates.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Thoughts on Print Equation Layout</title>
		<link href="https://www.peterkrautzberger.org/0218/"/>
		<updated>2022-09-17T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0218/</id>
		<content type="html">&lt;p&gt;This will possibly be another piece that will get updates in the future. Actually, I hope not but we&#39;ll see. This, above all, is for me. For me to gather my thoughts one (hopefully) last time. For me to deal with stupid garbage which has accumulated over the years. As for you, dear reader, I don&#39;t know if this is for you. I didn&#39;t write it for the handful of experts in this area; maybe for someone like my former self, back when I started writing mathematics on the web. Maybe for someone who knows the web and wonders what all that fuss with equations is always about. In any case here&#39;s a fair warning: I&#39;m not going into every detail so if I lose you along the way, remember these first few lines and go do something more fun, it&#39;s fine. Just don&#39;t be that guy (and it&#39;s invariably a guy) complaining about free stuff on the internet.&lt;/p&gt;
&lt;p&gt;This is also a lamentation. Nothing I write will change anything of what&#39;s to come. It is also (hopefully) a bit of mental cleansing. A few years ago I had strong burn-out symptoms due to working in this (as it turned out toxic) field. It takes a lot of effort for me to not be dragged down again. But one last time I want to try to write up where I&#39;ve arrived after thinking about equation layout on the web for over 10 years now.&lt;/p&gt;
&lt;p&gt;If I make a claim without substantiating links, it&#39;s most likely to avoid more burn out; you can ping me privately though. If I&#39;m giving just one or two examples, it&#39;s not because that&#39;s it but because this piece is very long already. [If you want deeply detailed research results, feel free to contact my company - we do that kind of thing.]&lt;/p&gt;
&lt;p&gt;Finally, I thank the kind people who read this piece before I pushed it live - your feedback was invaluable; I owe you one.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;10 years ago I joined the &lt;a href=&quot;https://www.mathjax.org/&quot;&gt;MathJax project&lt;/a&gt; which forced me to think a lot about how to put equations on web pages. Actually, I had already spent quite a bit of time during my PhD and postdoc years thinking (*cough* procrastinating) about the same thing, albeit from a different perspective.&lt;/p&gt;
&lt;p&gt;When I started writing on the web all 13 years ago, I quickly went through a phase I&#39;ve seen repeated by many people since:&lt;/p&gt;
&lt;p&gt;&amp;quot;oh, there&#39;s something called MathML, I should use that&amp;quot;&lt;/p&gt;
&lt;p&gt;followed by&lt;/p&gt;
&lt;p&gt;&amp;quot;oh this does not work at all, let&#39;s not use that&amp;quot;,&lt;/p&gt;
&lt;p&gt;usually followed by choosing jsMath (if you&#39;re old enough) and later MathJax because they ultimately remain - by far - the best tools for the job.&lt;/p&gt;
&lt;p&gt;After joining MathJax, my job was to profess the prowess of (read:blindly advertise for) MathML, so I did. That was largely because MathJax was formally part of Design Science at the time and they made money with MathML. Luckily, the project moved to the AMS where we could think about things more freely.&lt;/p&gt;
&lt;p&gt;When the whole Chrome 24 debacle happened, there was this one comment from a Googler (on Google+, naturally you might say). I can&#39;t find it anymore but what I recall them saying is: &amp;quot;maybe it&#39;s a good format but still a bad format for the web&amp;quot;. I kept coming back to that thought for a few years and, eventually, came to agree with it. (This piece is basically summarizing that.)&lt;/p&gt;
&lt;p&gt;In short, I realized that MathML is fundamentally a useful XML print publishing tool but a bad technology for the web. So I tried to pivot MathJax away from MathML and its toxic community towards actual web standards and their underlying principles. We wrote a whitepaper, got our sponsors on board, and started work on MathJax v3. (This is not what v3 became but that&#39;s ok. I left my position at MathJax around the first beta which was a thrill and I&#39;m grateful that Davide and Volker enabled me to leave with that big leap forward having begun.)&lt;/p&gt;
&lt;p&gt;Towards the end of that period, I wrote a number of pieces here, outlining MathML&#39;s many flaws and failures. But as I kept writing: it&#39;s not MathML&#39;s fault. MathML does what it&#39;s supposed to; but what it&#39;s supposed to do is a bad fit for the web. (Again, this piece is basically re-iterating those pieces. What do you expect from a recovering academic.)&lt;/p&gt;
&lt;p&gt;In the past 5 years, I&#39;ve worked primarily on the issue of converting full mathematical (LaTeX) documents to web formats. If you follow me on the fediverse, you may have seen me use the CW &amp;quot;TeX in real world production&amp;quot; sharing some of the &amp;quot;gems&amp;quot; from this experience.&lt;/p&gt;
&lt;p&gt;All of this has led me to realize that there&#39;s a &amp;quot;deeper&amp;quot; problem and I feel like it needs a (sort of) new name: print equation layout.&lt;/p&gt;
&lt;h2&gt;Framing&lt;/h2&gt;
&lt;p&gt;Framing is important. What I call &amp;quot;print equation layout&amp;quot; is more frequently called &amp;quot;math layout&amp;quot;, &amp;quot;formula layout&amp;quot;, or just &amp;quot;math&amp;quot; (I find that last one somewhat insulting). The phrase &amp;quot;print equation layout&amp;quot; may seem unnecessary or convoluted. To some degree, I agree. For example, I don&#39;t believe we have seen &amp;quot;web equation layout&amp;quot; come into existence during these first 30 years of the web. Nor, sadly, do I expect to see it in the next 10.&lt;/p&gt;
&lt;p&gt;But as a framing device &amp;quot;print equation layout&amp;quot; serves to ground the conversation and keep aspects in focus that I find critical to many discussions.&lt;/p&gt;
&lt;p&gt;In my opinion, print equation layout is, ultimately, a flawed print tradition that web technology should not have to make extra efforts to support (e.g., as proposed by MathML&#39;s addition to HTML5 way back when). The layout features are largely archaic with very few actual challenges remaining in the context of contemporary (2022) web layout.&lt;/p&gt;
&lt;p&gt;Furthermore, print equation layout is fundamentally inaccessible in the sense of web accessibility. However - and this is where this part gets very tricky - print accessibility traditions for print equation layout do exist (at least in the US as well as a few other countries and of course the US perspective always dominates these conversations). This may seem like a contradiction - how can there be an accessibility tradition that is bad in the context of the web - but it is really no different from the visual layout traditions.&lt;/p&gt;
&lt;p&gt;This is a key complication in the discussions (and myths) around this topic: because print layout and print accessibility traditions exist, people assume that they must be realized on the web - and in the same way.&lt;/p&gt;
&lt;p&gt;I want to argue that neither aspect of print equation layout - visual layout traditions and accessibility traditions - is particularly good or useful &lt;em&gt;on the web&lt;/em&gt;. As a result, it is a particularly problematic set of techniques because the contemporary web already has plenty of good and useful features that solve print equation layout.&lt;/p&gt;
&lt;p&gt;Adding an additional, separate feature layer for a problematic print technology tradition does not make any sense, in fact it has a (by now long) history of hurting the community.&lt;/p&gt;
&lt;h3&gt;Interlude&lt;/h3&gt;
&lt;p&gt;In TeX, we can put punctuation after a table. In HTML, we cannot. Should HTML change because this print tradition exists?&lt;/p&gt;
&lt;h2&gt;What&#39;s in a word&lt;/h2&gt;
&lt;p&gt;Let&#39;s try to be a bit more specific.&lt;/p&gt;
&lt;p&gt;Print equation layout is...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;what people think of when you say &amp;quot;math/formula/equation&amp;quot;   // what  MathML partially captures   // what math mode TeX usually produces  // what MS Word formulas usually look like&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;an elaborate, two-dimensional form of abbreviated notation for compressed, highly complex, context dependent, abstract thought.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&#39;s a bit of a mouthful, I know. But, in the spirit of recursion, this compressed, complex description captures the essential problems well and much follows easily from it.&lt;/p&gt;
&lt;p&gt;This is also not an observation of edge cases, it holds at an elementary level, e.g., in the shape of long addition/multiplication/division or &amp;quot;explanatory&amp;quot; annotated layout in school-level material. At the research level, things are much (much) worse, because research by its nature invents new concepts but more so because especially compression was exacerbated by the content explosion of the 20th century, combined with print page limits.&lt;/p&gt;
&lt;p&gt;A first consequence of this description is that print equation layout is highly ambiguous because the compression is very lossy. What&#39;s worse is that print equation layout not just needs context but is frequently integrated into other complex notation (e.g., text annotation or graphical documents).&lt;/p&gt;
&lt;p&gt;A consequence of this is that it is &amp;quot;non-text content&amp;quot; (in the sense of WCAG SC 1.1.1). It is much closer to &amp;quot;ascii art&amp;quot; than HTML traditions of &amp;quot;semantic markup&amp;quot; with concepts such as heading, landmark, or widget.&lt;/p&gt;
&lt;p&gt;Naturally, this does not apply to all instances of print equation layout. It crucially applies where the actual problems occur - both in visual and non-visual rendering. The problem is not to handle 1+1=2 (well, actually we&#39;ll get back to this), the problem is more like:&lt;/p&gt;
&lt;tex-block&gt;\det(A)=\sum _{\begin{array}{c}k_{1},k_{2},\ldots ,k_{n}\geq 0\\ k_{1}+2k_{2}+\cdots +nk_{n}=n\end{array}}\prod _{l=1}^{n}{\frac {(-1)^{k_{l}+1}}{l^{k_{l}}k_{l}!}}\operatorname {tr} \left(A^{l}\right)^{k_{l}}
&lt;/tex-block&gt;&lt;p&gt;This is a simple piece of print equation layout, something from the first few months at university. And it is packed.&lt;/p&gt;
&lt;h3&gt;interlude&lt;/h3&gt;
&lt;p&gt;Cars are a bad technology.&lt;/p&gt;
&lt;p&gt;They need massive resources to build, most use fossil fuel to run, all need massive infrastructure, destroying our habitat; they are vastly inefficient, they stress people out, they fill our streets and sidewalks with megatons of metallic monstrosities.&lt;/p&gt;
&lt;p&gt;And yet most people own one or want to own one. They have their uses, some at which they excel. So they&#39;re here and we need to think about them.&lt;/p&gt;
&lt;p&gt;But that doesn&#39;t change the fact that we should think about a world without cars because it could really be a much better world for everyone. And we shouldn&#39;t insist to keep building our world around cars or make cars a core assumption in our &amp;quot;cultural stack&amp;quot;.&lt;/p&gt;
&lt;p&gt;The same holds true for print equation layout.&lt;/p&gt;
&lt;h2&gt;Visual print equation layout on the web&lt;/h2&gt;
&lt;h3&gt;practically&lt;/h3&gt;
&lt;p&gt;The problem of realizing print equation layout on the web has been solved for almost two decades now. Well, 18 years to be exact.&lt;/p&gt;
&lt;p&gt;Let that sink in for a bit. 18! years!&lt;/p&gt;
&lt;p&gt;With CSS, this solution started as (client-side) JS only and span soup; we have had that since 2004 (via &lt;a href=&quot;https://www.math.union.edu/~dpvc/jsMath/&quot;&gt;jsmath&lt;/a&gt;, MathJax&#39;s precursor).&lt;/p&gt;
&lt;p&gt;With SVG, we&#39;ve had this since 2011. MathJax v2, at first client-side but quickly also server-side (via phantomjs and later via nodejs). [To some degree, various tools could produce SVG from, e.g., TeX, even earlier that but they didn&#39;t generally care about the web as a use case, e.g., they wouldn&#39;t give you good vertical alignment to meet a baseline.]&lt;/p&gt;
&lt;p&gt;It has been possible to realize print equation layout with just CSS (e.g., server-side generated span soup and CSS) since IE8 - IE8 dammit, released in 2009 - and with just SVG since IE9 (2011). [To be fair, MathJax was not the first to realize such a CSS approach, KaTeX was, but one of my favorite successes while at MathJax was to prod Davide long enough about a &amp;quot;fast Preview without JS&amp;quot; that he built the &amp;quot;CommonHTML&amp;quot; output for v2.7 which added this. I&#39;m also still fond of the previewHTML output which preceded it and traded beauty for CSS simplicity.]&lt;/p&gt;
&lt;p&gt;Visual print equation layout has been solved thrice over with a myriad of smaller and larger tools doing it in varying ways, balancing code simplicity with quality of layout. (mathquill, jqmath, math-ml etc)&lt;/p&gt;
&lt;p&gt;[Aside. This is why a certain blog post from a few years ago about &amp;quot;alternate timelines&amp;quot; was so very much off base - CSS driven print equation layout was first, SVG a close second, not some XML dialect nobody was using for anything but print production. (And no, IE plugins do not count in my book.) In the similar situations the same people would (indeed did famously) argue that working solutions should be considered the prime candidates to move standardization forward, not some contrived academic proposal. But I digress.]&lt;/p&gt;
&lt;p&gt;So print equation layout solutions for the web have existed for a long time.&lt;/p&gt;
&lt;p&gt;Are they perfect? No.&lt;/p&gt;
&lt;p&gt;Are they still getting better? Emphatically yes - all the time. The ever evolving CSS continues to make things easier (grids, container queries, variable fonts, layers!). SVG is just rock solid (for these purposes; and it improves, too).&lt;/p&gt;
&lt;p&gt;The most significant CSS challenge at this point are stretchy character constructions - not a massive problem and one that could (and should) be solved independently of any &amp;quot;mathematical&amp;quot; considerations since stretchy constructions are frequently used in other design scenarios (e.g., stretchy braces around lists are pretty common design patterns).&lt;/p&gt;
&lt;p&gt;In the nice-to-have category, I&#39;d place better control over glyph bounding boxes. Right now, high quality print equation layout realized with CSS depends on webfonts. This is not a particularly critical consideration in my book - much high quality text layout on the web relies on webfonts - and the bounding box problem also needs a more general solution (and in fact there are proposals for better access to font internals).&lt;/p&gt;
&lt;p&gt;Some people argue that the CSS and SVG layout solutions like MathJax generate &amp;quot;convoluted&amp;quot; markup output. This is a red herring.&lt;/p&gt;
&lt;p&gt;On the on hand, convoluted XML is not &amp;quot;better&amp;quot; if it says &amp;quot;math&amp;quot; in it. On the other hand, nobody from CSS or SVG (spec) land has ever tried to help so tools in this space make do with what they can find. My favorite comparison for web people is: the output looks a lot like Bootstrap&#39;s grid layout in its early days (somewhat convoluted) or an accessible datepicker markup today (extremely convoluted). In other words: it looks the way it looks to get the job done with the tools available. And it does. And it gets simpler every year.&lt;/p&gt;
&lt;p&gt;If print equation layout tools for the web were heard the same way grid frameworks were heard way back when, we might get simpler output even quicker &lt;em&gt;and&lt;/em&gt; in a way that benefits the entire web stack. In a world of div-soup SPA&#39;s I think the &amp;quot;ugly output&amp;quot; argument is entirely a red herring.&lt;/p&gt;
&lt;h4&gt;interlude&lt;/h4&gt;
&lt;p&gt;From &lt;a href=&quot;http://archive.computerhistory.org/resources/text/Oral_History/Lampson_Butler/102658024.05.01.pdf&quot;&gt;Oral History of Butler Lampson (Interviewed by:&lt;br /&gt;
Alan Kay)&lt;/a&gt; (via &lt;a href=&quot;https://www.robinsloan.com/newsletters/super-sweet-spots/&quot;&gt;Robin Sloan&#39;s newsletter&lt;/a&gt;)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alan Kay: But I wish that you had been at CERN on a sabbatical when [the Web was being born].&lt;/p&gt;
&lt;p&gt;Butler Lampson: I probably would have been a disaster.&lt;/p&gt;
&lt;p&gt;Kay: I don’t know. But I think you would have made a slightly better …&lt;/p&gt;
&lt;p&gt;Lampson: No. No. No. No. No. No. What Tim [Berners-Lee] did was perfect. My view about the web is that it’s the great failure of computer systems research. Why did computer systems researchers not invent the web? And I can tell you the answer. It’s because it’s too simple.&lt;/p&gt;
&lt;p&gt;Kay: It is too simple.&lt;/p&gt;
&lt;p&gt;Lampson: If I had been there I would have mucked it up. I swear to God. The idea that you’re going to make a new TCP connection for every mouse click on a link? Madness! The idea that you’re going to have this crusty universal data type called HTML with all those stupid angle brackets? We never would have done that! But those were the things that allowed it to succeed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;theoretically&lt;/h3&gt;
&lt;p&gt;Ultimately, the problem is: is this layout tradition a good fit for the web platform? Take table layout. At first people thought it was a good fit. Now we don&#39;t.&lt;/p&gt;
&lt;p&gt;I think print equation layout is a thoroughly bad fit, building it into the web actually damages the platform, just like tables have taken two decades to become unscrewed (oh hi Google, still using tables for layout in some products).&lt;/p&gt;
&lt;p&gt;Even for the simplest examples we run into &amp;quot;fun&amp;quot; print equation layout traditions that are problematic. The easiest to understand are the many text-dependent behaviors, i.e., layout behavior that changes depending on the specific text nodes present (beyond the obvious layout changes from having different text nodes).&lt;/p&gt;
&lt;p&gt;The simplest example is probably: single letter variables are italic, multi-letter ones are upright. This is pure tradition, i.e., something a lot of people like to do. Obviously people sometimes don&#39;t do this. Obviously, there&#39;s no technical difficulty to make this happen as an author / authoring tool on the web today. But as a feature for the web it is weirdly different from any CSS/text rendering features we have; that&#39;s just not how styling text works.&lt;/p&gt;
&lt;p&gt;A more complex example are moveable limits. For example the TeX expression &lt;code&gt;\sum_{i=0}^\infty&lt;/code&gt; would, at plain view, indicate we get a summation symbol with a subscript of &amp;quot;i=0&amp;quot; and a superscript of infinty.&lt;/p&gt;
&lt;p&gt;However, this depends. As an inline expression, TeX will lay this out as I just described.&lt;/p&gt;
&lt;p&gt;&lt;tex-inline&gt;\sum_{i=0}^\infty&lt;/tex-inline&gt;&lt;/p&gt;
&lt;p&gt;However, as a display/block expression, the sub- and superscripts will become under- and overscripts.&lt;/p&gt;
&lt;tex-block&gt;\sum_{i=0}^\infty
&lt;/tex-block&gt;&lt;p&gt;Most print equation layout systems have similar mechanics. This is of course convenient from an authoring perspective - you can switch modes as you like, thus macros or copy&amp;amp;paste are much easier to do.&lt;/p&gt;
&lt;p&gt;However, from a web design perspective it is a wild thing. Not the change as such (of course children change when a container switches from inline to block). But when you replace &lt;code&gt;\sum&lt;/code&gt; with something else (say an &amp;quot;S&amp;quot; for &amp;quot;sum&amp;quot;), you will suddenly not get this behavior anymore. It&#39;s driven by (an allow list of) text nodes.&lt;/p&gt;
&lt;p&gt;From a layout perspective, this automatism wouldn&#39;t have to be builtin - you have to have tooling to specify both layouts anyway - yet MathML, as a print equation layout system, builds it in. This means, we have intentionally ambiguous layout instructions that change rendering depending on text content. This very weird from the perspective text layout on today&#39;s web.&lt;/p&gt;
&lt;p&gt;From this we can take a step up and talk about the operator dictionary. Remember when I spoke about 1+1=2 not being the hard problem. Guess again, we made it hard!&lt;/p&gt;
&lt;p&gt;[I mean, what do you expect? There&#39;s a reason why &lt;a href=&quot;https://knowyourmeme.com/memes/zach-galifianakis-math&quot;&gt;movies&lt;/a&gt; will visually indicate something is difficult beyond comprehension by showing print equations layouts; even &lt;a href=&quot;https://knowyourmeme.com/memes/math-lady-confused-lady&quot;&gt;memes about confusion&lt;/a&gt; get more popular when you add equations to!]&lt;/p&gt;
&lt;p&gt;Naturally, there are traditions around how you space things. 1+1=2? 1+1 = 2? 1 + 1 = 2? Or something more subtle? There are many traditions like this, the most well known being Donald Knuth&#39;s as captured in the TeX book.&lt;/p&gt;
&lt;p&gt;But that&#39;s just it - they are traditions. Traditions, based on &lt;a href=&quot;https://www.ams.org/journals/bull/1979-01-02/S0273-0979-1979-14598-1/&quot;&gt;Knuth&#39;s review&lt;/a&gt; of Western (and mostly English language) publications from the late 19th and 20th century.&lt;/p&gt;
&lt;p&gt;And people can, do, and should deviate from traditions. If there&#39;s one thing I&#39;ve learned working in mathematical publishing production, it&#39;s that authors are full of ideas for how these traditions should change (most are bad, some are genuinely good, sometimes the worst ones are the best).&lt;/p&gt;
&lt;p&gt;I mean, look at &lt;a href=&quot;https://londmathsoc.onlinelibrary.wiley.com/doi/10.1112/jlms/19.73_Part_1.28&quot;&gt;this weird theorem layout from 1940&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.peterkrautzberger.org/assets/2022/LMS_theorem.png&quot; alt=&quot;screenshot of a theorem statement, spliting up lines into sublines to give simultaneous statements of multiple cases&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Thank goodness that style didn&#39;t catch on.&lt;/p&gt;
&lt;p&gt;Instead of giving them the tools that allow adjustments, most print equation layout systems (and MathML in particular), only hand them spacing hacks. This may be fine for print where, in the end, you just want to put some ink on a canvas and no one can see your hackery. On the web, the structural damage is apparent and causes numerous issues (e.g., performance, accessibility). Compare this to CSS where we have benefited from a system that allows resets and customization clearly and cleanly; in comparison, a paradise of tools for designers, authors, and developers.&lt;/p&gt;
&lt;p&gt;None of this is to say that there aren&#39;t interesting aspects of print equation layout that are worth a look for the web; something as basic as multiscripts or something as complex as stretchy glyphs. But bringing them in a way that drags the baggage of 100 years of print equation layout along is an obviously stupid idea (that can be explained but hardly forgiven).&lt;/p&gt;
&lt;p&gt;Stretchy characters are also a weirdly terrible tradition: to stitch together glyph pieces to form something else. Of course it makes sense in a print setting - you work with what you got (just like you re-use typefaces left right and center). But what do people use stretchy character for? It is actually a very simple concept: a border. Yes, the specifics of the border style may carry some (contextual) meaning, but that&#39;s the case with most borders in general. The main layout purpose is very specifically to separate a chunk of layout that benefits from a border on some sides of its box. That&#39;s all there is to it. It&#39;s even in the name: these constructions are commonly called &amp;quot;fences&amp;quot; (mathml 3 even has an &lt;code&gt;mfenced&lt;/code&gt; element, may it rest in pieces). A fence is a separator (ARIA pun intended), something to mark a border.&lt;/p&gt;
&lt;p&gt;The MathML people want the ability to build stretchy characters to be limited to internal magic of MathML implementations in browsers, relying on low-level access to Open Type MATH tables without doing the hard work of surfacing this complex technology to userland. This technology has a somewhat problematic history in itself. The extension was developed by Microsoft exclusively for Word and only years later proposed to Open Type properly to help, really, the MathML people to make the case that &amp;quot;it&#39;s a standard&amp;quot;. There are only a handful of fonts with MATH tables around because they are quite complex (read: expensive) to create.&lt;/p&gt;
&lt;p&gt;Even ignoring the troublesome history, this approach limits a rather lovely concept used throughout design (just search dribble or codepen for curly braces) to supposedly &amp;quot;mathematical&amp;quot; markup and layout. It is quite silly especially since even basic uses case won&#39;t actually be possible this way (e.g., a stretchy brace around a list would still be invalid,  even ignoring &amp;quot;semantics&amp;quot;). Any which way, we still need an alternative.&lt;/p&gt;
&lt;p&gt;So why not rethink this in the context of the web? Why carry over a silly tradition (which is actually even worse: fonts will have multiple sizes of fences and those will be used if the box they wrap around is small enough). We have so many useful ways to indicate that something is grouped together on the web, we have traditions around those too (e.g., hover, focus, target effects, easily shaded or animated backgrounds to indicate depth), some of which are problematic for other reasons. We have a dynamic medium that allows you to only expose the gnarly information like this when a user actually requires it instead of on-the-nose all the time. We can also leverage the web&#39;s infinite canvas to build new traditions that serve users better. For example, stretchy braces do not increase by just nesting. They stretch only to match the contents, thus in a double parentheses situation (like &lt;code&gt;((...)+(...))&lt;/code&gt;) they will not grow, thus not actually represent the full complexity. That&#39;s understandable in the limited, fixed page sizes of print products. It also forces authors to do silly hacks to get additional dimensions should they want to communicate differently than tradition allows for. Maybe authors on the web want to do things differently from the web?&lt;/p&gt;
&lt;p&gt;And of course it&#39;s funny how even if you insist on a fonts-based alternative there&#39;s already a solid alternative to build upon today: variable fonts can do wonderful stretchy things and can obviously solve this problem. Just without magic and with control in the hands of authors.&lt;/p&gt;
&lt;p&gt;If you move away from the silliness that you need to reproduce print tradition, you&#39;ll find that you can solve more problems. For example, &lt;a href=&quot;https://www.peterkrautzberger.org/0204/&quot;&gt;asymmetrical or partially colored stretchy constructions&lt;/a&gt;. It&#39;s perfectly reasonable to have a &lt;code&gt;\begin{cases}&lt;/code&gt; environment but want the part before it on the same line as the first case. You can&#39;t do that with Open Type MATH tables. You can &lt;a href=&quot;https://tex.stackexchange.com/questions/226155/a-curly-brace-with-an-asymmetric-cusp/226162&quot;&gt;hardly do it in TeX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The operator dictionary is another good example for this. Doing layout based on text content is an interesting topic - we&#39;ve had years of debates around aligning table cells at decimals. For trivial spacing changes around elements - which authors want to customize anyway - it seems just a bit silly.&lt;/p&gt;
&lt;p&gt;As related and similarly bad example are &lt;a href=&quot;https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols&quot;&gt;math alphabets&lt;/a&gt;. They are pretty mad to begin with; they are fontface traditions, captured as Unicode codepoints. It&#39;s understandable that mathematical writing and print traditions have abused fontfaces - it was a nice hack when all you have is moveable type. Enshrining them in Unicode does make some sense for print because (in a twist that should not be a surprise) it turns out that these glyphs have grown their own requirements. For example, Greek character glyphs tend to need special shapes for mathematical context than for Greek texts. In the mash up of technology limitations that is print equation layout these glyphs serve contradicting roles: be identifiably different but fit nicely with the Latin glyphs. This leads to really weird design quirks, see for example &lt;a href=&quot;https://read.somethingorotherwhatever.com/entry/Haralambous1999&quot;&gt;From Unicode to Typography, a Case Study: the Greek Script&amp;quot; by Yannis Haralambous&lt;/a&gt; (tip of the hat to Christian&#39;s wonderful Interesting Esoterica).&lt;/p&gt;
&lt;p&gt;The recent addition of math calligraphic scripts to unicode (while unsurprising given the people involved) is weird to me because authors will not and do not stop. The other day, we had a paper with runes come through production. Should they add &amp;quot;math runes&amp;quot; to Unicode next? And what about &lt;a href=&quot;https://github.com/stipub/stixfonts/issues/221&quot;&gt;David Griffith&#39;s script r&lt;/a&gt;, does he get a codepoint all for himself? What about math emoji? Also, what about math alphabets when they&#39;re in text mode - do they have special meaning, too? (Other than making horribly inaccessible social media profiles, that is.) More seriously, I&#39;m not aware of any discussion of how the math alphabets are purely focused on supporting authors using the Latin alphabet. For lack of expertise, I have not written about non-European print equation layout traditions because I know next to nothing of them but from the little I know (e.g. the ancient W3C note on Arabic mathematical notation) I suspect they might make for an interesting post-colonialism study. [Aside, there is a direction where I could see Unicode actually help, just like it does with emoji: codepoints for actual mathematical concepts that are usually represented as a single grapheme. Indeed, the history of some math alphabets hints at this, e.g., the few &lt;a href=&quot;https://en.wikipedia.org/wiki/Blackboard_bold&quot;&gt;blackboard bold&lt;/a&gt; symbols that are in the Basic Multilingual Plane. Just like we have a heart and a heart as card deck suit.]&lt;/p&gt;
&lt;p&gt;It is worthwhile to note at this point that in recent years more and more TeX users (including the AMS) have been leveraging a re-interpretation of Knuth&#39;s rulesets by adopting xelatex which realizes equation layout using Open Type MATH Table fonts which, again, was designed for Microsoft Word when they built their print equation layout facilities alongside a first font from Tiro. While Microsoft and Tiro followed the TeX book there are noticeable differences, making it hard to know if the bug is in the font, in MS Word, or in xelatex.&lt;/p&gt;
&lt;p&gt;On a slightly higher level, we have traditions such as labeled equations. They are surprisingly complicated in themselves. For example, most systems will provide authors flexibility in aligning or auto-numbering them, they may be mixed with other constructs (such as stretchy characters, e.g., in the LaTeX package cases or empheq). In some print equation layout systems they&#39;ll be realized via table-like constructs, in some via lower level primitives (horizontal fills, floats etc).&lt;/p&gt;
&lt;p&gt;It also gets complex when you look to integrate equations into surrounding content. Print (equation) layout systems tend to treat equation layout as starting an entirely different context; the break is fairly absolute, both for inline and block layout (albeit in different ways). This doesn&#39;t fit well with the web&#39;s DOM and CSS&#39;s box model however. For example, equations tend to have break out of all context and align to some global property, in particular their labels. So a block equation in a list will simply break out of everything and potentially place a label at a far end of the line. This fits very badly into the web&#39;s layout model(s).&lt;/p&gt;
&lt;p&gt;Next step up you&#39;ll find alignment across equations. A common feature in print layout engines with equation support (e.g., amsmath&#39;s intertext). Should we enable these kinds of traditions? Maybe; I do think this last one is fascinating. Should we do it by replicating print (equation) layout traditions? That sounds rather ludicrous to me.&lt;/p&gt;
&lt;p&gt;Another problematic print tradition lies in inheritance. Traditionally, print equation layout will work as a reset. E.g., in TeX math mode will not inherit surrounding text styles or weight. Why? Well, because that would change the &amp;quot;meaning&amp;quot; because you&#39;re abusing style and weight: if you use a normal V and a bold V for two different concepts, then naturally in a bold heading you&#39;ll run into trouble. On the web, we see other traditions to surface; for example, people find it very irritating if a fraction is not bold inside a heading. A very real problem happens if a link starts or ends with print equation layout which is suddenly no longer styled like a link (e.g., neither blue nor underlined). That&#39;s a real problem for users (and possibly failing one or more WCAG SCs). Purists will insist - and they are wrong, at the expense of users. [Fun fact: that&#39;s why I personally prefer SVG with paths instead of characters/glyphs when reproducing print equation layout on the web. It&#39;s more honest to the print content tradition and happens to avoid many of these problems with ease.]&lt;/p&gt;
&lt;p&gt;Of course there&#39;s yet another exception. In TeX for example, when you nest text mode inside math mode, that &amp;quot;inner&amp;quot; text mode can inherit. This means text in a piece of equation layout ends up, say, italic if the text surrounding the equation layout is italic (e.g., in a definition). Does this lead to confusion? You bet.&lt;/p&gt;
&lt;h3&gt;To recap&lt;/h3&gt;
&lt;p&gt;The problem of creating visual layout that matches print equation layout has been solved for well over a decade. It&#39;s somewhat ridiculous to suggest the web has a significant technology gap here that only a completely new set of markup and layout features can fix. Additionally, the theoretical structure of print equation layout enshrines problematic traditions that fit very badly, going against the web&#39;s grain.&lt;/p&gt;
&lt;p&gt;This leaves non-visual-layout considerations regarding print equation layout, in particular (web and print) accessibility considerations.&lt;/p&gt;
&lt;h2&gt;Ambiguity&lt;/h2&gt;
&lt;p&gt;Let&#39;s talk about ambiguity.&lt;/p&gt;
&lt;h3&gt;A different example&lt;/h3&gt;
&lt;p&gt;To start, consider a different example, &lt;a href=&quot;https://en.wikipedia.org/wiki/Musical_notation&quot;&gt;musical notation&lt;/a&gt;. In modern notation, a note is about as ambiguous on its own as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Grapheme&quot;&gt;grapheme&lt;/a&gt; in text. For example, without a staff and other context the note itself doesn&#39;t tell you much. You might know the value by looking at just the note but even then this might not be enough (e.g., if you happen to miss a subsequent dot or preceding accidentals). Even with a staff, you might still have trouble, e.g., if all you have is a single measure you might lack the key. Similarly for dynamics. Also, it might be part of music that ranges from Opera to Jazz and you&#39;ll want to know that to adjust your understanding.&lt;/p&gt;
&lt;p&gt;It&#39;s hard to know when you have all the relevant information. A piece may appear complete but lack information on, e.g., which instrument this is for, what other voices there might be.&lt;/p&gt;
&lt;p&gt;And this is for a fairly limited set of notational elements.&lt;/p&gt;
&lt;p&gt;Now expand this to the ever changing mathematical notation, then expand to engineering and sciences where there is no limit in generalization and specialization. [Of course, musicians can easily discuss a single note as passionately and intricately as mathematicians discuss a single number. But while this is much more interesting, it is unfortunately not the topic here.]&lt;/p&gt;
&lt;h3&gt;Some examples&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;a superscript 2 (e.g., &lt;tex-inline&gt;x^2, f^2, \sin^2&lt;/tex-inline&gt;) - is probably some sort of &amp;quot;squared&amp;quot; thingie? But is that always true? How about &lt;tex-inline&gt;A_1^2&lt;/tex-inline&gt; or &lt;tex-inline&gt;f|^2&lt;/tex-inline&gt;?&lt;/li&gt;
&lt;li&gt;a binomial coefficient cannot easily be distinguished from a 2-dim vector notation - &lt;tex-inline&gt;n \choose k&lt;/tex-inline&gt; vs &lt;tex-inline&gt;\left(\begin{smallmatrix}{} n \\ k \end{smallmatrix}\right)&lt;/tex-inline&gt;; similarly for &lt;a href=&quot;https://en.wikipedia.org/wiki/Stirling_number&quot;&gt;Stirling numbers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;frequent use of space or punctuation to imbue layout with extra meaning or to avoid having to add clarifying notation (e.g., &lt;code&gt;+a + -c + +b&lt;/code&gt; vs, say, &lt;code&gt;++b&lt;/code&gt; &lt;a href=&quot;https://whystartat.xyz/wiki/Space_is_significant&quot;&gt;https://whystartat.xyz/wiki/Space_is_significant&lt;/a&gt;)- &amp;quot;(a,b)&amp;quot; - open interval? point in the plane? bilinear form in a Hilbert space?&lt;/li&gt;
&lt;li&gt;punctuation in general. If it&#39;s in the middle, is it just a separator as in &lt;tex-inline&gt;i, j \in \mathbb{N}&lt;/tex-inline&gt; or an operator as in &lt;tex-inline&gt;1-2&lt;/tex-inline&gt; or &lt;tex-inline&gt;1.2&lt;/tex-inline&gt;? If it&#39;s at the end, is it part of the equation or actually text (a common sight in block expressions)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[a]&lt;/code&gt; might be the sign of a // &lt;code&gt;[c, d]&lt;/code&gt; might be a matrix // &lt;code&gt;A[b]&lt;/code&gt; might be a ring&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|...|&lt;/code&gt; - absolute value, norm, determinant?&lt;/li&gt;
&lt;li&gt;&lt;code&gt;|&lt;/code&gt; - evaluation of definite integral (alternatively &lt;code&gt;[ ... ]&lt;/code&gt; but that could also be a closed interval or a commutator)&lt;/li&gt;
&lt;li&gt;what is the difference between &lt;tex-inline&gt;\begin{array}{|c|}x \\ y\end{array}&lt;/tex-inline&gt; and &lt;tex-inline&gt;\left|\begin{array}{c}x \\ y\end{array}\right|&lt;/tex-inline&gt; and how would you find out?&lt;/li&gt;
&lt;li&gt;superscript -1. Is it an inverse? A &amp;quot;1 over something&amp;quot; ? A reverse operation? Some kind of transposition? An index for something enumerated by integers?&lt;/li&gt;
&lt;li&gt;&amp;quot;d&amp;quot; in &lt;tex-inline&gt;(a + bx^3 + cx^2 + dx = 0)&lt;/tex-inline&gt; vs &amp;quot;d&amp;quot; in &lt;tex-inline&gt;\int f(x) dx&lt;/tex-inline&gt;&lt;/li&gt;
&lt;li&gt;uppercase delta &lt;tex-inline&gt;\Delta&lt;/tex-inline&gt; and the triangle symbol &lt;tex-inline&gt;\triangle&lt;/tex-inline&gt; can look surprisingly similar depending on the font.&lt;/li&gt;
&lt;li&gt;invisible operators (U+2062, U+2061) - where to even start?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more &amp;quot;inspiration&amp;quot;, check out &lt;a href=&quot;https://whystartat.xyz/wiki/Category:Ambiguities&quot;&gt;https://whystartat.xyz/wiki/Category:Ambiguities&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;An absolute favorite of mine is the &lt;a href=&quot;https://en.wikipedia.org/wiki/Legendre_symbol&quot;&gt;Legendre symbol&lt;/a&gt; &lt;tex-inline&gt;\left({\frac {a}{p}}\right)&lt;/tex-inline&gt; which is obviously easy to confuse with a fraction that happens to have parenthesis (e.g., from reducing a more complex expression). It introduced before the year 1800 and yet, apparently, there&#39;s no dominant way to speak it, see &lt;a href=&quot;https://mathoverflow.net/questions/15447/is-there-a-standard-way-to-read-the-legendre-symbol&quot;&gt;this MO discussions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We can also go the other way around. We can have the same voicing for different notations. &lt;code&gt;|A|&lt;/code&gt; and &lt;code&gt;det(A)&lt;/code&gt; may read &amp;quot;determinant of A&amp;quot;; both &lt;code&gt;exp(x)&lt;/code&gt; and&lt;code&gt;e^x&lt;/code&gt; may read as &amp;quot;exponential function at x&amp;quot;. How would a non-visual user know which notation was used when someone said this?&lt;/p&gt;
&lt;p&gt;A separate problem are the math alphabets, i.e., the abuse of using typefaces to identify different, context-dependent concepts. It seems so easy to - visually - discern italic from normal, serif from sans-serif, gothic from Sütterlin - well, in truth it is not that easy but here we are anyway. In addition, foreign alphabets are abused so much that mathematics has become reliant on particular typeface designs as mentioned earlier.&lt;/p&gt;
&lt;p&gt;For non-visual users, these then have to be differentiated (otherwise you&#39;re no better than a &amp;quot;read more&amp;quot; link) and the noise (&amp;quot;Upper Gothic X&amp;quot;)  is immense - or more often: the silence is immense.&lt;/p&gt;
&lt;p&gt;And then we haven&#39;t even opened the box of notation that&#39;s specific to engineering, physics and chemistry but realized using print equation layout.&lt;/p&gt;
&lt;h3&gt;Semantics or layout?&lt;/h3&gt;
&lt;p&gt;There&#39;s a moving goalpost (or perhaps gaslighting) issue that I&#39;ve encountered many times: should assistive technologies (AT) provide information about layout or semantics when it comes to print equation layout?&lt;/p&gt;
&lt;p&gt;A common stance in the general accessibility community is: it&#39;s not about special treatment, it&#39;s about equal treatment. What does that mean for print equation layout? Equal treatment would provide precise information about layout.&lt;/p&gt;
&lt;p&gt;But that&#39;s not what users actually expect (or are accustomed to). Instead, &amp;quot;semantics&amp;quot; are desired. Take this simple example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;f: X → Y&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Should it read&lt;/p&gt;
&lt;p&gt;&amp;quot;lower italic f colon upper italic X right arrow upper italic Y&amp;quot;&lt;/p&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;p&gt;&amp;quot;f maps from upper X to upper Y&amp;quot;.&lt;/p&gt;
&lt;p&gt;The latter is certainly easier and happens to be how humans speak.&lt;/p&gt;
&lt;p&gt;However, print equation layout does not offer such semantic information. Therefore, solutions turn to guesswork aka &lt;a href=&quot;https://en.wikipedia.org/wiki/Heuristic&quot;&gt;heuristics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These heuristics work barely for school-level print equation layout and some, more &amp;quot;stable&amp;quot; parts of college education (e.g., engineering). But they easily and frequently fail even in these areas.&lt;/p&gt;
&lt;p&gt;To come back to our examples: yes, that superscript 2 is very likely &amp;quot;squared&amp;quot; but then again it is, inevitably, far from always &amp;quot;squared&amp;quot;. As usual, &lt;a href=&quot;https://xkcd.com/2614/&quot;&gt;xkcd has you covered on the subject of &amp;quot;2&amp;quot;&lt;/a&gt; (&lt;a href=&quot;https://creativecommons.org/licenses/by-nc/2.5/&quot;&gt;cc-by-nc 2.5&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/2.png&quot; alt=&quot;xkcd no. 2614, outlining what the number 2 might mean, depending on placement in sup/sub/pre/post/under/over scripts&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Beyond the evident lack of semantics in print layout per se, the second problem is the lack of contextual information. While MathJax and its sister project &lt;a href=&quot;https://speechruleengine.org/&quot;&gt;Speech Rule Engine&lt;/a&gt; did some Simons-funded research into the problem a few years back, no existing solution takes even other equations into account (e.g., using the guess for a variable that was used earlier).&lt;/p&gt;
&lt;p&gt;Heuristics definitely do not try to analyze the whole document context to trigger specialized heuristics despite the many obvious advantages. Just by field, you can differentiate a lot - &lt;tex-inline&gt;X\perp Y&lt;/tex-inline&gt; in statistics vs geometry, &lt;a href=&quot;https://en.wikipedia.org/wiki/Bra%E2%80%93ket_notation&quot;&gt;Bra-Ket notation&lt;/a&gt; in quantum mechanics, arrows in category theory, or (again) the Legendre symbol in number theory. Even outside subject-specific traditions, this could help, e.g., if you can identify a definition, it helps quite a bit later on. You know, like a human reader.&lt;/p&gt;
&lt;p&gt;The simplest examples can show how tricky this is. Think about &lt;code&gt;f(g+h)(x)&lt;/code&gt;. You might agree that there&#39;s a good chance that there&#39;s a function application (at x) happening but it could also just be a multiplication with x after some calculations within the parentheses, who knows. Fairly limited contextual checks can probably help out here but without them we&#39;re lost.&lt;/p&gt;
&lt;p&gt;What&#39;s worse: today&#39;s heuristics also often do not work within an equation, e.g.,&lt;/p&gt;
&lt;tex-block&gt;\operatorname{det}(A) = |A| = {\begin{vmatrix} a&amp;b \\ c&amp;d \end{vmatrix}} = ad-bc.
&lt;/tex-block&gt;&lt;p&gt;Speech Rule Engine&#39;s heuristics (the best around) will identify the middle part as a matrix determinant but still treats &lt;code&gt;|A|&lt;/code&gt; as absolute value. And in fairness, if we didn&#39;t have the first part, that A might still be some poorly named scalar.&lt;/p&gt;
&lt;p&gt;A key feature of the heuristics is surprisingly boring and complex: grouping things.  Where do subexpressions start and end? Since print equation layout is inherently non-semantic and built for author convenience, most authoring does not (have to) care about grouping the structure well. Who cares, you&#39;ll work it out visually and from context (thanks, PEMDAS!).&lt;/p&gt;
&lt;p&gt;Here&#39;s a pretty normal, real world example:&lt;/p&gt;
&lt;tex-block&gt;\begin{aligned} &amp; u_2(t,x) \\  &amp; {}= \frac{2}{t^{1/2}}. \operatorname {Re} \left( \left( \tilde{\Psi }(y) ( \log t + \log \sqrt {1-|y|^2}) +\tilde{\Phi }_2 (y) \right)\right.  \\ &amp; \quad \left. \left. \times \exp \left(it\sqrt {1-|y|^2} +i\Psi (y) \log t \right) \right)\right|_{y=\frac{x}{t+2B}}\\ &amp; \quad + O(t^{-\frac{3}{2}+c\delta }) \end{aligned}
&lt;/tex-block&gt;&lt;p&gt;Note how the parenthetical expression starting after &lt;code&gt;Re&lt;/code&gt; ends in another table row. This is perfectly understandable authoring from a print author perspective - your line is fixed and limited, you&#39;ll need to break things up. Since you&#39;re in print (and you want alignments to work), you push pieces into a table.&lt;/p&gt;
&lt;p&gt;How should heuristics work out this grouping? The answer is: probably not.&lt;/p&gt;
&lt;p&gt;Another interesting example is the treatment of horizontal and vertical space. &amp;quot;Clearly&amp;quot;, if you find &lt;code&gt;$(a \quad b)$&lt;/code&gt; and &lt;code&gt;$(a\ b)$&lt;/code&gt; near each other, they&#39;re different, right? But what can heuristics realistically do here? Especially, when they only work on a single expression? [Aside: I give the source here - SRE would indeed give the same descriptions for these expressions.]&lt;/p&gt;
&lt;p&gt;Print equation layout uses positive and negative space abundantly and often confusingly. Some authors like things spaced out where others like them tightened up. This makes it impossible to leverage space in heuristics. As a consequence, space is not to be messed with. [This also creates problems if you&#39;re mistakingly thinking print equation layout is text - because WCAG 1.4.12 would then force you to allow users to change word and letter spacing. In the end, &lt;a href=&quot;https://whystartat.xyz/wiki/Space_is_significant&quot;&gt;space is significant&lt;/a&gt;.]&lt;/p&gt;
&lt;p&gt;Similarly, one of the most important visual tools is completely absent from non-visual presentation: the size of stretchy glyphs. This is sad because the whole reason we use stretchy constructions in the first place is to make layout more accessible for users. Stretchy constructions indicate both grouping and the complexity of subexpressions. Authors frequently use it to explicitly highlight subexpressions because they realize their complexity. And yet despite their focus on layout, non-visual presentations provide no access to this information; there is no representation of stretchiness let alone the relative dimensions. The most you can hope for is something like Speech Rule Engine&#39;s ability to summarize a subexpression (along the lines of &amp;quot;a sum of 16 summands&amp;quot;). Good grouping informs users but there&#39;s a significant benefit to a stretchy character: the larger it is, the more crap is going to be inside whatever it groups - to the point where they can be downright scary. They are critical information.&lt;/p&gt;
&lt;p&gt;Frequently,  people (especially in the MathML community) promise magical semantics from print equation layout. Yet when faced with the obvious, plentiful, and unsolvable problems, the goal post is casually moved to &amp;quot;you need to give layout information&amp;quot; only so that when you provide layout information, people go back to &amp;quot;oh, that&#39;s not the idea, you should provide semantics&amp;quot;.&lt;/p&gt;
&lt;p&gt;Unfortunately, many people believe this empty promise.&lt;/p&gt;
&lt;h2&gt;Problems from print layout traditions&lt;/h2&gt;
&lt;p&gt;There are both theoretical and practical problems arising from print layout tradition.&lt;/p&gt;
&lt;h4&gt;Theoretical&lt;/h4&gt;
&lt;p&gt;Besides the fundamental issues outlined above, print equation layout has some traditions that add another layer of problems.&lt;/p&gt;
&lt;p&gt;For example, we go back to the concept of moveable limits. The expression&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\sum_{i=0}^\infty&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;would, at plain view, indicate we get &lt;code&gt;Σ&lt;/code&gt; with a subscript of &lt;code&gt;i=0&lt;/code&gt; and a superscript of &lt;code&gt;∞&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, as you might recall, this depends on the mode. In inline math mode, TeX will lay this out as described. In display math mode, the sub- and superscripts will become under- and overscripts. This is, again, convenient from an authoring perspective - you can switch modes as you like, thus macros / copy&amp;amp;paste are much easier.&lt;/p&gt;
&lt;p&gt;This automatism wouldn&#39;t have to be built into a system from a purely functional/layout point of view; it&#39;s authoring convenience. Of course, MathML builds it in to be compatible with print equation layout traditions for which it was designed. In fact, it adds the (ever growing) operator dictionary to describe various layout rules like this instead of focusing on just the necessary parts of layout.&lt;/p&gt;
&lt;p&gt;This means, we have intentionally ambiguous layout instructions and that&#39;s tricky for heuristics to work - yet another context to check. [For once, that&#39;s not terribly weird for the web but media and container queries separate this much more cleanly - and they are separate from any semantic interpretation.]&lt;/p&gt;
&lt;p&gt;So what do heuristics with such ambiguous layout? They cheat to make this ambiguity go away. When voicing, simply say &amp;quot;sum from ... to&amp;quot; - tada, problem solved! Of course now you have 3 new problems: a) what if it isn&#39;t a sum? b) what if it is a sum but using a different character for the summation operator?c) what if the authors want to use side- and overscripts to mean different things?&lt;/p&gt;
&lt;h4&gt;Practical&lt;/h4&gt;
&lt;p&gt;Given the nature of print products, authors are prone to abuse layout instructions. In the end, it has to look good, who cares how they got there.&lt;/p&gt;
&lt;p&gt;For example, authors frequently make their content work purely visually, placing elements side-by-side, relying on the fixed positions of print layout.&lt;/p&gt;
&lt;p&gt;They will use table layout to split content apart so that it can fit page dimensions (e.g., complex breaks across lines), thereby making it impossible to reflow in a dynamic environment and group correctly in anon-visual semantic analysis.&lt;/p&gt;
&lt;p&gt;Reversely, they will space content out to fill a page, impeding reflow or resizing in dynamic environments.&lt;/p&gt;
&lt;p&gt;They may &amp;quot;accidentally&amp;quot; rely on TeX&#39;s defaults, e.g., consecutive display math blocks may align if those blocks are generally centered (rather than explicitly grouped by aligned blocks); yet with different alignment defaults, the alignment may fail unexpectedly. Similarly with multi-line environments or other manual and/or automatic line breaks.&lt;/p&gt;
&lt;p&gt;All of this makes it even harder for heuristics.&lt;/p&gt;
&lt;p&gt;I want to stress that these are not bad practices - many of them have long and detailed traditions (e.g., commutative diagrams). They are simply print traditions with no consideration for the web, let alone accessibility.&lt;/p&gt;
&lt;h3&gt;interlude&lt;/h3&gt;
&lt;p&gt;On the web (and in many publishing content / production pipelines), abusing HTML markup is frequently favored (i.e., text superscripts, tables, simple vertical alignments) alongside low quality (usually GIF) rendering for inline math and high-quality (usually PNG) for display content. Frequently, only the &#39;problematic&#39; parts are rendered as images leading to poor layout (e.g., tables with some equational content and a text label in lieu of properly labeled equational content, HTML scripts with graphics in either base or script position). Accordingly, content providers have a long history of &amp;quot;optimizing&amp;quot; their content for these approaches (e.g., STEM publishers have really terrible XML workflows stripping out as much complex markup as possible). That content is in some ways lost forever (because it would be cost prohibitive to re-key it).&lt;/p&gt;
&lt;h2&gt;Non-visual print equation layout&lt;/h2&gt;
&lt;p&gt;In several countries, print equation layout has some form of non-visual accessibility tradition.&lt;/p&gt;
&lt;p&gt;The US traditions are dominating in this context since MathML was developed largely by US companies for a US audience.&lt;/p&gt;
&lt;p&gt;For print traditions in the US, the most well-known methods for non-visual presentation for equation layout were developed by &lt;a href=&quot;https://en.wikipedia.org/wiki/Abraham_Nemeth&quot;&gt;Abraham Nemeth&lt;/a&gt; - nowadays known as the MathSpeak ruleset for voicing and the Nemeth Braille notation for tactile presentation. A critical point is that both sets were designed for human creation. MathSpeak, to enable a non-expert reader to read mathematical publications to blind users; Nemeth Braille for a specialist braille transcriber to transcribe (and a trained reader to read).&lt;/p&gt;
&lt;p&gt;If you can catch him, Volker Sorge (who implemented support for both as part of Speech Rule Engine and MathJax) is happy to go into much detail telling you how neither MathSpeak nor Nemeth Braille can be produced from input without extensive heuristics. The rulesets are in fact not properly specified and can be contradictory.&lt;/p&gt;
&lt;p&gt;My impression is that this is by design - these rulesets could assume a human orator/transcriber who can adjust rules on the fly to enhance the simplistic heuristics. Even without understanding mathematics, it&#39;s fairly easy to make a change on the fly, e.g., after initial feedback from the user that in this particular pattern the superscripted 2 is not &amp;quot;squared&amp;quot; but, say, a row or column vector.&lt;/p&gt;
&lt;p&gt;It&#39;s probably worth noting that Nemeth Braille has not been revised since the 1970s (though a revision has finally started a few years back). It is a specialized Braille notation that describes visual layout and only partially linearizes the output (e.g., fractions and scripts are linearized while tabular structures are not), leading to many problems as contemporary refreshable braille displays for computers are single-line devices. And of course, if you&#39;re not a Braille user, then none of it helps you.&lt;/p&gt;
&lt;h3&gt;heuristics are bad, non-recursive ones worse&lt;/h3&gt;
&lt;p&gt;At this point let me point out that heuristics are something that web accessibility eschews; heuristics are only encouraged to recover from errors (e.g., turning invalid documents into a valid ones, making sense of authoring or connection errors, dealing with legacy code like table layout).&lt;/p&gt;
&lt;p&gt;Accessibility on the web revolves around the so-called &amp;quot;accessible name&amp;quot; which is calculated for each element in the document. &lt;a href=&quot;https://w3c.github.io/accname/&quot;&gt;This calculation&lt;/a&gt; is rather complicated but at the heart of it is Step F: the accessible name of a node is accumulated from the accessible name of its children. From the end users point of view, you tend to get some additional information (such as the role of the element) added by AT. And that&#39;s about it.&lt;/p&gt;
&lt;p&gt;Print equation layout frequently does not allow for recursive naming. The heuristics necessary to guess a non-visual presentation have to frequently analyze large subtrees to make sense of things.&lt;/p&gt;
&lt;p&gt;A simple example is binomial notation, &lt;tex-inline&gt;n \choose k&lt;/tex-inline&gt;. Without the parentheses &lt;tex-inline&gt;{n \atop k}&lt;/tex-inline&gt; is not binomial notation and yet when a binomial is voiced, you wil not hear the parentheses. If you were to calculate the description recursively, you would have to decide on the description of what&#39;s within the parentheses before you get to them. How can you decide what to do at that point?&lt;/p&gt;
&lt;p&gt;To make things worse, in many print equation layout systems this notation is realized via fraction primitives by removing the fraction bar (e.g., TeX, MathML). If you were to calculate the description you now have to decide what to do with a fraction that lacks a fraction bar. Now add to this mix the Legendre symbol &lt;tex-inline&gt;\left({\frac {a}{p}}\right)&lt;/tex-inline&gt;.&lt;/p&gt;
&lt;p&gt;We can also go back to our script examples from earlier - for 2 and -1. While &lt;tex-inline&gt;7^2 = 7\times 7&lt;/tex-inline&gt; and &lt;tex-inline&gt;7^{-1} = {1 \over 7}&lt;/tex-inline&gt;, we have &lt;tex-inline&gt;\sin^2(x) = \sin x \times \sin x&lt;/tex-inline&gt; yet &lt;tex-inline&gt;\sin^{-1} x&lt;/tex-inline&gt; is actually the inverse function at x, &lt;tex-inline&gt;\arcsin x&lt;/tex-inline&gt;. If you tried to capture this, you&#39;d have to ignore whatever you&#39;re creating for superscript -1 but still know about it to interpret the other part.&lt;/p&gt;
&lt;p&gt;MathML is even worse for this, as it is a stratified format for rendering purposes. With TeX you can at least have author macros to try to make sense.&lt;/p&gt;
&lt;p&gt;Authors, especially on a research level, invent layout that requires new heuristics - both large and small. For example, they create custom glyphs (such as blackboard bold Greek characters) or they invent notations as grapheme-like tikz diagrams which then get used like any other element of a larger expression.&lt;/p&gt;
&lt;p&gt;Historically, such inventions coincide with naming. &lt;a href=&quot;https://jeff560.tripod.com/calculus.html&quot;&gt;Jeff Miller&lt;/a&gt; collects several historic examples (such as nabla). Yet, naming is not always invented alongside notation if you recall the Legendre symbol once again. [Authors also regularly just hack layout badly (e.g., &lt;code&gt;\mathop{l\overline{og}}&lt;/code&gt;) but that&#39;s technically speaking a different problem.]&lt;/p&gt;
&lt;p&gt;Authors also intentionally hack things to work around traditions. A while back at work we ran into someone apparently abusing amsmath&#39;s sideset macro in something like &lt;code&gt;\sideset{}{^*}{\sum}_{a\mid A_y}&lt;/code&gt;. We only noticed that because MathJax had improved sideset to be more accessible which changed the rendering (and introduced regressions). Upon investigation, it turned out we had a lot of similar papers. Even better, all of these authors used sideset consistently to get a superscripted sum that would still have a &amp;quot;movable&amp;quot; subscript.&lt;/p&gt;
&lt;p&gt;In other words,  authors clearly want a certain type of layout that goes against traditions; they have good reasons to do so. Heuristics trying to make sense based on those traditions suddenly break as well.&lt;/p&gt;
&lt;p&gt;This is a perfect example how traditions hold us back. They are too rigid - you can&#39;t have &amp;quot;partially moveable limits&amp;quot; without resorting to hackery. And once you have that kind of a hammer, everything looks like a nail.&lt;/p&gt;
&lt;h2&gt;MathML&lt;/h2&gt;
&lt;p&gt;MathML does not provide sufficient quality, neither in theory nor in practice.&lt;/p&gt;
&lt;h3&gt;visually&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Practically&lt;/strong&gt;: visual rendering in browsers is poor, buggy, hard to work around. It is not maintained by browser companies and never has been.&lt;/p&gt;
&lt;p&gt;Bugs range from mildly annoying to problematic to terrifyingly bizarre. I stopped noting them publicly; it&#39;s too much fun to check every few years that even basic bugs do not get fixed or even reported.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theoretically&lt;/strong&gt;: visual rendering is not actually specified (thought here is a new effort to change that, making all existing MathML renderers invalid and breaking content without care).&lt;/p&gt;
&lt;p&gt;This is where they are after 25 years of MathML, 15 years of MathML in HTML5, ~10 years of MathML &amp;quot;support&amp;quot; in Firefox, ~8 years of MathML &amp;quot;support&amp;quot; in Safari, neither of which are due to actually Mozilla or Apple spending resources but co-opted volunteer efforts. No amount of Igalia newspeak will change that fact. [Apologies to all the wonderful people I&#39;ve met from Igalia - the MathML people are not a good look for you all.]&lt;/p&gt;
&lt;p&gt;Optimistically, I&#39;d wager it will take a good decade to get reliable visual rendering quality. Certainly, CSS, SVG, HTML and ARIA will all move faster and help tools like MathJax even when they&#39;re not trying.&lt;/p&gt;
&lt;p&gt;In any case, the handful of people behind the implementations seem unable to grasp what the real life problems are so they live in their little bubble insisting that repeating &amp;quot;but it&#39;s a standard&amp;quot; does anything for users.&lt;/p&gt;
&lt;h3&gt;non-visually&lt;/h3&gt;
&lt;p&gt;MathML captures abstracted, shared concepts of print equation layout traditions. It suffers the consequences in terms of non-visual rendition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Practically&lt;/strong&gt;, MathML support is poor among screenreaders and is non-existing in&amp;quot;simpler&amp;quot; assistive technologies (with way more users) such as &amp;quot;read aloud&amp;quot; features in browsers, voice assistants etc.&lt;/p&gt;
&lt;p&gt;Only three screenreaders formally claim support and their support is limited, buggy, and of poor quality - and the pieces are minimally maintained by AT companies. Brian Richwine&#39;s talk at ATHENs documented many cases, e.g., how some ATs don&#39;t voice &amp;quot;end fraction/script&amp;quot; causing confusion (my favorite part was the anecdata form Apple support saying there&#39;s just not enough users to warrant fixes.) [Though admittedly, I have yet to see someone exhaustively testing them. Nobody is paying to test that, not even publishers who have a vested interest because the &amp;quot;experts&amp;quot; say &amp;quot;just use MathML&amp;quot;.]&lt;/p&gt;
&lt;p&gt;In addition, the user experience of what little support screenreaders provide is often questionable, e.g., JAWS will not read larger MathML fragments but just announce the presence of a math fragment; users have to then open a separate viewer application to actually read and explore the expression. [Fun fact: this is called &amp;quot;Math Viewer&amp;quot; and actually uses MathJax for visual rendering, leading to significant differences from the browser.]&lt;/p&gt;
&lt;p&gt;Similarly, VoiceOver on iOS will open pop-ups for exploration of MathML fragments, creating a separate context and confusion. NVDA needs third party plugins to do anything and has no visual component (i.e., highlighting while exploring)leaving non-blind to guess what is going on.&lt;/p&gt;
&lt;p&gt;I don&#39;t think it&#39;s enough accessibility support in the sense of &lt;a href=&quot;https://www.w3.org/WAI/WCAG21/Understanding/conformance#accessibility-support&quot;&gt;WCAG conformance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Theoretically&lt;/strong&gt;, MathML cannot be accessible in the web&#39;s sense. Heuristics must be applied and now you (frequently) have 2 problems - your MathML must be hacked to work visually and then you might still have to hack around heuristics gone hawywire.&lt;/p&gt;
&lt;p&gt;Back when I led the W3C Math on the Web Community Group, we had fairly productive discussions even with the MathML people and we had strong consensus (with them) that MathML inevitably requires extensive heuristics to provide non-visual (voice, tactile) renditions. Nevertheless, the same people later  turned around again in wider W3C discussions, going back to the spurious claims that MathML is the only viable accessible solution.&lt;/p&gt;
&lt;p&gt;The sad part is that the W3C&#39;s TAG fell for it (and will fall for it again). My guess is because they just don&#39;t care to dig deeper - and why should they? Nobody wants to and anyone sensible will just use MathJax.&lt;/p&gt;
&lt;h3&gt;Other aspects&lt;/h3&gt;
&lt;h4&gt;localization&lt;/h4&gt;
&lt;p&gt;Another argument sometimes mentioned by MathML people is that MathML can be localized automatically. While that&#39;s true to some degree (e.g., Speech Rule Engine can create MathSpeak in a few languages), the benefit seems a questionable if the locale does not match the contextual language, say, coming to an article in Arabic and getting an equation localized in Korean. At the very least, I&#39;m unaware of research substantiating a positive effect.&lt;/p&gt;
&lt;p&gt;In practice, the available localizations in AT is quite limited and pretty undocumented. This is even worse for Braille.&lt;/p&gt;
&lt;p&gt;There are obvious obstacles for localizations. Many equations contain some text elements which would not be localized. Similarly, common terminology has different localizations/abbreviations. For example,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sine as &amp;quot;sen&amp;quot; (ES, IT) or &amp;quot;sin&amp;quot; (EN, DE)&lt;/li&gt;
&lt;li&gt;in geometry SAS/ASA/SSS (EN) vs SWS/WSW/SSS (DE)&lt;/li&gt;
&lt;li&gt;sin^-1 (EN) vs arcsin (EU) (etc)&lt;/li&gt;
&lt;li&gt;repeating decimal notation (above, below / arc vs line)&lt;/li&gt;
&lt;li&gt;open intervals as &lt;code&gt;(...)&lt;/code&gt; or  &lt;code&gt;]...[&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;any operator names the authors may introduce.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While layout-relevant terms might have suitable automatic translations (e.g., start/end fraction), layout traditions can also differ across languages (long division, matrix notation, integral notation).&lt;/p&gt;
&lt;p&gt;Nobody stops to ask: should we do this? As print equation layout is fairly meaningless without lots of context, it&#39;s difficult to imagine a reader that would read content in a language they don&#39;t understand enough to make sense of generic information like &amp;quot;begin/end fraction&amp;quot;.&lt;/p&gt;
&lt;h4&gt;The &amp;quot;Don&#39;t worry about it&amp;quot; argument&lt;/h4&gt;
&lt;p&gt;The MathML community likes to say &amp;quot;don&#39;t worry about the complicated markup, MathML is something that should be generated, not handwritten&amp;quot;&lt;/p&gt;
&lt;p&gt;This is a strange statement full of falsehoods.&lt;/p&gt;
&lt;p&gt;For example, MathML is also advertised as &amp;quot;something you can edit without understanding it&amp;quot;, e.g., while copy checking content, if an editor spots a symbol that does not match the (author) PDF, then - thanks to the structured XML markup - they can fix it without understanding.&lt;/p&gt;
&lt;p&gt;This marketing example also indicates two problems: conversion is not &amp;quot;easy&amp;quot; but riddled with errors, and having an &amp;quot;agnostic&amp;quot; markup  is actually a boon here - to any production person trying to fix a mistyped macro in TeX sources: I see you.&lt;/p&gt;
&lt;p&gt;But of course the same would be true for span soup since the markup really doesn&#39;t matter much here. Though actually, semantics tend get in the way while layout information would be useful. (Again, it makes actually sense for XML production to have a layout focus.)&lt;/p&gt;
&lt;p&gt;Similarly, as described before, MathML supports &amp;quot;moveable limits&amp;quot; (including magical dictionary-based behavior). These features show how important it was for the MathML community to be compatible with authoring traditions instead of focusing on the &amp;quot;pure&amp;quot; layout. If you generate it anyway, why not make it precise and avoid this?&lt;/p&gt;
&lt;p&gt;Finally, this line of argument is self-defeating. If the whole point is to convert from some kind of &amp;quot;real source&amp;quot; to MathML, then it&#39;s just as natural to convert from that &amp;quot;real source&amp;quot; to HTML or SVG.&lt;/p&gt;
&lt;p&gt;Why have an extra markup system that requires an extra rendering process when you can just use what&#39;s already much better supported?&lt;/p&gt;
&lt;h4&gt;Limited support for MathML&lt;/h4&gt;
&lt;p&gt;Among screenreader, only Apple VoiceOver and Vispero&#39;s JAWS claim to support for MathML. In reality, the implementations are full of bugs and gaps. Bug reports are rarely responded to, far less fixed. Apple has essentially communicated &amp;quot;it is what it is&amp;quot;.&lt;/p&gt;
&lt;p&gt;Nemeth Braille support has relied on the liblouis library - which recently discontinued its support of it; liblouis only ever converted MathML to the Nemeth/UEB and German/Dutch/Flamish families braille notations - and it did so quite poorly. [No, the new &amp;quot;cat&amp;quot; on the block doesn&#39;t count either. Old wine, new bottles, just as rusty.]&lt;/p&gt;
&lt;p&gt;More importantly, no &amp;quot;light&amp;quot; assistive technology supports MathML,e.g., &amp;quot;read aloud&amp;quot; features in smaller assistive helpers, in epub reading applications or in voice assistants (Siri et al, e.g., when reading out web content). This lack of support impacts the largest portion of users, since these tools are much more widely used than the more advanced, full-fledged screenreaders like VoiceOver, JAWS, and NVDA.&lt;/p&gt;
&lt;h2&gt;What do we do to address this inherent lack of accessibility?&lt;/h2&gt;
&lt;p&gt;Print equation layout is inherently not accessible in the sense of the web. MathML is a red herring as it (at most) aims to reproduce accessibility traditions from the print era but inherently cannot reproduce it well since those traditions relied on human operators(orators or transcribers). It also only works for English and, it seems, really only for Nemeth users.&lt;/p&gt;
&lt;p&gt;I approach such problems by looking for guidance regarding the web in general. &lt;a href=&quot;https://frankchimero.com/blog/2015/the-webs-grain/&quot;&gt;The web&#39;s grain&lt;/a&gt;, if you like.&lt;/p&gt;
&lt;h3&gt;The web&#39;s core principles&lt;/h3&gt;
&lt;p&gt;&amp;quot;Users before author before implementors before theory&amp;quot;&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&quot;https://www.w3.org/TR/design-principles/#priority-of-constituencies&quot;&gt;W3C design principles&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;User needs come before the needs of web page authors, which come before the needs of user agent implementors, which come before the needs of specification writers, which come before theoretical purity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What do users want from print equation layout? And what do they actually need?&lt;/h3&gt;
&lt;p&gt;We simply don&#39;t know.&lt;/p&gt;
&lt;p&gt;Yes, most countries do &lt;em&gt;something&lt;/em&gt; at the school level. But actual research is barely existent; what little there is, is usually limited to small groups and all the biases of simplistic educational studies.&lt;/p&gt;
&lt;p&gt;My impression has been that there are too few AT users among the mathematical community - support is so poor, few make it to college let alone research-level. Accordingly, few have both the skills and the luxury to think freely about the problem. (T.V. Raman is one of the few exceptions and I&#39;m still grateful he gave Davide and me the opportunity to work with the ChromeVox team for a bit - and to meet Volker Sorge who was on sabbatical at Google Research back then.)&lt;/p&gt;
&lt;p&gt;Anecdotally, the most consistent feedback I&#39;ve seen in educational studies comes down to &amp;quot;I want it to be read like my teacher/prof reads it in class&amp;quot;. That sounds an awful lot like users want to have what authors want to say, and matches the core principles pretty well. It&#39;s one of those &amp;quot;did you need a study for that?&amp;quot; moments (yes, we do) and yet it shows how flawed the current approaches are. Yes, it&#39;s a common paradigm that authors should enable AT users to customize their output. But if you take away the author&#39;s ability to express themselves, are you really helping users?&lt;/p&gt;
&lt;p&gt;Assuming I&#39;m wrong in my assessment for lower level equation layout: for mathematically heavy research material? There&#39;s absolutely nothing to base an opinion on.&lt;/p&gt;
&lt;p&gt;Those users probably want TeX annotations because research-level users very likely know TeX syntax. But is that what they &lt;strong&gt;need&lt;/strong&gt;, e.g., when exploring a complex expression? [Fun fact, some European communities favor presenting TeX strings in Braille - starting from first grade.]&lt;/p&gt;
&lt;h3&gt;interlude&lt;/h3&gt;
&lt;p&gt;This discussion is usually skewed by two major factors: US users and blind users. As with most of the web, the US dominance has led to people ignoring other factors. If it works for US users, it&#39;s fine, amiright. So the limited MathSpeak and Nemeth implementations in MathPlayer were enough to woo the US blind community and its influence wooed the rest.&lt;/p&gt;
&lt;p&gt;To take an example. Blind users (especially students) need to write assessments using the tools their classes require. If they need to use MS Word, they&#39;ll need something to paste into Word. MathML was originally conceived as just such an exchange format for print equation layout - abstracting the minimal common ground so that each engine could reasonably present it.&lt;/p&gt;
&lt;p&gt;However, the authoring problem requires conversion. This easily goes haywire. For example, Libre office&#39;s &amp;quot;Math editor&amp;quot; digests MathML but converts it into its own linearized (~Asciimath like) syntax. And while it includes MathML in its file, that MathML will be generated from the linearized format. This is a process full of bugs.&lt;/p&gt;
&lt;p&gt;MS Word does the same thing but has a custom XML format for print equation layout. Same approach, same problems. It doesn&#39;t help to add more formats (such as TeX-like input).&lt;/p&gt;
&lt;p&gt;MathML-based wysiwyg editors exacerbate the problem (as wysiwyg is wont to do) since they cannot easily discern logical structure of input // do not make it apparent to users that they&#39;re doing something really terrible (e.g., inserting random whitespace, flip-flopping font choices).&lt;/p&gt;
&lt;p&gt;However, if you are a blind user who knows Nemeth well, then that conversion process might just work sufficiently well for you because things were designed just for this use case. Ergo, you will say it&#39;s great and your influential community will prevent any discussion about whether this is actually a good trade off for other user needs.&lt;/p&gt;
&lt;p&gt;At ATHENs earlier this year, all speakers I heard included at least one statement along the lines of: &amp;quot;our successful students are those who know Nemeth&amp;quot;. Clearly, it&#39;s good that Nemeth helps students succeed but in the words of a spaceship captain: &amp;quot;Not good enough, dammit, not good enough&amp;quot;.&lt;/p&gt;
&lt;p&gt;The reality of all users is: the web is fairly bad at being copy&amp;amp;pasted elsewhere. And that&#39;s an interesting, complex, and very broad problem. I&#39;d love to see more movement in this direction but I don&#39;t think it makes sense to make copy&amp;amp;paste to MS Word a requirement for realizing print equation layout on the web when nothing else on the web can do this. (Pasting SVG into Word anyone?)&lt;/p&gt;
&lt;p&gt;[Repeated conversion tends to be lossy. From my limited yet painful experience of editing conversion results, I doubt it will be great if you went back and forth across print equation systems for a bit; if I cared enough, I&#39;d test that hypothesis.]&lt;/p&gt;
&lt;p&gt;Ultimately, Braille is a print technology, and  in the context of a refreshable display there isn&#39;t even a page, let alone a multidimensional presentation that a dynamic webpage can provide. Again, this is a broad and interesting problem, again print equation layout is not a great case for special treatment.&lt;/p&gt;
&lt;p&gt;Similarly, Unicode math alphabets are a print technology. It makes sense in a print context to separate out codepoints that have very special fontface requirements. They work fairly terribly in terms of non-visual rendering. &lt;a href=&quot;https://mathstodon.xyz/@pkra/108231823892676435&quot;&gt;For example, I once tested&lt;/a&gt; the string &lt;code&gt;ϕ(x)=∑ₖⁿ xᵏ&lt;/code&gt; and it came out as &amp;quot;open paren x close paren equals n-ary summation n x&amp;quot; (JAWS) or &amp;quot;x equals n-ary summation superscript n x&amp;quot; (NVDA) and &amp;quot;phi x equals to the power of n x&amp;quot; (Chrome speech synthesis). Yay us. Now you might argue that in the right markup, voicing could be different, e.g., automatically more verbose. And that&#39;s an excellent point - but I would couner that math alphabets are far from the only Unicode characters with these kinds of problems - emoji for one are even more prevalent than math alphabets to fill account names or social media postings - and a better solution would be a general one, not &amp;quot;math markup&amp;quot;.&lt;/p&gt;
&lt;p&gt;Another quirk of the accessibility traditions around print equation layout is what layout information is exposed. Font variants, upper/lower, under/overlines are all exposed - yet color is not. Why? Likely because print traditionally doesn&#39;t use (a lot of) color out of the pure necessity that color print is not readily available. But that is not stopping authors; color is a useful and beautiful tool. Of course, it&#39;s &amp;quot;obvious&amp;quot; that non-visual media won&#39;t present color well but neither will it present different typefaces well - and somehow that was ok with everyone. This is particularly strange, when you find dedicated syntax for &amp;quot;mathcolor&amp;quot; with no effect.&lt;/p&gt;
&lt;h3&gt;What to want for authors&lt;/h3&gt;
&lt;p&gt;I want to give authors the ability to express themselves well.&lt;/p&gt;
&lt;p&gt;And authors &lt;strong&gt;want&lt;/strong&gt; to do that.&lt;/p&gt;
&lt;p&gt;Just look at any &amp;quot;list of symbols&amp;quot; in a monograph, e.g., from &lt;a href=&quot;https://bookstore.ams.org/gsm-210/&quot;&gt;this combinatorics book&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.peterkrautzberger.org/assets/2022/gsm210-notation.png&quot; alt=&quot;a screencapture from a list of notations with short definitions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Despite using very ambiguous terms, the authors clearly have a strong intententions.&lt;/p&gt;
&lt;p&gt;I want authors to bring their intententions into the process.&lt;/p&gt;
&lt;p&gt;We can (barely) do that with Speech Rule Engine. We cannot do that with MathML.&lt;/p&gt;
&lt;h3&gt;What do you want for your users?&lt;/h3&gt;
&lt;p&gt;I want to take responsibility for my content.&lt;/p&gt;
&lt;p&gt;I want to provide high-quality consistent, robust rendering for all users.&lt;/p&gt;
&lt;p&gt;I want to provide visual layout that is robust against font issues (especially onepub).&lt;/p&gt;
&lt;p&gt;I want to provide speech and braille alternatives.&lt;/p&gt;
&lt;p&gt;I want to provide consistent, reliable, robust exploration.&lt;/p&gt;
&lt;p&gt;I want to be able to fix issues.&lt;/p&gt;
&lt;p&gt;I want to be able to provide additional features (e.g., convenient access to TeX sources, customization options).&lt;/p&gt;
&lt;h3&gt;What do you do?&lt;/h3&gt;
&lt;p&gt;I usually provide high-quality visual rendering using MathJax&#39;s SVG output, alongside the high-quality heuristics in Speech Rule Engine to generate non-visual rendering.&lt;/p&gt;
&lt;p&gt;I also generate fully explorable layout and descriptions that are the same for everyone everywhere. They are robust enhancements with solid fallbacks. If there is a problem, it is my fault, not the user&#39;s. And if need be I can fix it in post.&lt;/p&gt;
&lt;p&gt;I tend to use the MathSpeak ruleset for descriptions meant for voicing because MathSpeak doesn&#39;t attempt to be as &amp;quot;smart&amp;quot; as other available heuristics (which target school level mathematics).&lt;/p&gt;
&lt;p&gt;I use Nemeth Braille because content is primarily in English. Arguably, Speech Rule Engine doesn&#39;t have any support for other Braille output so I accept that I need to use Nemeth for other languages. I&#39;m not beyond using TeX as quick alt text. If I want more, I&#39;ll try to help Speech Rule Engine get funding.&lt;/p&gt;
&lt;p&gt;I make sure that speech descriptions work with all tools - from full-fledged screenreaders and simple read-aloud tools. If there are issues (e.g., a recent VoiceOver on iOS regression I ran into), I can work around it.&lt;/p&gt;
&lt;p&gt;Yes, the ARIA technique for exposing Braille alongside speech-optimized text is cutting edge and not widely supported yet. But they went from editor&#39;s draft to full browser support in 2 years and they help much more than just print equation layout.&lt;/p&gt;
&lt;p&gt;I often embed TeX sources that automatically copy&amp;amp;paste when you select things; it&#39;s a nice bit of magic under my control.&lt;/p&gt;
&lt;p&gt;I set things up so as to allow for additional options to expose various formats (e.g., populating everything with TeX or loading full-on client-side MathJax).&lt;/p&gt;
&lt;p&gt;I do so with intent and knowledge of the problem space, the capabilities of the solution I built and the flexibility to help users.&lt;/p&gt;
&lt;h3&gt;interlude&lt;/h3&gt;
&lt;p&gt;Some good old whataboutism:&lt;/p&gt;
&lt;p&gt;What about electrical engineering diagrams? Same garbage print stuff but at least nobody pretends to cook up markup. Take diodes - &lt;a href=&quot;https://www.electrical-symbols.com/electric-electronic-symbols/diode-symbols.htm&quot;&gt;their symbols&lt;/a&gt; for their types are basically letter like. Shouldn&#39;t we have those in Unicode?&lt;/p&gt;
&lt;p&gt;What about chemical markup? ChemML is another wonderful XML abomination.&lt;/p&gt;
&lt;p&gt;What about, again, music notation?&lt;/p&gt;
&lt;p&gt;Should all of these be first class citizens on the web, with separate rendering systems to adhere to their particular (print) histories?&lt;/p&gt;
&lt;h2&gt;Tangents&lt;/h2&gt;
&lt;h3&gt;copy &amp;amp; paste&lt;/h3&gt;
&lt;p&gt;The MathML community will frequently point out how &amp;quot;easy&amp;quot; it supposedly is to copy&amp;amp;paste MathML around.&lt;/p&gt;
&lt;p&gt;The reality is that extremely few applications accept MathML, it&#39;s a cumbersome process, and MathML is dropped in the end, converted to whatever internal format is used by the target application.&lt;/p&gt;
&lt;p&gt;The primary reason why this myth holds is that MS Word digests MathML. However nowadays, MS Word also accepts (reasonable math mode) TeX when pasting into &amp;quot;equation fields&amp;quot; which shows both the limited usefulness of MathML as well as the pervasiveness of (math mode) TeX. And of course Word uses its custom markup syntax internally (and its home-grown technology for braille and voice rendering instead of whatever ATs provide).&lt;/p&gt;
&lt;p&gt;More importantly, while copy&amp;amp;paste is a worthwhile challenge, it is also a general challenge with web content. Copy&amp;amp;pasting website content into any kind of text editor (word processor, email application, TeX sources) is very poorly supported in general. While headings might survive, even something as simple as a list can break, tables usually break and &amp;quot;true&amp;quot;web design (e.g., CSS grids) does not survive. Even (inline) SVG content is not supported widely (despite being a well-supported, self-contained graphics format).&lt;/p&gt;
&lt;p&gt;This is not at all surprising given how most of the targets are meant to create print layout - and struggle with web layout. Why then should equation layout on the web be less complicated than the rest? Instead of insisting on broken technology (like print equation layout and specifically MathML), it would be better to think about the more general problem that affects everyone. We have to anyway and a niche solution does not move us forward (yes, HTML v3 did have it wrong).&lt;/p&gt;
&lt;h3&gt;authoring and recreating&lt;/h3&gt;
&lt;p&gt;Related to copy&amp;amp;paste is the area of authoring and recreating print equation layout.&lt;/p&gt;
&lt;p&gt;Let&#39;s go back to the example of matrix determinants. Both &lt;code&gt;det(A)&lt;/code&gt; vs&lt;code&gt;|A|&lt;/code&gt; would be voiced as &amp;quot;determinant of A&amp;quot; yet their authoring is quite different.&lt;/p&gt;
&lt;p&gt;Sometimes, readers have to (re)create content to match someone else&#39;s example (e.g., in any educational setting). Again, this is a general problem (color vs colour). For text, AT usually provides character-by-character navigation (incidentally, I&#39;m not sure about graphemes more generally). For print equation layout this is naturally trickier: on the one hand, we come back to the idea to &amp;quot;just&amp;quot; give layout information, on the other hand layout may be insufficient (e.g., table-like).&lt;/p&gt;
&lt;h3&gt;Voice input/output&lt;/h3&gt;
&lt;p&gt;Voice input is becoming an important aspect of how users interact with web content, both using AT (e.g., in combination with Dragon NaturallySpeaking) as well as using general purpose tools such as smart assistants (Siri, Alexa etc).&lt;/p&gt;
&lt;p&gt;Despite funded efforts, there has been not even light progress for enabling print equation layout by voice input.&lt;/p&gt;
&lt;h3&gt;Audio and Video Captioning&lt;/h3&gt;
&lt;p&gt;Providing accessible audio and video media requires captions, in particular transcripts, subtitles, and audio descriptions. For content rich with print equation layout, automatic conversion requires voice recognition, image analysis, and conversion to speech (e.g., a mathematical lecture with equations on a blackboard that the speaker discusses). Since the standard formats (e.g., for subtitle functionality in a video player) require plain text, MathML is of no use here even if it were an adequate fit for how people speak. In addition, there are no tools nor traditions around &amp;quot;speaking equation layout&amp;quot; in such settings.&lt;/p&gt;
&lt;p&gt;Reversely, there&#39;s audification. Take &lt;a href=&quot;https://play.google.com/books/publish/autonarrated/&quot;&gt;Google&#39;s AI-driven audio book narration&lt;/a&gt;. I doubt it&#39;ll do print equation layout anytime soon (by which I mean: ever).&lt;/p&gt;
&lt;h3&gt;Search&lt;/h3&gt;
&lt;p&gt;Despite decades of research, search around MathML has never provided tangible benefits despite clear user needs to discover existing results both to avoid and identify duplicate research. This is true for all of the ever expanding STEM fields.&lt;/p&gt;
&lt;p&gt;Springer&#39;s discontinued LaTeX search or zbmath&#39;s MathML based system are good examples of this failure. A sizeable research corpus revolves around analyzing MathML fragments, yet no actually useful system is available.&lt;/p&gt;
&lt;h3&gt;Progress&lt;/h3&gt;
&lt;p&gt;Here&#39;s a very personal opinion: the MathML people have actively worked against progress for equation layout on the web, and STEM content as a larger issue.&lt;/p&gt;
&lt;p&gt;Why do I say this? Because anyone who has followed this area as closely as I have in the past decade or so (which, alas, is barely a handful of people) know that it&#39;s the MathML people who insist that MathML must be implemented at all costs, actively damaging other approaches, even approaches with better results for users.&lt;/p&gt;
&lt;p&gt;To the purists, MathML is more important than users, authors, and browsers.&lt;/p&gt;
&lt;p&gt;As a result, projects that made actual progress (like MathJax, mathlive, hell - even KaTeX) are in danger of losing support from their communities because people are being told &amp;quot;you must use MathML&amp;quot;. [Don&#39;t get me wrong, I can gripe about these projects, too, but at least they move.]&lt;/p&gt;
&lt;p&gt;All the while, there&#39;s no indication that MathML development will speedup at all, even if Chrome ships the new, cut-down, incompatible spec.&lt;/p&gt;
&lt;p&gt;So if you ever again complain about the lack of improvements in this space, blame the MathML people. They are to blame. We could have made many small improvements to the platform that would have benefited everyone if the MathML people hadn&#39;t killed everything that didn&#39;t suit them and drove away anyone who didn&#39;t agree with them.&lt;/p&gt;
&lt;p&gt;Reversely, MathML is being held back where it actually shines - XML print production. There&#39;s so much useful print equation content to consider for XML workflows (tikz alone!); extending MathML would be natural here. Instead, even the XML people get nothing (or worse - a cut down spec they can&#39;t use until JATS adopts it).&lt;/p&gt;
&lt;p&gt;I wrote earlier that &amp;quot;web equation layout&amp;quot; is not something that exists. So you might say, what&#39;s the problem? I think the problem is that we might get stuck, in a bad way. Here&#39;s quote from &lt;a href=&quot;https://arxiv.org/pdf/1511.08033v1.pdf&quot;&gt;On Mathematical Symbols in China&lt;/a&gt; (again, thanks to Christian&#39;s &lt;a href=&quot;https://read.somethingorotherwhatever.com/entry/OnMathematicalSymbolsinChina&quot;&gt;Interesting Esoterica&lt;/a&gt;).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ancient Europeans struggled with fractions because of the use of tedious Roman numerals, so ancient Europeans were weakest in algebra. However, when Indian-Arabic numerals were introduced into Europe, four operations in Europe were simplified. [... As] a result, algebra developed rapidly. Similarly, China&#39;s ancient mathematics failed to form a perfect symbol system and were stagnant because of the limits of the counting rod, resulting in not only a lack of the axiomatic system, but also the absence of symbolic algebra, analytic geometry, calculus and modern variable mathematics in China. Naturally, to move forward it was necessary to break barriers and to get in touch with international standards and to forward to symbolic mathematics. This shows that the extension and development of any prosperous culture cannot be confined to the boundaries of that nation, and it is hard to create a complete and rich symbol system independently and separately from the influence of other nations and the outside world.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think the web should not be confined to print equation layout. We should not want to get stuck replicating a limited medium in a much more versatile one.&lt;/p&gt;
&lt;p&gt;MathML is the zombie of web standards. Each decade brought 1 additional, differently broken implementation (first Firefox, then Safari, soon Chrome). The new spec for Chrome will render a lot of existing content unexpectedly or not at all, but it will be the author&#39;s fault - how clever of them. XML people will have even more &amp;quot;fun&amp;quot; getting their content to work on the web. (Or, more likely, just continue to use MathJax which just keeps getting better instead of worse.)&lt;/p&gt;
&lt;p&gt;Bugs in those three disjoint implementations are, of course, aplenty; I find a new one every time something forces me to look at native MathML implementations, some are ridiculously bad (wiping out HTML content is one of my favorites) and most bugs go for years without bugs even being filed (remember when Safari didn&#39;t draw fraction lines reliably for over a year? No, you don&#39;t because you don&#39;t use it and nobody even filed a bug).&lt;/p&gt;
&lt;p&gt;For the web, the best case scenario is: nobody cares and nobody uses it. It will become the new font/center/marquee or whatever.&lt;/p&gt;
&lt;p&gt;Even if the MathML people were interested in moving the layout facilities into CSS, we&#39;ll get, at best, something like CSS tables. If you don&#39;t know the &amp;quot;joy&amp;quot; of working with CSS tables: to this day they are not able to suitably realize HTML table layout, they were super buggy for the longest time and are still buggy in many edge cases (despite years of active improvements). But  most of all: people realized they actually needed something completely different, i.e., CSS grid, flexbox, and container queries.&lt;/p&gt;
&lt;p&gt;Accessibility wise, the shoddy implementations in JAWS and VoiceOver will continue to barely help English speaking blind users. Soiffer will try to push his weird MathPlayer clone into every AT so that he can continue to claim patently false things. No &amp;quot;intent&amp;quot; in the world will make print equation layout accessible in the sense of the web. Nemeth users might eventually get what they want - the rest? Not so much.&lt;/p&gt;
&lt;p&gt;In other words, users will continue to find themselves out of luck. But hey, &amp;quot;it&#39;s a standard&amp;quot;. They made you beg for it, then they begged you for money (instead of Apple and Google); now you have something you don&#39;t want, something that doesn&#39;t work and something that cannot work. It&#39;s what you wanted, a &amp;quot;standard&amp;quot;.&lt;/p&gt;
&lt;p&gt;I think MathML (this of course means: its community) is fundamentally not willing and not able to be a positive force. It&#39;s &amp;quot;MathML or die&amp;quot; all the way. The zombie standard continues its shambling walk.&lt;/p&gt;
&lt;h2&gt;interlude&lt;/h2&gt;
&lt;p&gt;The future will be different from what you expect. The other day/month/year, our oldest started learning the piano. Of course, this means learning musical notation; it&#39;s one of the benefits, I&#39;d say. But it&#39;s (inevitably) hard to motivate.&lt;/p&gt;
&lt;p&gt;So being a stupid internet person, I searched around for fun apps that teach the piano. I found apps that, shockingly, didn&#39;t really use traditional musical notation. Instead, they had some barebones resemblance (I&#39;m guessing to enable later learning) but they took things in another direction by leveraging the medium, i.e., modern mobile applications.&lt;/p&gt;
&lt;p&gt;These apps would listen via microphone, give immediate feedback on correctness in a range of properties (dynamically increasing tolerance for errors). They would use their infinite canvas to provide just-in-time context in large visuals to keep silly younglings focused and unconcerned about length. They would play along simple training material providing more complex arrangements so that even an early learner would not be facing dull pieces of repetitive notes. They would immediately get you to jump into contemporary pop songs, sync highlighting along the way, bringing enormous satisfaction. They were just generally being impressively awesome. None of it with traditional musical print notation.&lt;/p&gt;
&lt;h2&gt;Quo vadis?&lt;/h2&gt;
&lt;p&gt;So where do we go from here?&lt;/p&gt;
&lt;p&gt;I&#39;m not saying you shouldn&#39;t put print equation layout on the web. I do it for a living; knock yourself out.&lt;/p&gt;
&lt;p&gt;I am saying that you should consider two things when you do.&lt;/p&gt;
&lt;h3&gt;even mildly complex print equation layout is simply a bad type of content.&lt;/h3&gt;
&lt;p&gt;Find better ways, communicate better. You don&#39;t need to go all &amp;quot;Kill math&amp;quot; about it. But for crying out loud experiment, be creative. Or at the very least copy creative people.&lt;/p&gt;
&lt;p&gt;There are soooo many good people out there experimenting -- just check math teachers on twitter and you&#39;ll find wondersome and wonderful experimentation - shout out to &lt;a href=&quot;https://www.visualpatterns.org/&quot;&gt;Fawn&#39;s visual patterns&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Think about responsive arrangements of your equation layout. Think about animations of equation content. Think about mixing imagery with complex text content and only a touch of equation layout. Think about scrollytelling techniques. Think about using something else instead. And fail! Fail again! Fail well! Fail at failing until you fail to fail.&lt;/p&gt;
&lt;p&gt;The web is a wonderous medium. Its grain is fantastic to work with, read its direction, feel it out, mess with it.&lt;/p&gt;
&lt;h3&gt;story telling&lt;/h3&gt;
&lt;p&gt;Way back when I disliked &lt;a href=&quot;http://worrydream.com/KillMath/&quot;&gt;Bret Victor&#39;s Kill math&lt;/a&gt; for actively ignoring the power of compression of equation layout.&lt;br /&gt;
But nowadays I don&#39;t like it for its lack of imagination regarding the presentation of equation layout. It just gives up and uses a completely different medium. Before you ask, I didn&#39;t like the &lt;a href=&quot;http://worrydream.com/ScrubbingCalculator/&quot;&gt;scrubbing calculator&lt;/a&gt; that much either - even when you ignore that it&#39;s not accesible. But at least the idea Explorable Explanations inspired &lt;a href=&quot;https://explorabl.es/&quot;&gt;more&lt;/a&gt; &lt;a href=&quot;https://landgreen.github.io/physics/&quot;&gt;people&lt;/a&gt;. I readily admit I might be jaded and just can&#39;t find anything good in mathematical examples while I enjoy lovely examples like &lt;a href=&quot;https://halftone.co/projects/why-is-the-sky-blue/&quot;&gt;why is the sky blue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Apps like &lt;a href=&quot;https://soulver.app/&quot;&gt;soulver&lt;/a&gt; showed a while ago how some kinds of complex equation layout might become unnecessary. &lt;a href=&quot;https://www.tandisgame.com/&quot;&gt;Tandis&lt;/a&gt; is mesmerizing and I suspect accessibility questions around these could teach us an awful lot more than any MathSpeak/Nemeth debate.&lt;/p&gt;
&lt;p&gt;Where are the animated equations? What would that even look like?&lt;br /&gt;
Where are the scrollytelling explanations of deep concepts in new ways?&lt;br /&gt;
Where are at least typesetting animations for equation layout?&lt;br /&gt;
Why capture a complex diagram statically when you can send something that dynamically or interactively draws it?&lt;/p&gt;
&lt;h3&gt;if you do it, do it accessibly and that means do accessibility yourself&lt;/h3&gt;
&lt;p&gt;If you put print equation layout on the web, be aware that you&#39;re bringing inaccessible content to the web. I&#39;m happy to point anyone to WCAG 1.1.1 because every print equation layout tradition clearly separates its use from text. TeX literally separates &amp;quot;math mode&amp;quot; from &amp;quot;text mode&amp;quot; - you can&#39;t be much clearer. Other systems do the same thing: in MS Word and similar tools, the equation editing will be found in an entirely separate UI area from text editing. The two are disjoint. Call me non-intuitionistic but &amp;quot;not text&amp;quot; is &amp;quot;non-text&amp;quot;.&lt;/p&gt;
&lt;p&gt;Essentially, you&#39;re putting up a structured image. SVG is the best mental model here: if you generate a diagram with mermaidjs, you could use some of your input to enable accessibility. But you probably have to do a whole lot more.&lt;/p&gt;
&lt;p&gt;You don&#39;t have to do everything yourself. Speech Rule Engine provides you the best heuristics on the market and &lt;a href=&quot;https://www.peterkrautzberger.org/0215&quot;&gt;a direct way to bring your own&lt;/a&gt; - whether you bring a plain label (that still offers SRE&#39;s heuristics and layout information) or you write your own heuristics, it&#39;s there to build upon.&lt;/p&gt;
&lt;p&gt;Above all: be mindful what you want to do. If you want to provide TeX as non-visual presentation, do that; you are the author and it&#39;s your prerogative. Just tell users and be open to criticism.&lt;/p&gt;
&lt;p&gt;If you want to provide more standardized descriptions, use Speech Rule Engine to generate them. You can provide braille-specific descriptions alongside speech-oriented ones. MathJax and SRE are the best there is for that kind of thing. If you don&#39;t use JS on the client, you can still do things server-side. If equation layout gets complex, use an ARIA tree or tree grid with a modicum of client-side JS (check &lt;a href=&quot;https://github.com/pkra/peterkrautzberger.org/&quot;&gt;this site&#39;s source&lt;/a&gt; to see one way of doing that); several ideas are being discussed in the standards world which might make exploration work without client-side JS in the future.&lt;/p&gt;
&lt;p&gt;If any of that sounds interesting but you don&#39;t know where to go next, feel free to get in touch, I&#39;d love to help.&lt;/p&gt;
&lt;h2&gt;The end&lt;/h2&gt;
&lt;p&gt;You&#39;ve made it this far. Incredible! Time to move on. Here, read &lt;a href=&quot;http://hackeducation.com/2022/03/08/hope&quot;&gt;this transcript of a talk by the wonderful Audrey Watters&lt;/a&gt; about hope. I wished I could write as well as her but alas.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Granularity walkers for textual descriptions</title>
		<link href="https://www.peterkrautzberger.org/0219/"/>
		<updated>2023-08-20T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0219/</id>
		<content type="html">&lt;p&gt;I started volunteering at an elementary school this year, helping kids learning to read. I&#39;m not an elementary school teacher (and no amount of university teaching would save me) but luckily anyone who can read can help; it&#39;s all about patience and the simple reality of being present for a child. One of the textbooks typesets syllables in different colors to help the inexperienced readers. This simple typograhic tool reminded me of a problem I&#39;ve been thinking about for a while now: enabling users on the web to adjust the granularity of textual descriptions.&lt;/p&gt;
&lt;p&gt;Before we get started, let me give you a quick overview over what to expect in this awfully long bit of rambling on my part. First, I&#39;ll try to give you a rough outline of the thinking behind this idea, followed by a couple of code examples and the reasoning behind their evolution. Finally, there&#39;ll be a more fully fledge example, including demos on the authoring experience and the user experience (across major screenreaders).&lt;/p&gt;
&lt;p&gt;When I have to provide a textual description, it often helps me to strike up a conversation with someone without access to the object or context (e.g., over the phone). Rubber-ducking such a conversation works, too, e.g., think about it as a form of conversational chatbot (well, if such a thing actually existed without irreperable damage to society). Reversely, you could try and coax a description out of a child. Anyways.&lt;/p&gt;
&lt;p&gt;What happens naturally in my experience is that you&#39;ll start from a coarse description (say, &amp;quot;it&#39;s a photo of a cat&amp;quot;) and then naturally progress towards finer and finer details. For example, &amp;quot;it&#39;s a black and white photo, a cat appears in close-up, with black fur but white paws, prowling right-to-left through a grass field; the background is out of focus but appears to show a clear sky above a winter landscape of a snowy field with snow-laden conifers far off to the right&amp;quot;).&lt;/p&gt;
&lt;p&gt;Now such results may not be good advice for when you&#39;re asked to create a bespoke textual description because, e.g., in the context of WCAG, textual alternatives are there to serve the same purpose as what they describe. However, often you can tweak this approach to fit the relevant context. So if it&#39;s, say, a picture shown to exemplify a particular cat breed, then you&#39;d focus on more and more details relevant to differentiating cat breeds such as coat length and pattern.&lt;/p&gt;
&lt;p&gt;Sometimes you are also simply in luck and you have extremely detailed descriptions for the exact purpose at hand. This happens, for example, with content that is generated from rich, heavily annotated (or annotatable) data such as &lt;a href=&quot;https://iiif.harvard.edu/mirador-viewer/&quot;&gt;archival images&lt;/a&gt;, &lt;a href=&quot;https://jupyter.org/&quot;&gt;data visualization&lt;/a&gt;, &lt;a href=&quot;https://mermaid.js.org/&quot;&gt;diagrammatic content&lt;/a&gt;, or &lt;a href=&quot;https://www.peterkrautzberger.org/0218&quot;&gt;print equation layout&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In such situations (or, you know, when the kid you&#39;re talking to is very chatty) you can easily have an &lt;em&gt;overwhelming&lt;/em&gt; amount of information that could be useful but is probably confusing for users if you use all of it as textual description. That&#39;s why we have &lt;a href=&quot;https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html#non-text-content-situation-1&quot;&gt;an abundance of techniques around long form descriptions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While it&#39;s great to have textual descriptions available to everyone, I think there is a specific boon to annotating &lt;em lang=&quot;la&quot;&gt;in situ&lt;/em&gt;, so to speak. That means, not just having concise descriptions at the location of non-textual content but have overly verbose ones there, too. The former enables users to efficiently access the context as whole, the latter enables them to deeply dive into the particulars of the content that is being described, immerse themselves, relish in the details.&lt;/p&gt;
&lt;p&gt;Ideally the levels of detail are malleable so that you enable the user to decide which level of detail they need in the flow.&lt;/p&gt;
&lt;h2&gt;web accessibility for complex content isn&#39;t great&lt;/h2&gt;
&lt;p&gt;This might be a bit controversial but I think web accessibility doesn&#39;t really do much for complex content. HTML deals with &amp;quot;documents&amp;quot;, whatever that means, but provides only a fairly minimal set of content structure - sectioning, headings, lists, figures, tables - before you grab a generic element. Even in contemporary HTML there isn&#39;t much for complex, non-interactive content. Sure, tables are kind of neat from an accessibility point of view - but they are for data. Figures have some nice logic with grouping and captioning at least, but are very specific in scope. When I think of contemporary HTML improvements, I think of dialog, popup, select menu. All are important, none of them are helping with complex &amp;quot;static&amp;quot; content. I always feel that SVG is overlooked in this context but it does provide a notion of graphic document and it does cover basic geometric shapes.&lt;/p&gt;
&lt;p&gt;ARIA doesn&#39;t go much beyond HTML when it comes to content. Yes, there&#39;s the dpub-aria module which in my eyes suffers too much from its epub history to be useful to users. You might say it&#39;s simply in the name: ARIA stands for accessible rich internet applications, after all. If you&#39;ve followed the recent discussion arount aria-flowto, you&#39;ll have noticed that a fair number of experts feel that ARIA provides too little for complex diagrammatic content such as flow charts. The same is true for many data visualizations or simply complex drawings, collages, annotated archival images etc. All of these are &amp;quot;static&amp;quot; (even if easily responsive these days) but their exploration is usually very complex.&lt;/p&gt;
&lt;p&gt;Just take the classic meme with a pin board detailing a conspiracy theory, full of photos, newspaper clips, and notes, chaotically connected by strings across the board. How would you make that into an accessible piece of content on the web?&lt;/p&gt;
&lt;p&gt;One good answer is indeed: applications. Maps are a good example for that because they&#39;ve embraced the dynamic abilities of the web. Digital maps have moved so far away from their print origins - it&#39;s hard to actually get a decent print product out of them again (not the least of which are the many many options you have nowadays).&lt;/p&gt;
&lt;p&gt;Another good answer is: use the infinite canvas of the web to simplify through deconstruction. What I mean by that is that much complex content comes from print traditions where you have to cram a lot of information into the limited dimensions of print formats. On the web, you can often untangle content into smaller pieces that are more usable (and accessible). Perhaps you do this by breaking the complexity down into several steps. For example, a school-level geometric construction in a print publication (or, sadly, on the web) is often overlaying many steps at once rather than creating many diagrams for the individual steps. Such a series of diagrams would be far less complex to convey non-visually as the individual step&#39;s description can focus on a small amount of changes, despite the overall complexity. You can even still combine these back to a single graphic if you need.&lt;/p&gt;
&lt;p&gt;So, at the very least in theory, I believe we could often use the infinite size of a web page or even a series of web pages to achieve an accessible presentation where the print equivalent would be nigh impossible to make accessible.&lt;/p&gt;
&lt;p&gt;And yet, when push comes to shove, it&#39;s not easy. We lack traditions. As a result, it&#39;s easier to build an accessible, vastly complex autocomplete searchbox today than to build an accessible version of Gray&#39;s Anatomy or Byrne&#39;s version of Euclid&#39;s elements.&lt;/p&gt;
&lt;h2&gt;Users before authors before implementors&lt;/h2&gt;
&lt;p&gt;Call me oldfashioned but I put my trust in authors. Maybe it&#39;s out of desperation; without authors, there&#39;s nothing for users and implementors can implement all they like, it doesn&#39;t matter without authors. [Shout out to all the web developers still building with floats and jquery today!]&lt;/p&gt;
&lt;p&gt;So if we lack traditions, we must build them. No amount of wishing for magical solutions from the print era will help us. Yes, a braille embossable version of a complex diagram is a fantastic achievement to pull off from the same source as your print content. But does that help us move everyone forward?&lt;/p&gt;
&lt;p&gt;For me this also means that those who build tools should build them so that they&#39;re easy to be adapted by authors. Education, it&#39;s a thing.&lt;/p&gt;
&lt;p&gt;Obligatory Baldur Bjarnason quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Idle thought: most Open Source Software, certainly most OSS on npm, shouldn’t be OSS but should instead be educational resources&lt;br /&gt;
&lt;a href=&quot;https://notes.baldurbjarnason.com/2021/05/03/idle-thought-most.html&quot;&gt;https://notes.baldurbjarnason.com/2021/05/03/idle-thought-most.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;trees, may be too complex&lt;/h2&gt;
&lt;p&gt;But I digress. If you have followed my previous attempts at exploring accessibility patterns for complex content, then you might ask: Peter, didn&#39;t you say the ARIA tree pattern is the great solution? (If you don&#39;t, here&#39;s a quick recap: way back when, I got David Tseng, Volker Sorge, and Davide Cervone to create a minimal version of the ChromeVox/Speech-Rule-Engine/MathJax&#39;s walker that would work on server-side rendered MathJax output. Later, Volker helped me stabilize that into a small library and I continued to &amp;quot;dismantle&amp;quot; it so that it &lt;a href=&quot;https://github.com/krautzource/aria-tree-walker&quot;&gt;became more of a multi-purpose tool&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Anyway, I&#39;m pretty sure I never said or wrote that but, clearly, I found many benefits from re-purposing this traditional ARIA pattern to complex content fragments. The tree pattern is well supported and comes with many nice conveniences for users. Hoever, I struggled for quite some time now to get where I really wanted to get with this pattern.&lt;/p&gt;
&lt;p&gt;Firstly, mobile support has proven elusive. Right now, I&#39;m at a point where I think I understand things well enough to say that mobile AT is simply too limited to allow for how I re-purposed the tree pattern.&lt;/p&gt;
&lt;p&gt;Secondly, it seems it&#39;s still too complex. Even MathJax v4&#39;s alpha failed to properly adopt it (despite my help) and I&#39;ve seen few people trying to build on, say, the tikz examples in the code base, let alone any interesting different kinds of content (e.g., musical staff or chord notation, physics content such as Feynman diagrams).&lt;/p&gt;
&lt;p&gt;Thirdly, the walker&#39;s focus management is a bit heavy handed - only one tree item is ever accessible - which feels like going against the grain of tree patterns (pardon the pun). Tree patterns should expose a more complex structure (even though I&#39;d also say it doesn&#39;t do a great job in best real life scenarios either).&lt;/p&gt;
&lt;p&gt;So, on and off over the past year or so - or 2 years since the first draft of this line is from 2023-02-03 - I&#39;ve been thinking about starting fresh. Is there perhaps a better pattern out there? Maybe not an ARIA pattern but perhaps one that fits better nevertheless.&lt;/p&gt;
&lt;h2&gt;granularity&lt;/h2&gt;
&lt;p&gt;There is a core feature of most assistive technologies that I kept returning to: changing the granularity of AT output. Any screenreader or read-aloud tool worth their salt will allow users to change the granularity at which they can explore the content. Usually, this goes from &amp;quot;everything&amp;quot; to paragraphs (or some form of chunking) and on to words and ultimately to characters.&lt;/p&gt;
&lt;p&gt;This is obviously a good idea and a key feature to make web pages more understandable - form the big picture problem down to the smallest ambiguities such as homophones, heteronyms, or just plain spelling.&lt;/p&gt;
&lt;p&gt;In other words, this is a good strategy for dealing with regular but complex text fragments - and if it&#39;s good enough for web pages globally, then it seems a decent assumption that a similar approach could help users locally, i.e., with highly structured, non-textual, content fragments.&lt;/p&gt;
&lt;p&gt;Users need to dive into some details but not others while at the same time we want to preserve a consistent structure across the content fragment. In other words, they want to change the granularity of the description. While some content fragments might indeed match other existing patterns (such as a trees), I think this might be one of the most basic constructs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;finer and finer granularity when users want it&lt;/li&gt;
&lt;li&gt;with minimal changes, i.e., while keeping a good integration into the overall document flow&lt;/li&gt;
&lt;li&gt;without exploration ending up in isolation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I must admit to a feeling of retconning things when I originally added this section. There&#39;s a decent chance that I&#39;m rationalizing post-hoc. At the same time, the affordances offered by AT to navigate plain text was very much a driving paradigm as soon as I realized the parallel.&lt;/p&gt;
&lt;p&gt;Obligatory &lt;a href=&quot;https://abstrusegoose.com/230&quot;&gt;abstruse goose comic #230 on writing proofs&lt;/a&gt; (warning: no textual description there).&lt;/p&gt;
&lt;h2&gt;naive attempts&lt;/h2&gt;
&lt;p&gt;How hard can it be? Well, really not hard at all. Especially if you don&#39;t care about accessibility right away.&lt;/p&gt;
&lt;p&gt;The requisites&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a DOM fragment (e.g., an inline SVG, some HTML overlapping a graphic, or just some HTML), focusable so as to allow keyboard accessibility [I wished we had a way to create &amp;quot;light&amp;quot; interactivity without adding to the taborder but that&#39;s just not how things are right now.]&lt;/li&gt;
&lt;li&gt;annotated with some kind of consistent &amp;quot;level&amp;quot; information, i.e., some nodes have &amp;quot;levels&amp;quot;, some of their descendants might have lower &amp;quot;levels&amp;quot;.&lt;/li&gt;
&lt;li&gt;each element with a level has a good accessible name. [We&#39;ll see how this gets tricky but again that&#39;s just not how things are right now.]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I realize that &amp;quot;level&amp;quot; is a confusing word here (what with aria-level being a thing). You can call it whatever you like - granularity or chunk.&lt;/p&gt;
&lt;p&gt;If we think about basic keyboard operations, then we could start with just up and down arrows. If our fragment is focused, hitting the down key should &amp;quot;unwrap&amp;quot; it to expose the level below it. If we hit the up arrow key, we should wrap the closest ancestor.&lt;/p&gt;
&lt;p&gt;That&#39;s doable in a few dozen lines of code, depending on how much error handling you want to do. Here&#39;s an example - again, please be warned that this is not actually very accessible yet. To summarize, you can focus the DOM node, then use up and down arrows to change granularity - but the tab key will always move you around all chunks of the fragment (which might be at varying degress of refinement).&lt;/p&gt;
&lt;!-- [experiment #3] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment1&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script style=&quot;display:block&quot;&gt;
function move1(event) {
  if (![38, 40].includes(event.keyCode)) return; // Only move on up/down arrow
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;)) return; // Only move if the currently focused element is the target and part of the game
  event.preventDefault(); // Avoid scrolling away
  changeLevel1(event.keyCode, event.target); // Do the thing
}
function changeLevel1(keyCode, activeNode) {
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (keyCode === 40){ //down 
    const newLevel = activeNodeLevel + 1; //increase granularity / switch to descendants
    if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
    activeNode.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;); // take current node out of taborder
    activeNode
      .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
      .forEach((currentLevelChild) =&gt;
        currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;)
      );// add next level descendants to taborder
    activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus(); // move focus to first descendant
  } else if (keyCode === 38) {  // up 
    if (activeNodeLevel &lt; 1) return;
    const newLevel = Math.max(activeNodeLevel - 1, 0); // decrease granularity / switch to ancestor
    const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
    ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;); // add ancestor to taborder
    ancestor
      .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
      .forEach((descendant) =&gt; descendant.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;)); // take &quot;siblings&quot; out of taborder
    ancestor.focus(); // focus ancestor
  }
}
experiment1.addEventListener(&quot;keydown&quot;, move1);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g aria-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; aria-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; aria-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g aria-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; aria-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; aria-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; aria-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;There are obviously more clever ways of doing this, depending on your situation. For example, you might find it neat to avoid level values, opting to cleverly filter out lower descendants instead; or you could prefer to use just 2 alternating types of levels; or or or.&lt;/p&gt;
&lt;p&gt;For my other goal, a key part is that ~30 lines of code seem fairly manageable to any user with a bit of JS knowledge.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Only move on up/down arrow&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;activeElement &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Only move if the currently focused element is the target and part of the game&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Avoid scrolling away&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;changeLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Do the thing&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;changeLevel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyCode &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//down &lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//increase granularity / switch to descendants&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// take current node out of taborder&lt;/span&gt;&lt;br /&gt;    activeNode&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentLevelChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;        currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// add next level descendants to taborder&lt;/span&gt;&lt;br /&gt;    activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// move focus to first descendant&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyCode &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// up &lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// decrease granularity / switch to ancestor&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ancestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add ancestor to taborder&lt;/span&gt;&lt;br /&gt;    ancestor&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;descendant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// take &quot;siblings&quot; out of taborder&lt;/span&gt;&lt;br /&gt;    ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// focus ancestor&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And of course the user experience relies on very simple primitives: up/down and tabbing.&lt;/p&gt;
&lt;h2&gt;less naive attempts&lt;/h2&gt;
&lt;p&gt;What&#39;s missing here? On the one hand, usability, on the other accessibility (which are, in the end, one and the same).&lt;/p&gt;
&lt;p&gt;In terms of usability, adding right/left to accompany tabbing seems rather straight forward.&lt;/p&gt;
&lt;!-- [ experiment #4 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment2&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script style=&quot;display:block&quot;&gt;
function move2(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally2(event.target, true);
      break;
    case 38: //up
      moveUp2(event.target);
      break;
    case 39: //right
      moveHorizontally2(event.target, false);
      break;
    case 40: //down
      moveDown2(event.target);
      break;
    default:
      break;
  }
}
function moveDown2(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt;
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;)
    );
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
}
function moveUp2(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; descendant.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;));
  ancestor.focus();
}
// the new thing: moving left/right
function moveHorizontally2(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment2.addEventListener(&quot;keydown&quot;, move2);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g aria-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; aria-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; aria-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g aria-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; aria-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; aria-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; aria-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;With a bit of a refactoring, the additional functionality moves us up to ~60 lines of code.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// only move on arrow keys&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;activeElement &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[data-level]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;keyCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;37&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//left&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;38&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//up&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;39&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//right&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//down&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// refactored&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentLevelChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// refactored&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ancestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;descendant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// the new thing: moving left/right&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; increment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; isReversed &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// forwards or backwards?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentNodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[data-level=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// grab all active siblings&lt;/span&gt;&lt;br /&gt;  currentNodes&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;currentNodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; increment&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;//move focus forwards/backwards&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a next step, let&#39;s go down a dead end. In some way, all of this originated with the idea to support click (or more specficially: tap) as an interaction method. Initially, I had hoped to get away with single click for down, double-click for up:&lt;/p&gt;
&lt;!-- [ experiment #5 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment3&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script style=&quot;display:block&quot;&gt;
function move2(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally2(event.target, true);
      break;
    case 38: //up
      moveUp2(event.target);
      break;
    case 39: //right
      moveHorizontally2(event.target, false);
      break;
    case 40: //down
      moveDown2(event.target);
      break;
    default:
      break;
  }
}
function moveDown2(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt;
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;)
    );
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
}
function moveUp2(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; descendant.setAttribute(&quot;tabindex&quot;, &quot;-1&quot;));
  ancestor.focus();
}
// the new thing: moving left/right
function moveHorizontally2(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment3.addEventListener(&quot;keydown&quot;, move2);
// the new thing: click
let isSingleClick = false; // quick&amp;dirty global flag to identify single clicks
function handleClick3(event) {
  const realTarget = event.target.closest(&quot;[data-level]&quot;); // Spoiler: what might be a problem with this?
  if (!realTarget) return;
  const activeAncestor = realTarget.closest(&#39;[tabindex=&quot;0&quot;]&#39;);
  console.log(isSingleClick);
  if (event.detail == 1) {
    // it&#39;s a singleclick
    isSingleClick = true;
    setTimeout(() =&gt; {
      // wait for potential double click
      if (isSingleClick) {
        //if the flag is still set after the delay
        moveDown2(activeAncestor);
      }
    }, 200); // common delay advice from the intertubes
  } else if (event.detail == 2) {
    isSingleClick = false;
    moveUp2(activeAncestor);
  }
}
experiment3.addEventListener(&quot;click&quot;, handleClick3);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g aria-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; aria-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; aria-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g aria-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; aria-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; aria-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; aria-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;Using click here is so poor, it&#39;s hard to call it functional. You have to hit the target you want to dive in or out of and that&#39;s hard.  But at least it wasn&#39;t a huge increase in code to test it out.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// the new thing: click&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; isSingleClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// quick&amp;amp;dirty global flag to identify single clicks&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; realTarget &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[data-level]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Spoiler: what might be a problem with this?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;realTarget&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeAncestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; realTarget&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSingleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// it&#39;s a singleclick&lt;/span&gt;&lt;br /&gt;    isSingleClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// wait for potential double click&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isSingleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token comment&quot;&gt;//if the flag is still set after the delay&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// common delay advice from the intertubes&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;detail &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    isSingleClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token function&quot;&gt;moveUp2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// add click listener&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My driving motivation here was actually mobile AT users. The assumption being that they&#39;re focused on an element and triggering a synthetic click to explore.&lt;/p&gt;
&lt;p&gt;But as far as I know, I can&#39;t really access more than single-tap on mobile AT (in a browser). Sure, talkback can do a bit more if you expect two-finger input (so you could theoretically pick up on gestures). And sure, VoiceOver has its weird double-tap-and-hold mechanism for direct input (if you&#39;re able to pull that off as a user).&lt;/p&gt;
&lt;p&gt;I really wished we could have the equivalent of &amp;quot;focus mode&amp;quot; on mobile, too.&lt;/p&gt;
&lt;h2&gt;well actually, accessibility&lt;/h2&gt;
&lt;p&gt;Anyway, let&#39;s leave click input for now and turn to non-visual accessiblity. As it turns out, there are a couple of problems we have to deal with. Namely, if you, say, use a screenreader in browse mode you might be annoyed that every single label is actually presented to you. Oops.&lt;/p&gt;
&lt;p&gt;A particular goal for me here is to create a good user experience no matter the state of the fragment. The idea behind this is that users will know best what state is good for them. And whichever state of granularity they prefer should integrate smoothly into the surrounding content.&lt;/p&gt;
&lt;p&gt;Another goal stems form one of the reasons I went with an ARIA tree pattern before: the AT experience of easy access to focus-mode while transitioning through text in browse mode is really good, even for inline content (some weird Firefox bugs notwithstanding). Users can just hit enter while the content label is voiced or braille-displayed to jump to focus mode and then they can hit ESC whenever to get back into browse mode.&lt;/p&gt;
&lt;p&gt;Now with trees, there&#39;s a certain level of focus management expected of developers, whether roving tabindex or active descendant management, and since you control the position so tightly, you might not have to worry much about the rest of the tree items. In the case of the tree walker, I opted keep users restricted to their current branch so all I had to do was reset the tree to its root when the user leaves the fragment. That way all content is present in the accessibility tree while also avoiding having a mess of tree items around for users in browse mode.&lt;/p&gt;
&lt;p&gt;In this approach I&#39;d like to keep the state when leaving, which is slightly more complex. Because we leave multiple chunks in the accessibility tree, we need to clean up better.&lt;/p&gt;
&lt;p&gt;But the first step is similar to the tree walker: we have to hide and reveal our content on the accessibility tree. This is pretty straight forward: ahead of time, we add aria-hidden to everything we want to keep hidden (i.e. the lower levels); then we un-hide what we want when we want it to be accessible:&lt;/p&gt;
&lt;!-- [ experiment #8 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment4&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script style=&quot;display:block&quot;&gt;
function move4(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally4(event.target, true);
      break;
    case 38: //up
      moveUp4(event.target);
      break;
    case 39: //right
      moveHorizontally4(event.target, false);
      break;
    case 40: //down
      moveDown4(event.target);
      break;
    default:
      break;
  }
}
function moveDown4(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.removeAttribute(&quot;tabindex&quot;);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt; {
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
      currentLevelChild.removeAttribute(&quot;aria-hidden&quot;);
    });
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
}
function moveUp4(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; {
        descendant.removeAttribute(&#39;tabindex&#39;);
        descendant.setAttribute(&quot;aria-hidden&quot;, &quot;true&quot;);
    });
  ancestor.focus();
}
// the new thing: moving left/right
function moveHorizontally4(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment4.addEventListener(&quot;keydown&quot;, move4);
// the new thing: click
let isSingleClick4 = false; // quick&amp;dirty global flag to identify single clicks
function handleClick4(event) {
  const realTarget = event.target.closest(&quot;[data-level]&quot;); // Spoiler: what might be a problem with this?
  if (!realTarget) return;
  const activeAncestor = realTarget.closest(&#39;[tabindex=&quot;0&quot;]&#39;);
  console.log(isSingleClick4);
  if (event.detail == 1) {
    // it&#39;s a singleclick
    isSingleClick4 = true;
    setTimeout(() =&gt; {
      // wait for potential double click
      if (isSingleClick) {
        //if the flag is still set after the delay
        moveDown4(activeAncestor);
      }
    }, 200); // common delay advice from the intertubes
  } else if (event.detail == 2) {
    isSingleClick4 = false;
    moveUp2(activeAncestor);
  }
}
experiment4.addEventListener(&quot;click&quot;, handleClick4);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g aria-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; aria-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; aria-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g aria-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; aria-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; aria-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; aria-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;The modifications are adding a handful of lines:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// we&#39;ll fix you later&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// snip&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: a bit stronger than before&lt;/span&gt;&lt;br /&gt;  activeNode&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentLevelChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: un-hide descendants&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ancestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;descendant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: a bit stronger than before&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: hide descendants again&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// add click listener&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, we now have a different problem: the upper layers are still around. Depending on the roles you use - and you can check the raw experiments to see which ones I&#39;d tried - you can get anything from losing content to triggering noisy heuristics in AT tryng to fix what they (not incorrectly) perceive as broken markup.&lt;/p&gt;
&lt;p&gt;So we have to be more precise and hide the &amp;quot;upper&amp;quot; layers more actively. One way to approach this is to stash labels (and possibly roles) of &amp;quot;upper&amp;quot; layers. For this, we start without labels (and roles), i.e., move them into a neutral position.&lt;/p&gt;
&lt;!-- [ experiment #10 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment5&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script&gt;
function move5(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally5(event.target, true);
      break;
    case 38: //up
      moveUp5(event.target);
      break;
    case 39: //right
      moveHorizontally5(event.target, false);
      break;
    case 40: //down
      moveDown5(event.target);
      break;
    default:
      break;
  }
}
function moveDown5(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.removeAttribute(&quot;tabindex&quot;);
  activeNode.removeAttribute(&quot;aria-label&quot;);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt; {
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
      currentLevelChild.removeAttribute(&quot;aria-hidden&quot;);
      currentLevelChild.setAttribute(&quot;aria-label&quot;, currentLevelChild.getAttribute(&quot;data-label&quot;));
    });
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
}
function moveUp5(activeNode) { // refactored
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor.setAttribute(&quot;aria-label&quot;, ancestor.getAttribute(&quot;data-label&quot;));
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; {
        descendant.removeAttribute(&#39;tabindex&#39;);
        descendant.setAttribute(&quot;aria-hidden&quot;, &quot;true&quot;);
        descendant.removeAttribute(&quot;aria-label&quot;);
    });
  ancestor.focus();
}
// the new thing: moving left/right
function moveHorizontally5(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment5.addEventListener(&quot;keydown&quot;, move5);
// the new thing: click
let isSingleClick5 = false; // quick&amp;dirty global flag to identify single clicks
function handleClick5(event) {
  const realTarget = event.target.closest(&quot;[data-level]&quot;); // Spoiler: what might be a problem with this?
  if (!realTarget) return;
  const activeAncestor = realTarget.closest(&#39;[tabindex=&quot;0&quot;]&#39;);
  console.log(isSingleClick5);
  if (event.detail == 1) {
    // it&#39;s a singleclick
    isSingleClick5 = true;
    setTimeout(() =&gt; {
      // wait for potential double click
      if (isSingleClick) {
        //if the flag is still set after the delay
        moveDown5(activeAncestor);
      }
    }, 200); // common delay advice from the intertubes
  } else if (event.detail == 2) {
    isSingleClick5 = false;
    moveUp2(activeAncestor);
  }
}
experiment5.addEventListener(&quot;click&quot;, handleClick5);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g data-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot; data-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; data-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; data-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g data-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; data-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot; data-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; data-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; data-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;The modifications are yet another handful of lines (for now, we don&#39;t change the roles):&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// we&#39;ll fix you later&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// snip&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: remove label&lt;/span&gt;&lt;br /&gt;  activeNode&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentLevelChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: add label&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// refactored&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ancestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: add label&lt;/span&gt;&lt;br /&gt;  ancestor&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;descendant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// NEW: remove label&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// add click listener&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this we&#39;ve almost reached 100 lines of code; then again, we&#39;ve come a long way.&lt;/p&gt;
&lt;p&gt;We no longer have duplicate content being announced but instead the level of granularity decided by the user is honored in browse mode. Relatively speaking it&#39;s significant work but in the end we still have a fairly simple piece of code.&lt;/p&gt;
&lt;h2&gt;pick a role, why don&#39;t you?!&lt;/h2&gt;
&lt;p&gt;As I mentioned, I tried a few different roles in my experiments. When coming up with a pattern like this, I inevitably face the trouble that there&#39;s no obvious ARIA role for me to use.&lt;/p&gt;
&lt;p&gt;Ideally, a role would fit in both the semantics as well as AT behavior.&lt;/p&gt;
&lt;p&gt;With tree patterns, you have a good balance, I think. In the end, any DOM is a tree (even if tree patterns are derived from file trees), and the UX (as mentioned earlier) is quite nice. Most importantly, it tested well with users, even when it&#39;s used for content like this.&lt;/p&gt;
&lt;p&gt;But I wanted to test out some alternatives here. On the one hand, I&#39;m not sure how disorienting the unwrapping would be in the context of tree patterns. On the other hand, it&#39;s good to try new things.&lt;/p&gt;
&lt;p&gt;The most straight forward role might be image. Especially when dealing with actual images, it&#39;s obviously perfect semantically. The UX is ok, too: once you explore, you get a bunch of images. You&#39;d have to modify the code to switch the role on and off because role=image sets children to be presentational, i.e., ignored. Something like&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;    activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;    currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;img&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// and later...&lt;/span&gt;&lt;br /&gt;    ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;img&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;br /&gt;    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;    descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;br /&gt;    descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only trouble is that images are historically static so the discoverability is problematic - why would you try to interact with an image?  I guess role descriptions can help but it&#39;s always best to avoid them.&lt;/p&gt;
&lt;p&gt;Thinking about such content fragments more broadly, I once again tested with using the group role - and of course the abstract SVG is just full of groups. Again, semantically this seems very apt. It offers decent UX and is highly discoverable as AT users expect to explore groups. I really like that many ATs announce entering and leaving groups but unfortunately this gets very noisy when you&#39;re deep in the content where AT announces every single higher-up group that you&#39;re leaving or entering.&lt;/p&gt;
&lt;p&gt;Even a mix of the two - an outermost group, say, with a role-description, and then just image roles inside - comes out too noisily for my taste when going through a granular state in browse mode.&lt;/p&gt;
&lt;p&gt;And of course the tree role remains a good option. In the end, user testing should decide where to go.&lt;/p&gt;
&lt;h2&gt;so about that click (and other nasty details)&lt;/h2&gt;
&lt;p&gt;As I mentioned, click-based exploration in experiment #5 was not good. In particular, mobile ATs actually don&#39;t provide double clicking in browsers (naturally).&lt;/p&gt;
&lt;p&gt;Somebody kindly pointed out on mastodon, a virtual dpad is one possible way to solve this. That&#39;s a super robust solution and I was grateful for the discussion. But I wanted to try something minimalistic.&lt;/p&gt;
&lt;p&gt;The best idea I&#39;ve come up with so far is stolen from good old physical device UX. If you have only a single button to set some kind of level in discrete steps (e.g., volume, speed, temperature), then one common UX pattern is as follows: pressing the button will start at one end of the scale, step through the levels and upon reaching the other end of the scale, the button&#39;s behavior will reverse, so we can step in reverse until we&#39;re back at the starting point.&lt;/p&gt;
&lt;p&gt;I think there&#39;s a good chance this can work for users because realistically content that&#39;s so complex that this becomes cumbersome, is likely too complex (read: flawed) to begin with. But as always, user testing will tell you if it works with your content for your users. (For example, another UX patterns from: jump immediately to the starting level instead of reversing direction.)&lt;/p&gt;
&lt;!-- [ experiment #11 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment6&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script&gt;
function move6(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally6(event.target, true);
      break;
    case 38: //up
      moveUp6(event.target);
      break;
    case 39: //right
      moveHorizontally6(event.target, false);
      break;
    case 40: //down
      moveDown6(event.target);
      break;
    default:
      break;
  }
}
function moveDown6(activeNode) {
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.removeAttribute(&quot;tabindex&quot;);
  activeNode.removeAttribute(&quot;aria-label&quot;);
  activeNode.setAttribute(&quot;role&quot;, &quot;none&quot;);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt; {
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
      currentLevelChild.removeAttribute(&quot;aria-hidden&quot;);
      currentLevelChild.setAttribute(&quot;aria-label&quot;, currentLevelChild.getAttribute(&quot;data-label&quot;));
      currentLevelChild.setAttribute(&quot;role&quot;, &quot;img&quot;); 
    });
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
  return activeNode; //NEW: help with click
}
function moveUp6(activeNode) {
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor.setAttribute(&quot;aria-label&quot;, ancestor.getAttribute(&quot;data-label&quot;));
  ancestor.setAttribute(&quot;role&quot;, &quot;img&quot;); 
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; {
        descendant.removeAttribute(&#39;tabindex&#39;);
        descendant.setAttribute(&quot;aria-hidden&quot;, &quot;true&quot;);
        descendant.removeAttribute(&quot;aria-label&quot;);
        descendant.setAttribute(&quot;role&quot;, &quot;none&quot;);
    });
  ancestor.focus();
}
function moveHorizontally6(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment6.addEventListener(&quot;keydown&quot;, move6);
// the new thing: single-click exploration
function handleClick6(event) {
  if (!event.target.closest(&quot;[data-level]&quot;)) return;
  const activeAncestor = event.target.closest(&#39;[tabindex=&quot;0&quot;]&#39;);
  if (!activeAncestor) return;
  // If we&#39;re in reverse, move up
  if (
    activeAncestor.getAttribute(&quot;data-reverse&quot;) === &quot;true&quot; ||
    activeAncestor.querySelector(&#39;[data-reverse=&quot;true&quot;]&#39;)
  ) {
    let newActiveNode = moveUp6(activeAncestor);
    if (!newActiveNode)
      activeAncestor
        .querySelectorAll(&#39;[data-reverse=&quot;true&quot;]&#39;)
        .forEach((node) =&gt; node.removeAttribute(&quot;data-reverse&quot;));
    return;
  }
  // Otherwise, try moving down, if it fails, set to reverse
  let newActiveNode = moveDown6(activeAncestor);
  if (!newActiveNode) activeAncestor.setAttribute(&quot;data-reverse&quot;, &quot;true&quot;);
}
experiment6.addEventListener(&quot;click&quot;, handleClick6);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g data-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; data-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; data-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g data-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; data-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot; data-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; data-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; data-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;This change doesn&#39;t do much in terms of our code length, since we traded double click handling against the reversing logic.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;[data-level]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeAncestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If we&#39;re in reverse, move up&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-reverse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;&lt;br /&gt;    activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[data-reverse=&quot;true&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newActiveNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;newActiveNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;      activeAncestor&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[data-reverse=&quot;true&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-reverse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Otherwise, try moving down, if it fails, set to reverse&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; newActiveNode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;newActiveNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; activeAncestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-reverse&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip!&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//NEW: helps with click handling&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// refactored&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveHorizontally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isReversed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// snip! same as before&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;// add click listener&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;keydown&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; move&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// add keydown listener&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the UX for AT users seems reasonable, it comes at the price of rather opaque behavior for non-AT users. Clicking around the SVG does not seem very intuitive to me. There&#39;s also the question on making it a bit more clever, e.g., you might want to drop out of reverse mode if the user moves away horizontally as they might not remember the state if they come back. Alternatively you might want to provide a cue which mode the user is in (e.g., an earcon, pre/suffix).&lt;/p&gt;
&lt;p&gt;To some degree this relates to a nastly little snag I eventually hit on (mobile) AT: the synthetic click event that&#39;s triggered by AT when double tapping can miss the target (or more specifically: appear to miss the target).&lt;/p&gt;
&lt;p&gt;Namely, if you have an SVG, then the synthetic click seems to land on the first pixel of the element&#39;s bounding box. If the node you&#39;re on does not have any paint at that position, this won&#39;t register with the child but with whatever ancestor is considered to be painting that pixel. Thus, when an AT user triggers a click on the focused element, that click might register for simply the SVG root - and our users will never be able to explore.&lt;/p&gt;
&lt;p&gt;Of course you get the same problem with a mouse click - you&#39;ll have to make sure you hit a path to get the expected result. But I guess visual users are more likely to automatically for a painted path.&lt;/p&gt;
&lt;p&gt;This is of course not a new problem - clicking SVGs being iffy is just the way it is. One workaround is to add a transparent rectangle that covers the relevant nodes (and remove them again when levels change). As annoying as that is, it&#39;s not a huge lot of code to maintain - and comes with the benefit of much simpler highlighting.&lt;/p&gt;
&lt;!-- [ experiment #12 ] --&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;640&quot; height=&quot;300&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot; id=&quot;experiment7&quot; viewBox=&quot;0 0 640 300&quot; tabindex=&quot;0&quot; aria-label=&quot;a rectangle containing several nested rectangles&quot; data-label=&quot;a rectangle containing several nested rectangles&quot; data-level=&quot;0&quot;&gt;
&lt;script&gt;
// adding and removing rectangles
function addRect7(node) {
  if (node.tagName !== &quot;g&quot;) return;
  const bbox = node.getBBox();
  node.insertAdjacentHTML(
    &quot;afterbegin&quot;,
    `&lt;rect x=&quot;${bbox.x}&quot; y=&quot;${bbox.y}&quot; width=&quot;${bbox.width}&quot; height=&quot;${bbox.height}&quot; data-rect=&quot;true&quot; fill=&quot;transparent&quot;/&gt;`
  );
}
function clearRect7(node) {
  if (node.tagName !== &quot;g&quot;) return;
  node.querySelectorAll(&quot;rect[data-rect]&quot;).forEach((node) =&gt; node.remove());
}
function move7(event) {
  if (![37, 38, 39, 40].includes(event.keyCode)) return; // only move on arrow keys
  if (event.target !== document.activeElement ||!event.target.hasAttribute(&quot;data-level&quot;))
    return;
  event.preventDefault();
  switch (event.keyCode) {
    case 37: //left
      moveHorizontally7(event.target, true);
      break;
    case 38: //up
      moveUp7(event.target);
      break;
    case 39: //right
      moveHorizontally7(event.target, false);
      break;
    case 40: //down
      moveDown7(event.target);
      break;
    default:
      break;
  }
}
function moveDown7(activeNode) {
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  const newLevel = activeNodeLevel + 1;
  if (!activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`)) return;
  activeNode.removeAttribute(&quot;tabindex&quot;);
  activeNode.removeAttribute(&quot;aria-label&quot;);
  activeNode.setAttribute(&quot;role&quot;, &quot;none&quot;);
  clearRect7(activeNode);
  activeNode
    .querySelectorAll(`[data-level=&quot;${newLevel}&quot;]`)
    .forEach((currentLevelChild) =&gt; {
      currentLevelChild.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
      currentLevelChild.removeAttribute(&quot;aria-hidden&quot;);
      currentLevelChild.setAttribute(&quot;aria-label&quot;, currentLevelChild.getAttribute(&quot;data-label&quot;));
      currentLevelChild.setAttribute(&quot;role&quot;, &quot;img&quot;);
      addRect7(currentLevelChild);
    });
  activeNode.querySelector(`[data-level=&quot;${newLevel}&quot;]`).focus();
  return activeNode; //NEW: help with click
}
function moveUp7(activeNode) {
  const activeNodeLevel = parseInt(activeNode.getAttribute(&quot;data-level&quot;));
  if (activeNodeLevel &lt; 1) return;
  const newLevel = Math.max(activeNodeLevel - 1, 0);
  const ancestor = activeNode.closest(`[data-level=&quot;${newLevel}&quot;]`);
  ancestor.setAttribute(&quot;tabindex&quot;, &quot;0&quot;);
  ancestor.setAttribute(&quot;aria-label&quot;, ancestor.getAttribute(&quot;data-label&quot;));
  ancestor.setAttribute(&quot;role&quot;, &quot;img&quot;);
  addRect7(ancestor);
  ancestor
    .querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
    .forEach((descendant) =&gt; {
        descendant.removeAttribute(&#39;tabindex&#39;);
        descendant.setAttribute(&quot;aria-hidden&quot;, &quot;true&quot;);
        descendant.removeAttribute(&quot;aria-label&quot;);
        descendant.setAttribute(&quot;role&quot;, &quot;none&quot;);
        clearRect7(descendant);
    });
  ancestor.focus();
}
function moveHorizontally7(activeNode, isReversed) {
  const increment = isReversed ? -1 : 1; // forwards or backwards?
  const currentNodes = [
    ...activeNode.closest(&#39;[data-level=&quot;0&quot;]&#39;).querySelectorAll(&#39;[tabindex=&quot;0&quot;]&#39;)
  ];// grab all active siblings
  currentNodes[currentNodes.indexOf(activeNode) + increment]?.focus();//move focus forwards/backwards
}
experiment7.addEventListener(&quot;keydown&quot;, move7);
// the new thing: single-click exploration
function handleClick7(event) {
  if (!event.target.closest(&quot;[data-level]&quot;)) return;
  const activeAncestor = event.target.closest(&#39;[tabindex=&quot;0&quot;]&#39;);
  if (!activeAncestor) return;
  // If we&#39;re in reverse, move up
  if (
    activeAncestor.getAttribute(&quot;data-reverse&quot;) === &quot;true&quot; ||
    activeAncestor.querySelector(&#39;[data-reverse=&quot;true&quot;]&#39;)
  ) {
    let newActiveNode = moveUp7(activeAncestor);
    if (!newActiveNode)
      activeAncestor
        .querySelectorAll(&#39;[data-reverse=&quot;true&quot;]&#39;)
        .forEach((node) =&gt; node.removeAttribute(&quot;data-reverse&quot;));
    return;
  }
  // Otherwise, try moving down, if it fails, set to reverse
  let newActiveNode = moveDown7(activeAncestor);
  if (!newActiveNode) activeAncestor.setAttribute(&quot;data-reverse&quot;, &quot;true&quot;);
}
experiment7.addEventListener(&quot;click&quot;, handleClick7);
&lt;/script&gt;
 &lt;g&gt;
  &lt;g data-label=&quot;The first inner rectangle containing other rectangles&quot; data-level=&quot;1&quot; data-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;310&quot; x=&quot;3&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;10&quot; y=&quot;100&quot; data-label=&quot;In the first inner rectangle, another smaller rectangle on the left&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;100&quot; stroke=&quot;#000000&quot; width=&quot;100&quot; x=&quot;150&quot; y=&quot;164.5&quot; data-label=&quot;followed by another smaller rectangle to its lower right, also within the first inner rectangle&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
  &lt;g data-label=&quot;Then the second inner rectangle containing other rectangles, to the right of the first one.&quot; data-level=&quot;1&quot; aria-hidden=&quot;true&quot;&gt;
   &lt;rect fill-opacity=&quot;0&quot; height=&quot;290&quot; stroke=&quot;#000000&quot; width=&quot;320&quot; x=&quot;317&quot; y=&quot;5&quot;&gt;&lt;/rect&gt;
   &lt;g&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;330&quot; y=&quot;200&quot; data-label=&quot;In the second inner rectangle, another, smaller rectangle in the low left&quot; data-level=&quot;2&quot; data-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;420&quot; y=&quot;100&quot; data-label=&quot;followed by a second, smaller rectangle in the relative center&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
    &lt;rect fill-opacity=&quot;0&quot; height=&quot;75&quot; stroke=&quot;#000000&quot; width=&quot;75&quot; x=&quot;550&quot; y=&quot;30&quot; data-label=&quot;followed by a third, smaller rectangle in the upper right of the second inner triangle.&quot; data-level=&quot;2&quot; aria-hidden=&quot;true&quot;&gt;&lt;/rect&gt;
   &lt;/g&gt;
  &lt;/g&gt;
 &lt;/g&gt;&lt;/svg&gt;
&lt;p&gt;This adds a bit of complexity, of course:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// adding and removing rectangles&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;addRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; bbox &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getBBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token string&quot;&gt;&quot;afterbegin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;rect x=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; y=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; width=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; height=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;bbox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;height&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot; data-rect=&quot;true&quot; fill=&quot;transparent&quot;/&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;g&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rect[data-rect]&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; node&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//snip&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;clearRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;currentLevelChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;img&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token function&quot;&gt;addRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currentLevelChild&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;//NEW: help with click&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moveUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;activeNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-level&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activeNodeLevel &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ancestor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activeNode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[data-level=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;newLevel&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;tabindex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;img&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;addRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ancestor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[tabindex=&quot;0&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;descendant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;tabindex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-label&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        descendant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token function&quot;&gt;clearRect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;descendant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  ancestor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it does not add a whole lot to our code: now at ~120 lines of code.&lt;/p&gt;
&lt;p&gt;Of course, you might have more ideas. For example, creating a subtitle from the active node or using speech synthesis adds maybe another 10 lines each (see the &lt;a href=&quot;https://krautzource.github.io/aria-tree-walker/&quot;&gt;aria-tree-walker demos&lt;/a&gt; for an example), whatever helps your users.&lt;/p&gt;
&lt;h2&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;Overall, I&#39;m quite satisfied with these experiments. Despite the growth in complexity, the approach still feels appealingly simple at heart. I can add and remove complexity as use cases require/permit. I actually see a production use case for something like it - which might pave the way to more (user) testing.&lt;/p&gt;
&lt;p&gt;I leave you with a real world example where I tested out the authoring experience form yself. This works with a diagram from &lt;a href=&quot;https://en.wikipedia.org/wiki/Lever&quot;&gt;Wikipedia&#39;s article on the lever principle&lt;/a&gt;. Attribution for the referenced raster is: Jjw, &lt;a href=&quot;https://creativecommons.org/licenses/by-sa/3.0&quot;&gt;CC BY-SA 3.0&lt;/a&gt;, &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Lever_Principle_3D.png&quot;&gt;via Wikimedia Commons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To start out, I include a screencapture below. It was captured while I took that raster graphic into SVGEdit, then quickly drew some rectangles on top of of the various parts, followed by manipulating the SVG source in a text editor to add groups and the necessary attributes for our purposes. Apologies for the lack of audio or subtitles - I added this more as a decorative item so I hope this description suffices.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/wiki-boxen.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;While it wasn&#39;t my first try working with the graphic, I think 10-15mins is a reasonable expectation for this kind of work, especially if you&#39;re the author of the graphic.&lt;/p&gt;
&lt;p&gt;Here&#39;s the result integrated in to our final code.&lt;/p&gt;
&lt;svg style=&quot;max-width: 100%&quot; width=&quot;512&quot; height=&quot;291&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 512 291&quot; aria-label=&quot;Diagram of the lever principle&quot; data-label=&quot;Diagram of the lever principle&quot; role=&quot;image&quot; aria-roledescription=&quot;explorable graphic&quot; tabindex=&quot;0&quot; data-level=&quot;0&quot; id=&quot;experiment8&quot;&gt;
&lt;script&gt;
experiment8.addEventListener(&quot;keydown&quot;, move7);
experiment8.addEventListener(&quot;click&quot;, handleClick7);
&lt;/script&gt;
  &lt;image height=&quot;291&quot; id=&quot;svg_3&quot; width=&quot;512&quot; x=&quot;0&quot; href=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Lever_Principle_3D.png/512px-Lever_Principle_3D.png&quot; y=&quot;0&quot; role=&quot;none&quot;&gt;&lt;/image&gt;
  &lt;g data-level=&quot;1&quot; data-label=&quot;the equation mass m 1 times distance a is equal to mass m 2 times distance b&quot; aria-hidden=&quot;true&quot;&gt;
  &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;35&quot; width=&quot;186&quot; x=&quot;131&quot; y=&quot;3&quot;&gt;&lt;/rect&gt;
  &lt;/g&gt;
  &lt;g data-level=&quot;1&quot; data-label=&quot;The lever with the mass m1 on the left side, the pivot and mass m2 on the right side&quot; aria-hidden=&quot;true&quot;&gt;
    &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;111&quot; width=&quot;508&quot; x=&quot;1&quot; y=&quot;25&quot;&gt;&lt;/rect&gt;
    &lt;g data-level=&quot;2&quot; data-label=&quot;The lever with the larger mass m 1 on the shorter left side&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;124&quot; width=&quot;106&quot; x=&quot;6&quot; y=&quot;18&quot;&gt;&lt;/rect&gt;
    &lt;/g&gt;
    &lt;g data-level=&quot;2&quot; data-label=&quot;followed by the pivot in the middle&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;82&quot; width=&quot;99&quot; x=&quot;157&quot; y=&quot;64&quot;&gt;&lt;/rect&gt;
    &lt;/g&gt;
    &lt;g data-level=&quot;2&quot; data-label=&quot;followed by the smaller mass m2 on the longer right side of the lever&quot; aria-hidden=&quot;true&quot;&gt;
     &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;88&quot; width=&quot;96&quot; x=&quot;402&quot; y=&quot;44&quot;&gt;&lt;/rect&gt;
    &lt;/g&gt;
  &lt;/g&gt;
  &lt;g data-level=&quot;1&quot; data-label=&quot;Below the lever, two underbraces demarking distance A and distance b&quot; aria-hidden=&quot;true&quot;&gt;
    &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;127&quot; width=&quot;462&quot; x=&quot;16&quot; y=&quot;157&quot;&gt;&lt;/rect&gt;
    &lt;g data-level=&quot;2&quot; data-label=&quot;Below the level, an underbrace from the left side to the pivot, demarking distance A&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;105&quot; width=&quot;176&quot; x=&quot;21&quot; y=&quot;182&quot;&gt;&lt;/rect&gt;
    &lt;/g&gt;
    &lt;g data-level=&quot;2&quot; data-label=&quot;followed by an underbrace from the pivot to the left end of the pivot, demarking distance B&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;rect fill=&quot;#000000&quot; fill-opacity=&quot;0&quot; height=&quot;88&quot; width=&quot;261&quot; x=&quot;201&quot; y=&quot;197&quot;&gt;&lt;/rect&gt;
    &lt;/g&gt;
 &lt;/g&gt;
&lt;/svg&gt;
&lt;p&gt;Of course, &amp;quot;really&amp;quot; you want to automate this kind of thing. That might work if you&#39;re using a tool to generate such a diagram.&lt;/p&gt;
&lt;p&gt;In case you can&#39;t use SVG but HTML with CSS: when I tried it the first time around, I started with divs in a CSS grid - here&#39;s &lt;a href=&quot;https://codepen.io/pkra/pen/zYMoLEY&quot;&gt;a slightly incomplete codepen&lt;/a&gt;. This works the same from an accessibility perspective so you always have that option, too.&lt;/p&gt;
&lt;h2&gt;test test test&lt;/h2&gt;
&lt;p&gt;To wrap things up, here are some rough screenreader demos of a simplified interaction.&lt;/p&gt;
&lt;p&gt;First, the standard DPAD/arrow navigation on the desktops. I&#39;ve tried to be consistent in the recordings but these are in no way polished. Ideally, you&#39;ll notice the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;starting at the paragraph just above the lever diagram&lt;/li&gt;
&lt;li&gt;use browse mode to read past it&lt;/li&gt;
&lt;li&gt;go back and explore a bit in focus mode&lt;/li&gt;
&lt;li&gt;back to browse mode, move back to the paragraph before the diagram and read the explored version&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, apologies for lack of proper video subtitles. The active speech text display of each screenreader is hopefully sufficient to get a first impression. (I should really have found the time to add some subtitling to the code...).&lt;/p&gt;
&lt;p&gt;First off, NVDA with Chrome which has some verbosity due to the role description being repeated alongside the proper role and some virtual buffer repetition when re-entering browse mode; as an aside, Firefox is similar but doesn&#39;t suffer from repeating role and outer role description:&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_nvda-chrome.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Next is JAWS with Chrome which has less verbosity but role description is voiced; again, Firefox is basically the same.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_jaws-chrome.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Now ORCA on Chrome does not add extra verbosity but triggers a click / exploration when exiting focus mode (which looks like a bug).&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_orca-chrome.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Finally, VoiceOver on Mac with Safari. I hope the German Macbook VO reading English with a funky German accent doesn&#39;t throw you off too much. Functionally, the result on VO is much the same as the others. Notably, VO doesn&#39;t voice role descriptions (which is IIRC generally the case) so depending on your needs, you could try to replace role description with aria-description to see if that yields better results for your users.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_voiceover_mac.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;That wraps up desktop screenreaders. On mobile I&#39;ve also tried to be consistent in what I&#39;m doing in the demos. Here&#39;s hopefully what you&#39;ll notice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;starting at the paragraph just above the lever diagram&lt;/li&gt;
&lt;li&gt;flick manually to read past it&lt;/li&gt;
&lt;li&gt;go back and explore a bit using double-tap (i.e., synthetic single click)&lt;/li&gt;
&lt;li&gt;move back to the paragraph before the diagram and let AT read continuously&lt;/li&gt;
&lt;li&gt;then go back to diagram and collapse it again, i.e., navigate down and up again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First off, VoiceOver on iOS. Again, role description won&#39;t work (and again aria-description is worth a test). Additionally, when flicking manually, VO adds its magic OCR output. To me, it adds noise but I&#39;m guessing users either like it or disable it. On the bright side, the OCR magic doesn&#39;t show up during continuous reading.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_voiceover_ios.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Lastly, Talkback. It behaves basically the same as all the others. Again, my German devices lead to funny results - the role description is read in a German voice (and of course instructional voicing and menus are in German); still, I hope you get the ist. Visually, it&#39;s a bit weird how it jumps around but it could be worse.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://www.peterkrautzberger.org/assets/2023/granularity-walker_talkback_chrome.mp4&quot; style=&quot;max-width: 100%; height: auto&quot; controls=&quot;&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Note that I didn&#39;t test the hardest use case: inline content. That is the trickiest part in my experience. It&#39;s often feasible to find patterns where AT can enter easily into a complex inline content fragments while in browse mode (again, trees are pretty great here as is this pattern). The tough part is exiting and continuing inline; AT tends to not do great here, i.e., loses its position, at minimum switching back to the starting of the browse mode buffer, but often losing its position quite badly.&lt;/p&gt;
&lt;p&gt;Again, I hope this pattern might make that easier because at least users can control what state the fragment is in when they next come across it, ideally enabling them to make it more usable. Do I think that sounds great? Not at all. But better is sometimes all I can aim for.&lt;/p&gt;
&lt;p&gt;Finally, I cleaned up the code a bit and &lt;a href=&quot;https://github.com/krautzource/granularity-walker&quot;&gt;left a copy on GitHub&lt;/a&gt;. This brings it back down to ~100 lines. Hopefully I&#39;ve added enough warnings to the repository to make it clear that you should only use the idea, not really the code.&lt;/p&gt;
&lt;p&gt;If you have a chance to try out this pattern, I&#39;d love to hear about it.&lt;/p&gt;
&lt;h2&gt;... and in the long run?&lt;/h2&gt;
&lt;p&gt;I can&#39;t shake the feeling that there&#39;s something more fundamental lurking in all of this, something that&#39;s worth considering on the platform layer. More experiments might help surface the relevant parts.&lt;/p&gt;
&lt;p&gt;For example fairly late in writing this, I looked at the &lt;a href=&quot;https://w3c.github.io/graphics-aria/&quot;&gt;graphics-aria specification&lt;/a&gt; again. I admit I&#39;d somewhat forgotten about it - it doesn&#39;t see a lot of activity. Coming from this exploration, the examples in the spec seemed pretty wild to me. Besides things like overly clever use of aria-labelledBy/describedBy it seems very odd to me how AT is working with these. I didn&#39;t have time to dig into what is really supposed to happen or where perhaps there are bugs or incomplete implementations stirring trouble. Perhaps this will be for another post. I&#39;ll just have to see.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Web components for (mathematical) writing, a first problem</title>
		<link href="https://www.peterkrautzberger.org/0220/"/>
		<updated>2023-11-14T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0220/</id>
		<content type="html">&lt;p&gt;So a while back - checks notes - &lt;em&gt;ahem&lt;/em&gt;, 6 years ago &lt;a href=&quot;https://scoskey.org/&quot;&gt;Sam&lt;/a&gt; gifted me with some of his precious time to do &lt;a href=&quot;https://github.com/pkra/laml/&quot;&gt;a fun little experiment&lt;/a&gt; around writing mathematical long form using the web. With &amp;quot;using the web&amp;quot;, I don&#39;t mean writing markdown or LaTeX (or whatever) and converting that to HTML. Instead, I had this weird idea that we could actually use the web platform - HTML, CSS, JS - to write content. Sounds crazy, right?&lt;/p&gt;
&lt;p&gt;Well, it kind of was. We gave up after a few months (due to life in general but also after getting stuck on paragraphs of all things) and I have been meaning to come back to it ever since.  At the end of October I was finally of a mind to do just that.&lt;/p&gt;
&lt;p&gt;Back then I had already wanted to restart this as an experiment in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Components&quot;&gt;web components&lt;/a&gt;, getting a chance to do more than write a few dummy components.  I&#39;m not very far along but there have already been a few lessons for me. Instead of &lt;a href=&quot;https://www.peterkrautzberger.org/0218&quot;&gt;spending&lt;/a&gt; &lt;a href=&quot;https://www.peterkrautzberger.org/0220&quot;&gt;years&lt;/a&gt; on a blog post, I thought I&#39;d try to jot down a first problem I ran into. If only because then I&#39;d document my thoughts a little.&lt;/p&gt;
&lt;p&gt;One of the many reasons that Sam was relentelessly helpful back then is that he is a working mathematician (as opposed to myself). So when I think about a possible user experience for writing long form mathematical documents he is the kind of user I&#39;d turn to -  knowledgeable and open to exploring new ideas. Obviously, we both grew up mathematically with LaTeX so that was a base line in terms of UX. You might think LaTeX would not be a high bar but of course muscle memory for any system develops a UX of its own. I mean, you&#39;d think just showing someone &lt;a href=&quot;https://docs.emmet.io/&quot;&gt;emmet&lt;/a&gt; and modern linting&amp;amp;debugging tools would be amazing for people stuck in TeX&#39;s Chomsky-Type-1-maybe-0-hell.&lt;/p&gt;
&lt;p&gt;Perhaps needless to say but just because we started out from the point of view of a working mathematician doesn&#39;t mean that there&#39;s not something to be found that benefits a wider audience.&lt;/p&gt;
&lt;p&gt;Anyway. Let me focus on my first steps starting fresh. So. The idea is to write some web components that magically solve particular challenges or pain points. Let&#39;s collect a few.&lt;/p&gt;
&lt;p&gt;A central item for mathematical writing is first-class support for &lt;em&gt;enunciations&lt;/em&gt; (as the &lt;a href=&quot;https://www.ams.org/arc/styleguide/index.html&quot;&gt;AMS style guide&lt;/a&gt; lovingly calls them), that is definitions, theorems, propositions, lemmata, remarks etc. - and the ever problematic proof environment.&lt;/p&gt;
&lt;p&gt;Another item that makes TeX great is automation around counters. A particular key to magically populating labels of various things - sectioning elements, figures, enunciations, and equations - with (sometimes too much) control for resetting and adjusting them.&lt;/p&gt;
&lt;p&gt;Finally, a central item is magic around referencing - just specify the ID and you get a lot of magic to fill those references. Even more so if you use fancier packages like cleverref.&lt;/p&gt;
&lt;p&gt;As a first test, those felt like a good set of items: enunciations, automatic labeling and magical cross-references.&lt;/p&gt;
&lt;h2&gt;Generating content with web components&lt;/h2&gt;
&lt;p&gt;We want our HTML simple. In emmet terms: type &lt;code&gt;thm-&amp;gt;p&lt;/code&gt;, then hit &lt;kbd&gt;TAB&lt;/kbd&gt;, finally write the first paragraph.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;thm-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This is amazing&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;thm-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s nice. [Fun fact: custom elements need a dash and &lt;code&gt;thm-&lt;/code&gt; is valid - though &lt;code&gt;-thm&lt;/code&gt; is not.]&lt;/p&gt;
&lt;p&gt;But where&#39;s our label? Well, we can generate it automatically. Since I know I&#39;ll have plenty of variation, I start with a simple base element:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// enunciation base&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;enunciation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// no boilerplate to keep this short&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Enunciation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// this.setAttribute(&#39;role&#39;, &#39;???&#39;); // let&#39;s get to that another time&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;afterbegin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;enunciation-label&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/enunciation-label&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;enunciation-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; enunciation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Long names are ok, I think, since this element is not meant to be used directly; verbosity is not the problem here but hopefully helps with understanding the output. [Or not, let&#39;s not tie ourselves down this early.]&lt;/p&gt;
&lt;p&gt;When this element is enters the DOM (or more likely: gets upgraded), it will generate a label, containing a customizable name. (Should I use templates and slots? Should I look into mixins? Way too early to answer, I think. Let&#39;s stay naive as long as we can.)&lt;/p&gt;
&lt;p&gt;Anyway, the key thing is that we can pass an argument to &lt;code&gt;connectedCallback&lt;/code&gt; which we do in our actual theorem element:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// theorem&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;theorem&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;enunciation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Theorem&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;thm-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; theorem&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fair enough, we have generated our label - and a prototype to base other enunciations on. Let&#39;s move on.&lt;/p&gt;
&lt;h2&gt;CSS counters are sooo lovely (until they aren&#39;t)&lt;/h2&gt;
&lt;p&gt;For automatic counters, CSS goes very well with our components.&lt;/p&gt;
&lt;p&gt;Just add&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;thm-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;counter-increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; thmCounter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token selector&quot;&gt;thm- &gt; enunciation-label::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; &quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;thmCounter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;. &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sweet. This seems super simple.&lt;/p&gt;
&lt;p&gt;Resetting CSS counters is also trivial; typical variations in mathematical writing such as adding a title alongside the label or an option to surpress the label would be straight forward next steps.&lt;/p&gt;
&lt;p&gt;However, let&#39;s ignore these tempting side quests and move on again so we can run into trouble.&lt;/p&gt;
&lt;h3&gt;Call me by my label&lt;/h3&gt;
&lt;p&gt;At first, the third item seem equally easy: magic cross references with web components.&lt;/p&gt;
&lt;p&gt;Obviously, we need a point of reference and a reference to it. Let&#39;s adjust our theorem example; in emmet terms: &lt;code&gt;thm#thm&amp;gt;p&lt;/code&gt;. Also, add a paragraph with a cross-reference to that ID, maybe (in emmet) &lt;code&gt;ref[target=thm]&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;thm-&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;thm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This is amazing&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;thm-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Have you heard about &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ref-&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;thm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ref-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;?&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Could be that &lt;code&gt;target&lt;/code&gt; is too long but I like readable strings. In any case: what should &lt;code&gt;ref-&lt;/code&gt; do? Well, just find the label of the theorem and use that, obvs.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;crossreference&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;target&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;a href=&quot;#&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;targetId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;enunciation-label&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; targetId&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/a&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// maybe prepend so authors can add parenthetical notes?&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;customElements&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;define&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ref-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; crossreference&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, I run into trouble. The resulting link just says &amp;quot;Theorem&amp;quot;, not &amp;quot;Theorem 1&amp;quot;. That&#39;s not super helpful (and clearly fails &lt;a href=&quot;https://www.w3.org/WAI/WCAG22/quickref/?versions=2.1#link-purpose-in-context&quot;&gt;WCAG 2.4.4&lt;/a&gt;). But of course that&#39;s my mistake - the &lt;code&gt;innerHTML&lt;/code&gt; is &amp;quot;Theorem&amp;quot;, after all.&lt;/p&gt;
&lt;p&gt;So how do you get the pseudo-element&#39;s content? As you might do, I hit the usual search engines to figure out if it can be done. In the process, I found some ancient stackoverflow posts - but it&#39;s not too helpful to know this didn&#39;t work in 2008.&lt;/p&gt;
&lt;p&gt;I eventually remembered that getComputedStyle can look up pseudo elements. But unfortunately it gives us &lt;code&gt;&amp;quot; &amp;quot; counter(thmCounter) &amp;quot;. &amp;quot;&lt;/code&gt;- very much correct and very much not what I want. (Reminds me of an old joke about mathematicians, fog and a hot air balloon, actually.)&lt;/p&gt;
&lt;p&gt;So &lt;a href=&quot;https://bonn.social/@pkra/111314587141501059&quot;&gt;I asked my fedi bubble&lt;/a&gt; and kind people helped - only to confirm that it&#39;s &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/5879&quot;&gt;not doable today&lt;/a&gt;, despite having been &lt;a href=&quot;https://discourse.wicg.io/t/query-live-counter-values-via-cssom/415/13&quot;&gt;discussed by relevant people (approvingly) 9 years ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Shucks.&lt;/p&gt;
&lt;h3&gt;This is why we can&#39;t have nice things&lt;/h3&gt;
&lt;p&gt;I guess we&#39;ll have to realize the wonders of CSS counters in our custom elements instead. Hold my beer?&lt;/p&gt;
&lt;p&gt;Ok, maybe just a counter for what we need? Either way, then we can populate the label with the actual counter directly, and references will start to work. A quick &amp;amp; dirty solution might be something like this&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;enunciation&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Enunciation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; counter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;afterbegin&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;enunciation-label&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;counter&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/enunciation-label&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;theorem&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;enunciation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;theorem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Theorem&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;theorem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, that feels quick and dirty. But it also raises some questions.&lt;/p&gt;
&lt;p&gt;As I already mentioned, CSS counters offer a lovely resetting mechanism - just something like&lt;/p&gt;
&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;section&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;counter-reset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; thmCounter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to reset the theorem counter at the start of each section. I need something for that as well.&lt;/p&gt;
&lt;p&gt;I guess if I have a custom sectioning element, say &lt;code&gt;chapter-&lt;/code&gt;, I could try resetting this:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;chapter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HTMLElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;role&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;doc-chapter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    theorem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// (don&#39;t do this)&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if I make that work, what would I do for a regular HTML sectioning element like &lt;code&gt;article&lt;/code&gt; or &lt;code&gt;section&lt;/code&gt;? Well, I can&#39;t. Instead, I&#39;ll have to exclusively use custom elements for sectioning (&lt;code&gt;sec-&lt;/code&gt; maybe?), so I can do this kind of a reset. [Though maybe that&#39;s not a real problem since I probably want &lt;code&gt;ref-&lt;/code&gt;s pointing to sections.]&lt;/p&gt;
&lt;p&gt;It was funny how dirty this reset felt to me. I mean, it is how CSS does it, right? Then again, perhaps &lt;code&gt;counter-reset&lt;/code&gt; is an excellent footgun on the CSS end? I admit I&#39;ve never used it outside of lists.&lt;/p&gt;
&lt;p&gt;Anyway.&lt;/p&gt;
&lt;p&gt;To keep the counter logic in the theorem element, I need to find out how many theorems precede the current one. Maybe a quick&amp;amp;dirty while-loop along &lt;code&gt;previousElementSibling&lt;/code&gt; is enough - if you&#39;re a super clean author and theorems with related counter values are surely siblings, good for you, I&#39;m probably not. I could also go full on DOM treewalker to walk up the DOM to find the previous theorem, read its counter, increment it (with some break condition to match the counter reset desires).&lt;/p&gt;
&lt;p&gt;As with everything I&#39;ve written here, I have no answer, but at least a few ideas. For now I&#39;m taking what feels like the middle ground - find a closest ancestor of my (reset-)liking, look at the reset&#39;s descendants, use the current element&#39;s index therein.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; siblings &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;closest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;section&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;tagName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connectedCallback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Theorem&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; siblings&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The end result seems good enough for now; easy to muck about, learn, try something.&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;rNPeBKR&quot; data-preview=&quot;true&quot; data-user=&quot;pkra&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/pkra/pen/rNPeBKR&quot;&gt;
  Theorems with counters, part 1&lt;/a&gt; by Peter Krautzberger (&lt;a href=&quot;https://codepen.io/pkra&quot;&gt;@pkra&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io/&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async=&quot;&quot; src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;The code is so trivial that I can throw it away once I&#39;ve learned more (e.g., how about &amp;quot;Theorem 2.1&amp;quot;? should that label be inside the first paragraph? What role to use?).&lt;/p&gt;
&lt;p&gt;As an aside, the links earlier point to &lt;a href=&quot;https://drafts.csswg.org/css-content/#target-counter&quot;&gt;target-counter&lt;/a&gt; from the CSS Generated Content Level 3 spec where you&#39;ll also find target-text. Besides not having gained any traction I admit I find those three oddly unappealing. It seems more natural to me to have &amp;quot;getComputedText&amp;quot; to access the actual pseudo-element value, e.g., when &lt;code&gt;attr()&lt;/code&gt; is involved. But perhaps I&#39;m too JS-oriented here; it just seems to me like people would need this anyway to cover more ground. And maybe it&#39;s lower hanging fruit.&lt;/p&gt;
&lt;p&gt;In any case, surely there&#39;s more trouble ahead. On the bright side, I know this will continue to work for a very (very) long time thanks to the backward compatibility of the web platform. (Definitely longer than the LaTeX stylesheets from my PhD thesis.) And once counters catch up, I can simplify.&lt;/p&gt;
&lt;p&gt;In the end, this first set of experiments has worked out as I&#39;d hoped. It got me thinking (and learning) about web components again, about limitations of the web platform, and about writing itself. More than I expected to get to in a few evenings.&lt;/p&gt;
&lt;p&gt;In the mean time, I&#39;ve continued this journey a bit further. But that&#39;s another story for another time perhaps.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>A first look at MathJax v4</title>
		<link href="https://www.peterkrautzberger.org/0222/"/>
		<updated>2023-12-31T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0222/</id>
		<content type="html">&lt;p&gt;So here on the blog (and more relevantly at work) I&#39;ve been rolling out MathJax v4 after it hit &lt;a href=&quot;https://github.com/mathjax/MathJax-src/releases/tag/4.0.0-beta.3&quot;&gt;its third beta&lt;/a&gt; in July. MathJax v4 is chock full of fixes, features and even a tiny bit of finally-getting-rid-of-stuff. To close off the year, I thought I&#39;d sum up my personal highlights.&lt;/p&gt;
&lt;p&gt;Here&#39;s my (obviously totally personal and biased) short list.&lt;/p&gt;
&lt;h2&gt;STIX Two (and fonts in general)&lt;/h2&gt;
&lt;p&gt;I&#39;ve been using the experimental STIX2 branch in production for a few years but now that Davide had time to completely rewrite the fonts support, the generalization leads to a lot of small (and large) improvements in STIX Two again. It&#39;s lovely to see the best font keeps getting better. (And yes, a better default CM-derived font is cool too, as are the other options!)&lt;/p&gt;
&lt;p&gt;Working together on a small project to build a custom font earlier this year also gave me a first look into the fantastic capabilities (and slightly scary machinery) behind the new font tooling. I can&#39;t wait to see what people might do when it&#39;s opened up to the public.&lt;/p&gt;
&lt;h2&gt;line breaking&lt;/h2&gt;
&lt;p&gt;While I don&#39;t care much about line breaking inside display elements these days, inline reflow has been on my wish list for a long time. But what I hadn&#39;t expected at all was Davide adding support for MathJax&#39;s SVG output.&lt;/p&gt;
&lt;p&gt;That&#39;s a bit unbelievable, really, but MathJax v4 will split up the SVG output into reflowable chunks which is &lt;em&gt;just&lt;/em&gt; lovely. I wished the webfont hack could be avoided but I&#39;m looking forward to hacking a decent compromise there so that this becomes usable in the horrors of epub, too (where I need it the most).&lt;/p&gt;
&lt;h2&gt;ESM&lt;/h2&gt;
&lt;p&gt;You might say &amp;quot;it&#39;s about damn time!&amp;quot; but it&#39;s super sweet to see this move, even if it broke everything during the beta for me.  I don&#39;t think I&#39;ll ever like MathJax&#39;s custom build system for its &amp;quot;components&amp;quot; but &lt;a href=&quot;https://github.com/mathjax/MathJax/issues/3086&quot;&gt;with Davide&#39;s patient help&lt;/a&gt; I could move my third party extensions over to the new system.&lt;/p&gt;
&lt;h2&gt;TeX input&lt;/h2&gt;
&lt;p&gt;Soo many improvements! A lot of subtle changes matching LaTeX behavior more accurately, a few more extensions.&lt;/p&gt;
&lt;p&gt;If I had to pick just one thing, &lt;a href=&quot;https://github.com/mathjax/MathJax-src/pull/783&quot;&gt;HTML-in-TeX support&lt;/a&gt; would be it. The potential seems boundless as MathJax is not limited by obnoxious theoretical-purity nonsense but instead embraces HTML. Which is as it should be.&lt;/p&gt;
&lt;h2&gt;non-visual rendering&lt;/h2&gt;
&lt;p&gt;SRE also saw a massive set of enhancements in &lt;a href=&quot;https://github.com/Speech-Rule-Engine/speech-rule-engine/releases&quot;&gt;its recent releases&lt;/a&gt;. Better performance, more languages, 2D nemeth, plenty of bug fixes. Amazing work by Volker - nothing gets even close to SRE&#39;s range of non-visual capabilities.&lt;/p&gt;
&lt;p&gt;Even more importantly, SRE will (finally!) be active by default in v4 - the most accessible output as the default is good in my book.&lt;/p&gt;
&lt;h2&gt;coda&lt;/h2&gt;
&lt;p&gt;There&#39;d be much more to write but the year is over. For balance, any complaints? I think the only one  I found would be testing which was painful at times - but that&#39;s what you get for adopting a beta release. Still, I don&#39;t know how I&#39;d feel if I didn&#39;t know how good Davide and Volker are (and if I didn&#39;t know how much testing happens at the AMS).&lt;/p&gt;
&lt;p&gt;Of course I still wished that MathJax was not so focused on being client-side first. It is without competition when it comes to server-side-rendering, too - the most robust and most accessible option available. More people need to hear that! But of course I remember why that is: MathJax users love the ease of client-side rendering, they have never cared about the downsides (e.g., page load, performance). Still, I&#39;ll keep spreading the word.&lt;/p&gt;
&lt;p&gt;As it stands MathJax v4 is a massive step forward, proving again that MathJax is the only reliable project to make progress in this space.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Web components for (mathematical) writing, continued</title>
		<link href="https://www.peterkrautzberger.org/0221/"/>
		<updated>2024-01-27T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0221/</id>
		<content type="html">&lt;p&gt;After sharing the &lt;a href=&quot;https://www.peterkrautzberger.org/0220/&quot;&gt;first interesting problem I had run into&lt;/a&gt;, Sam and I spent a few more days working on this experiment. I had hoped to finish this write up before &lt;a href=&quot;https://www.jointmathematicsmeetings.org/meetings/national/jmm2024/2300_program_thursday.html#2300:AMSCOEPAN&quot;&gt;attending JMM 2024&lt;/a&gt; but life was more important. In any case, time for an update.&lt;/p&gt;
&lt;p&gt;If you want to skip to the end, here&#39;s &lt;a href=&quot;https://github.com/pkra/laml-wc/&quot;&gt;a link to the (now public) repository&lt;/a&gt;. You can just browse through the git history if you prefer - I stopped at &lt;a href=&quot;https://github.com/pkra/laml-wc/commits/main/?before=52c8ee52dacc396b208492bcd947f406e4866935+35&quot;&gt;this commit&lt;/a&gt; when finishing up.&lt;/p&gt;
&lt;h2&gt;unexpected pains&lt;/h2&gt;
&lt;p&gt;An overly large chunk of work went into fixing up the example document. That was probably inevitable. I wanted to start with a pre-existing source to avoid the trap of writing content to fit the tools. As a consequence, I had to hunt down markup inconsistencies from 6 years ago including times when I had to just shake my head at my former self (&lt;code&gt;role=&amp;quot;bla blub&amp;quot;&lt;/code&gt; - I don&#39;t even).&lt;/p&gt;
&lt;p&gt;Along similar lines: Sam at some point decided to rename all custom element tags. That was an easy challenge and a good reminder of real world editing with co-authors.&lt;/p&gt;
&lt;h2&gt;that counter problem&lt;/h2&gt;
&lt;p&gt;As I wrote &lt;a href=&quot;https://www.peterkrautzberger.org/0221/0220/&quot;&gt;last time&lt;/a&gt;, counters turned out to be a slightly curious problem so I adjusted the code with the knowledge I had gained around CSS counter issues. Another &amp;quot;fun&amp;quot; fact I learned is that &lt;code&gt;display: contents&lt;/code&gt; breaks counter increments - which is one of those &amp;quot;makes sense and yet I&#39;m annoyed&amp;quot; moments. Since I was using contents to fake sub-grids, that particular case would resolve itself easily in 2024.&lt;/p&gt;
&lt;p&gt;Anyway, for theorems, I ended up going with the simplest approach of using &lt;code&gt;this.closest(bla).querySelectorAll(this.tagName)&lt;/code&gt; to find the index and magically populated cross references with that. Performance wasn&#39;t an issue even on this reasonably large (~80pp) document - and even less of an issue for reasons given below.&lt;/p&gt;
&lt;h2&gt;wasting time&lt;/h2&gt;
&lt;p&gt;I also wasted a modicum of time customizing the enunciation element to allow proofs to re-use it, only to realize that I don&#39;t like this at all. A proof and its label are different to me (in more than one way) so now they have their own, simpler custom element inserting &lt;code&gt;&amp;lt;proof-label&amp;gt;Proof.&amp;lt;/proof-label&amp;gt;&lt;/code&gt; if no such element exists.&lt;/p&gt;
&lt;p&gt;Of course there were plenty of bugs to fix in the new code. You might say it&#39;s because I was just fooling around instead of doing it &amp;quot;properly&amp;quot;, say via test driven development (&lt;a href=&quot;https://www.baldurbjarnason.com/courses/uncluttered/&quot;&gt;which you should totally go an learn from Baldur&lt;/a&gt;). But that was kind of the point of this. I wanted many &amp;quot;oops, this was stupid&amp;quot;-moments. In the end, a lot of that work came from re-using our source of our 6-year-old experiment instead of starting with a clean slate. However, I would like to think that it gave some grounding - and serendipity.&lt;/p&gt;
&lt;h2&gt;a little bit design&lt;/h2&gt;
&lt;p&gt;After I had done the first bit of work on elements, it was time to think a little bit about design. Just something small, to get things going. And somehow I started to think about print (of all things) because I was doing something similar at work. So I dragged out &lt;a href=&quot;https://pagedjs.org/&quot;&gt;pagedjs&lt;/a&gt; and started tweaking print styles. While it started out fun, I fairly quickly hit a mental wall. What was I doing looking up LaTeX class files (of all things)? The idea was to do something for&amp;amp;on the web!&lt;/p&gt;
&lt;p&gt;So I stepped away for a while. I dreamed up a few cool things (sequential diagrams! scroll linked animations! interactive highlighting!) and then faced the reality of the effort that would take, especially during a normal messy daycare/school fall (one germ hitting the family after the other).&lt;/p&gt;
&lt;p&gt;I finally settled on something simple - a few flashy colors, a lot of grid, vertical writing, sticky positioning, silly borders for various enunciations. And yes, some traditional print styles are in there. All this should eventually hint at how different the presentation can be.&lt;/p&gt;
&lt;p&gt;At some point pagedjs starting crashing on me so there currently isn&#39;t a flashy pagedjs print demo. I think I know the problem so I hope I find time to add this back (stay tuned for another update?). What I really want is time to explore print CSS more seriously.&lt;/p&gt;
&lt;p&gt;Is the design well thought out? Not in the slightest. Are there bugs in the design? Yeah! Plenty. But, again, still not the point right now. Polish can come later (if ever).&lt;/p&gt;
&lt;h2&gt;that sweet SSR magic&lt;/h2&gt;
&lt;p&gt;Here&#39;s the weird thing. When people think of web components, they think of something interactive. Because, after all, isn&#39;t HTML enough for &amp;quot;just content&amp;quot;? And really, should you be relying on JavaScript when you can just ship static HTML?&lt;/p&gt;
&lt;p&gt;I agree. But the point of this experiment is not (just) the final content, it is also the creation thereof. There&#39;s plenty of writing about the perils of favoring developer experience over user experience. And using web components for writing content seems like just such a DX trap.&lt;/p&gt;
&lt;p&gt;I hope to avoid it by thinking about server-side-rendering (SSR) from the start. In the end, a custom tag is valid whether or not it is ever registered as a custom element.&lt;/p&gt;
&lt;p&gt;So the ideas is to use SSR to magically render components out of existence, or rather: compile them out so that the document no longer needs JavaScript just for the static content. (I definitely don&#39;t want to get rid of the custom tags.)&lt;/p&gt;
&lt;p&gt;That&#39;s easy enough. I love &lt;a href=&quot;https://github.com/WebReflection/linkedom&quot;&gt;linkedom&lt;/a&gt; for this kind of thing - it&#39;s small, it&#39;s  fast, it&#39;s simple to use - and we can just dynamically import our web component code:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; parseHTML &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;linkedom&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;node:fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    document&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; customElements&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    HTMLElement&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./m522.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// make some globals so customelements.js can find it&lt;/span&gt;&lt;br /&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HTMLElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HTMLElement&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;customElements &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; customElements&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;globalThis&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;document &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  document&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// upgrade/render our elements&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./customelements.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// save&lt;/span&gt;&lt;br /&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ssr.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;p&gt;Naturally, I can&#39;t stop there. I love MathJax most when I use it in an SSR setting so &lt;a href=&quot;https://github.com/pkra/laml-wc/blob/52c8ee52dacc396b208492bcd947f406e4866935/mj.js&quot;&gt;I added that as well&lt;/a&gt;. Well actually. It turned out to be slightly harder. Because I was lazy and the way MathJax detaches nodes, my components were connecting twice, causing duplication. That took a second to work around lazily. I also found a bug which Andrea rejected a while back (I like his opinionated OSS style!) so I added a polyfill and &lt;a href=&quot;https://github.com/mathjax/MathJax/issues/3134&quot;&gt;let the MathJax folks know&lt;/a&gt;. Another second gone.&lt;/p&gt;
&lt;p&gt;Next on my list was building a dedicated pagedjs output but as mentioned, pagedjs started crashing on me so I haven&#39;t returned to it. It&#39;s just as simple, obviously.&lt;/p&gt;
&lt;h2&gt;bits and pieces&lt;/h2&gt;
&lt;p&gt;While wrapping things up at the end of last year, I did a few other tweaks. I DRYed up the code to generate enunciation variations. I renamed &lt;code&gt;name-&lt;/code&gt; elements to &lt;code&gt;h-&lt;/code&gt; because at this point they were only serving as headings. I renamed Sam&#39;s lovely &lt;code&gt;blame-&lt;/code&gt; tag to &lt;code&gt;e-title&lt;/code&gt; (which probably should become &lt;code&gt;enunciation-title&lt;/code&gt; for clarity) because I&#39;ve grown accustomed to this use for the words label and title.&lt;/p&gt;
&lt;p&gt;Finally, Sam asked for a table of contents so I add simplistic toc- element that populates itself. Speaking of conversations, Sam was surprised that I turned almost all cases of &lt;code&gt;*...*&lt;/code&gt; markdown syntax into &lt;code&gt;dfn&lt;/code&gt; elements. That&#39;s somewhat unusual since you&#39;d usually choose &lt;code&gt;i&lt;/code&gt; or &lt;code&gt;em&lt;/code&gt; for this. However, I noticed that almost always these were used for &amp;quot;inline definitions&amp;quot;, e.g., in proofs. We have &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dfn&quot;&gt;a tag exactly for that&lt;/a&gt;; we should use it. And maybe one day Volker will do what we wanted to do years ago and teach speech-rule-engine to pick up definitions to leverage in its heuristics.&lt;/p&gt;
&lt;h2&gt;what&#39;s next&lt;/h2&gt;
&lt;p&gt;So there you have it. A first somewhat longer experiment. And always more to do.&lt;/p&gt;
&lt;p&gt;For example, I need to rewrite the cross-references code to emit from the target element. The reason is twofold. First, the target may not yet be in the DOM when the reference connects, thus things get complicated; that&#39;s only half a reason because the same argument applies the other way around.  Second, the code for cross references would otherwise become weird - so far it&#39;s only been enunciations but when sectioning, figures, equations come into play, the code would become this convoluted list of separate cases.  I think I&#39;d much rather keep the code for links with the code for targets. As a result, we won&#39;t need a custom element anymore - an &lt;code&gt;a&lt;/code&gt; tag will do just fine.&lt;/p&gt;
&lt;p&gt;Next there are some accessibility considerations to take care of. For example, our generic heading element should label its sectioning element. I also prefer enunciations as figures with captions; it fits well semantically and AT provides nice affordances around them. I&#39;ll probably just use ARIA directly here but maybe I&#39;d really like to have time to see how &lt;code&gt;extends&lt;/code&gt; holds up. Maybe more ideas will stop by. And I think I already spotted some unfortunate limitations in the current state of the AOM so that will be fun to dive into.&lt;/p&gt;
&lt;p&gt;Then there&#39;s the bibliography. The sample lecture notes didn&#39;t have one so I disabled the old code for now. We need to try things out in a document that has them (or add some for testing).&lt;/p&gt;
&lt;p&gt;At this point, I still feel pretty good about this. It developed the way I expected and new ideas are still becoming apparent. Complexity is still low (~100 lines of JS, ~500 lines of CSS, both pretty simple stuff). Pop over to &lt;a href=&quot;https://github.com/pkra/laml-wc/commits/main/?before=52c8ee52dacc396b208492bcd947f406e4866935+35&quot;&gt;the code&lt;/a&gt; or just &lt;a href=&quot;https://pkra.github.io/laml-wc/&quot;&gt;the live demo&lt;/a&gt; if you like and let me know what you think.&lt;/p&gt;
&lt;p&gt;I hope I have more time and inspiration to work on this. Why, Sam even threatened to write something with these components. Now wouldn&#39;t that be something.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>On texml and texml-to-html</title>
		<link href="https://www.peterkrautzberger.org/0223/"/>
		<updated>2025-07-26T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0223/</id>
		<content type="html">&lt;p&gt;For the past, uhm, decade? (wut?) I&#39;ve been helping the AMS to build its tooling for TeX to HTML conversion. The first two parts were made open-source in 2022 (texml) and 2023 (texml-to-html). And yet, I&#39;ve never written about them once. Let&#39;s change that.&lt;/p&gt;
&lt;p&gt;There are plenty of tool for TeX-to-HTML conversion, so why another? The reason is fairly trivial: it already existed. The AMS had done some of this for a very long time (e.g. to populate abstract pages and MathSciNet reviews). Since their tooling wasn&#39;t built by some script kiddie trying to hack around TeX but by the AMS&#39;s in-house TeX expert, this turned out to be a great basis.&lt;/p&gt;
&lt;p&gt;When I joined the AMS for a bit in 2013 (while running MathJax), David was more than ready to expand this tooling to full-text publications. I was knowledgeable enough in MathJax to help &amp;quot;outsourcing&amp;quot; math mode layout to MathJax. So we started to experiment.&lt;/p&gt;
&lt;p&gt;David made texml a TeX-to-XML converter (with the output roughly following jats/bits). After a few experiments with with O&#39;Reilly&#39;s htmlbook and eLife&#39;s(/substance&#39;s) Lens, I pitched the AMS that I could build the HTML side of things from scratch. All of this went well enough and we scaled steadily from 2 tiny journals and a few books to all journals and (hopefully soon) all books.&lt;/p&gt;
&lt;p&gt;Now a curious thing happened a while back. Somebody became interested enough to try to get those two open pieces to run. This was honestly surprising given that there&#39;s virtually no documentation alongside two quick youtube videos I had recorded for Sam a while back.&lt;/p&gt;
&lt;p&gt;In a recent conversation, the inescapable question came up: why use texml/texml-to-html rather than all the other tools out there? I generally don&#39;t like &amp;quot;elevator pitch&amp;quot; questions. But I sometimes teach using TeX-to-HTML tools (and how to get something accessible out them).&lt;/p&gt;
&lt;p&gt;When I teach these courses, I tend to expose people to pandoc and make4ht. It&#39;s not that they&#39;re great - honestly, no tooling in this space is particularly great. However, they&#39;re easy to install and they represent the two broad tendencies in this space: staying close to TeX and reaching close to the web.&lt;/p&gt;
&lt;p&gt;I&#39;ve found pandoc to be decent when your priority is the quality of HTML. It&#39;s generally clean, well structured, malleable. But I found its consideration for TeX to be lacking when TeX is the input. Since pandoc seems uninterested in deeply re-implementing TeX&#39;s input (whoever would?), it can struggle with fairly basic TeX constructs.&lt;/p&gt;
&lt;p&gt;This is a decent tradeoff. And given its large community, there is a great variety of additional tooling you can hook up (e.g. custom filters) to get yourself further along. Combined with a strong support for HTML, you can then get really nice side-effects (e.g., customization). It becomes a steep learning curve however.&lt;/p&gt;
&lt;p&gt;On the other end there&#39;s make4ht (the modern tex4ht) which stays very close to TeX, leveraging dvi to get around some of the messiness of TeX as an input. The trade off here is that the resulting HTML is often capturing (and getting stuck reproducing) print layout rather than helping you build good web content (and design). Again, that&#39;s a good trade off in many situation. And if you can hack TeX, you can learn to hack a make4ht config file.&lt;/p&gt;
&lt;p&gt;Since I cheered for it way back when, I feel like I should mention latexml. I haven&#39;t looked at it in great detail in a while but my last evaluation left me thinking that it still suffers from its historic twists. Going from building the DLMF with custom XML semantics to converting all of the arXiv to &amp;quot;vain&amp;quot; HTML leaves a mark. While latxml&#39;s approach appears to be more like pandoc&#39;s on the surface, I&#39;ve always found its output to be more like make4ht - a lot of TeX-derived layout, noisy markup and some core decisions I strongly dislike.&lt;/p&gt;
&lt;p&gt;I also want to mention to plastex. I liked it whenever I gave it a spin but I don&#39;t feel comfortable enough in python to enjoy it (or to suggest in workshops, except for people who are comfortable with python). Still, it has one huge plus in my book: Christian&#39;s Chirun builds on it.&lt;/p&gt;
&lt;p&gt;Anyway, so what about texml and texml-to-html?&lt;/p&gt;
&lt;p&gt;I would say the main thing about texml, texml-to-html, and the rest of the AMS toolchain is that it&#39;s opinionated: texml does not want to support everything under the sun, it supports what AMS publications need - and some things are supported but maybe in intentionally limited or unexpected ways (e.g. to improve HTML output or layout on the web).&lt;/p&gt;
&lt;p&gt;At first sight this may seem no different than the other tools I just described the difference is control.&lt;/p&gt;
&lt;p&gt;You can get away with this when you control the source. The AMS runs an essentially self-sufficient publishing operation. This gives them great control over the production process and helps enforce reasonable standards during production.&lt;/p&gt;
&lt;p&gt;This is a massive shortcut for any tools having to deal with the resulting TeX sources. The amounts of maddening hacks these tools need to support is very limited. You want to hack &lt;code&gt;\relax&lt;/code&gt;? No thank you. Your minipage-infested layout hackery? Goes out the window (or into a graphic). You want tikz to draw across your page? Nope.&lt;/p&gt;
&lt;p&gt;The second cornerstone is that the toolchain was devised for HTML production. Because TeX continues to be in charge of producing the PDF output, you don&#39;t worry about XML-to-print workflows. HTML is where it&#39;s at. This gives you the freedom to re-interpret TeX packages in a way that makes better HTML.&lt;/p&gt;
&lt;p&gt;The third cornerstone is: MathJax-first. Texml handles math mode in a way that optimizes the result for MathJax&#39;s TeX input. Macros get resolved far enough to make sense to MathJax (and ideally no further). IDs are generated and integrated to match text and math mode (e.g., links to tagged expressions), generated graphics (e.g. tikz) are extracted but integrated into TeX input for MathJax to re-combine. Additionally, the work has led to half a dozen MathJax extensions, some of which made their way back into MathJax proper.&lt;/p&gt;
&lt;p&gt;I think that&#39;s the main gist. I suppose the other question is: should you use it?&lt;/p&gt;
&lt;p&gt;I don&#39;t know. Like all tools in this space, it&#39;s easy to point out its many flaws and failings. Even more so, given how specialized it is. I also don&#39;t write much LaTeX myself these days. Nor do I have to deal with students (who are probably using LLMs to generate questionable TeX sources). And I would always tell you: to create web content, learn HTML and CSS (and maybe &lt;a href=&quot;https://www.peterkrautzberger.org/0221&quot;&gt;web components&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Still, I would say this: if you&#39;ve tried &amp;quot;all the tools&amp;quot; and were never quite satisfied, give these a try. If you think &amp;quot;oh, this drives AMS publications, interesting!&amp;quot;, give them a try. &lt;a href=&quot;https://codeberg.org/krautzource/texml2html-demo&quot;&gt;Here&#39;s a demo on codeberg&lt;/a&gt; you can poke. Let me know how it goes.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Probably don&#39;t put equation layout in links</title>
		<link href="https://www.peterkrautzberger.org/0224/"/>
		<updated>2025-10-02T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0224/</id>
		<content type="html">&lt;p&gt;Links with complex content are a long standing accessibility challenge, essentially ever since HTML opened up the &lt;code&gt;a&lt;/code&gt; tag to contain virtually anything else. Equation layout &lt;a href=&quot;https://www.peterkrautzberger.org/0218&quot;&gt;is not accessible&lt;/a&gt; and there is no single ARIA pattern that will help you make it so. So you need to adjust your approach as the situation requires - and, above all, as your users require.&lt;/p&gt;
&lt;p&gt;One of my old sturdy helpers (to make mathjax and sre actually useful server-side) was having &lt;a href=&quot;https://github.com/krautzource/sre-to-tree/issues/41&quot;&gt;a bit of a problem&lt;/a&gt; recently. (You may think this is about links inside equation layout but it&#39;s actually about links wrapping around equation layout; it doesn&#39;t matter if the link is in or outside the equation layout wrapper.)&lt;/p&gt;
&lt;p&gt;It took me forever to decide how I wanted to resolve this. Luckily, real world data came to the rescue: this simply doesn&#39;t seem to happen a lot in practical content. Or rather, this mostly happens when the equation layout is really just flat text (e.g., a number or letter or word). That&#39;s the trivial case - because that&#39;s not actually equation layout - and easy to resolve as it presents none of the actual complications commonly found in equation layout (i.e. highly compressed, ambiguous notation in 2-dimensional layout). However, every once in a while (in this data: 0.1% of the time) authors will put something non-trivial inside such links.&lt;/p&gt;
&lt;p&gt;And that&#39;s a problem. Elementary accessibility advice will tell you: don&#39;t nest interactive elements. One reason why equation layout is not accessible is that it usually requires complex interactive exploration to reach a minimal level of accessibility. When that&#39;s in a link, we have a problem. You don&#39;t have to hit &lt;a href=&quot;https://act-rules.github.io/rules/307n5z&quot;&gt;ACT rule 307n5z&lt;/a&gt; but there&#39;s generally a very good chance that the interaction models will interfere with each other, causing user confusion and developer headaches.&lt;/p&gt;
&lt;p&gt;Now you could go the (client-side) mathjax/sre-way and just nuke the whole thing and build every interaction from scratch; who needs a platform when you have endless spans and javascript? Or you could join the fanatical purists and expect ATs to magically resolve this for you (because they are not part of the platform so you won&#39;t have to prove it actually works - win/win!).&lt;/p&gt;
&lt;p&gt;I&#39;m a fan of the platform. Yes, it sometimes (often?) sucks because it&#39;s still so young and limited, especially when it comes to accessibility. You may have to push it to move things forward but &lt;a href=&quot;https://frankchimero.com/blog/2015/the-webs-grain/&quot;&gt;going against the grain&lt;/a&gt; is unwise. ARIA patterns in particular are fairly limited - but at least they are part of the platform. On my end, user testing has always shown a clear preference among users for standard ARIA patterns. Even if no pattern fits, giving users a pattern they know how to operate removes a significant barrier for them; that&#39;s what it&#39;s about. Users don&#39;t have a problem seeing a pattern used out of its typical context - they&#39;ve probably seen so much worse - as long as it operates like that pattern should.&lt;/p&gt;
&lt;p&gt;Even if it&#39;s not going to be a perfect or even a great fit at least you&#39;re not forcing them to first figure out your magical ideas about UI or the gazillion shortcuts you put in. Seriously, who has time for that when they just want to read something that&#39;s difficult enough to grok as it is? (And yes, of course it makes it worse that accessibility traditions for equation layout remain are still stuck in the print era.)&lt;/p&gt;
&lt;p&gt;So what to do with my little problem? Honestly, I got lucky. The real world data I&#39;m dealing with showed that the non-trivial equation layout inside links was still pretty simple equations, e.g. a simple fraction or a simple base with short superscript. So adapting the simplest of patterns - a flat label - was sufficient. Don&#39;t overthink it, make it usable.&lt;/p&gt;
&lt;p&gt;Of course that leaves the reverse case - links inside equation layout. Whaddayaknow, that&#39;s a hot mess, too, for effectively the same reasons. Let&#39;s see what the real world usage tells me when I get to improving that part.&lt;/p&gt;
&lt;p&gt;All I&#39;m saying is, maybe don&#39;t put equation layout in links. And when you have to, keep them simple so they can be flattened. Don&#39;t mess up your readers for no good reason. Texts with equation layout are usually hard enough. Bonus points: your underlined link (&lt;a href=&quot;https://webaim.org/techniques/hypertext/link_text#underlining&quot;&gt;because they should be&lt;/a&gt;) won&#39;t run into the trouble of (most likely) losing the underline under that equation layout (giving you a WCAG failure).&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Being unhappy with accessible SVGs: text elements</title>
		<link href="https://www.peterkrautzberger.org/0225/"/>
		<updated>2026-01-24T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0225/</id>
		<content type="html">&lt;p&gt;This started out last fall and is mostly a quick observation on screenreader behaviors and turned into some meandering thoughts about &amp;quot;linearizable&amp;quot; SVGs.&lt;/p&gt;
&lt;p&gt;The other day (month/season/year), I found myself thinking about a text role in ARIA. Not exactly original but at least I was thinking about it in the context of SVG. So technically I was thinking about a role for the (woefully unmaintained) &lt;a href=&quot;https://w3c.github.io/graphics-aria/&quot;&gt;Graphics ARIA Module&lt;/a&gt; where I have a hunch a role for text (say, &lt;code&gt;role=graphics-text&lt;/code&gt;) could be useful. The simplest reason is probably handwriting in its various forms, whether it&#39;s signatures, calligraphy, graffiti, or other heavily stylized writing. Generally speaking, this kind of content is not (and often cannot be) represented as SVG text elements; instead it usually appears as SVG paths, rendering a reproduction of text (handwritten or otherwise). But many other forms of stylized text tend to be realized as paths in SVG as well.&lt;/p&gt;
&lt;p&gt;It&#39;s still &lt;em&gt;text&lt;/em&gt; though.&lt;/p&gt;
&lt;p&gt;It&#39;s used as text, it looks like text, its text alternative will match. So, &lt;a href=&quot;https://en.wikipedia.org/wiki/Duck_test&quot;&gt;duck test&lt;/a&gt; passed?&lt;/p&gt;
&lt;p&gt;One obvious objection is: it&#39;s only text if you use the SVG text element. I think that&#39;s a weak argument. SVG&#39;s text element is so limited in terms of design that even text that could be using the text element is often turned into paths. There&#39;s a reason why every vector graphics editor has a function to replace text with paths. While &lt;a href=&quot;https://github.com/w3c/svgwg/issues?q=is%3Aissue%20state%3Aopen%20updated%3A%3E2025-01-01%20sort%3Aupdated-desc&quot;&gt;the SVG spec is finally seeing some activity again&lt;/a&gt;, its &lt;a href=&quot;https://www.w3.org/2024/04/svg-wg.html&quot;&gt;current charter&lt;/a&gt; is focused on maintenance and interop, not new features, so this is not going to improve anytime soon. Besides, ARIA is about expanding developer options to allow solutions that host languages cannot otherwise provide.&lt;/p&gt;
&lt;p&gt;A slightly stronger argument might be that text is somehow special. It provides certain fundamental affordances that nothing else can match. I don&#39;t disagree but, again, ARIA is for when the correct™ way is not available. And ARIA roles are only ever a (weak) signal, a promise from developer to user; it&#39;s the developer&#39;s responsibility to make good on that promise.&lt;/p&gt;
&lt;p&gt;The graphics-aria module only has 3 roles to offer: graphics-document, graphics-object, and graphics-symbol. I&#39;d say none of these fit so I find myself thinking &lt;code&gt;graphics-text&lt;/code&gt; might be reasonable in SVG.&lt;/p&gt;
&lt;h2&gt;Testing text&lt;/h2&gt;
&lt;p&gt;Inevitably this made me wonder whether it&#39;s actually a good idea (for accessibility) to use text elements in SVGs. What actually happens when you use the text element, and what happens more when things turn a little more complex?&lt;/p&gt;
&lt;p&gt;To start off, I took &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/text#example&quot;&gt;the MDN example for SVG text&lt;/a&gt; for a spin, testing different screenreader in different modes (with default settings). A rough list of results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ORCA with Chrome: reads the text content in every mode, no additional cues that this is (inside) an svg. browse mode stops after each text element; no indication that it&#39;s a text element.&lt;/li&gt;
&lt;li&gt;Narrator with Edge: reads the text content in read all, paragraph/browse, and word mode; adds &amp;quot;image&amp;quot; once upon entering (in any mode).&lt;/li&gt;
&lt;li&gt;NVDA with Chrome/Edge: adds &amp;quot;image&amp;quot; to every text element&#39;s content in read-all and browse/paragraph mode; in word navigation adds &amp;quot;image&amp;quot; to first (or last) text element when entering svg, then &amp;quot;leave graphic&amp;quot; when leaving. With Firefox, read-all is similar but browse mode gives only a single combined reading of all text contents (no &amp;quot;stepping through&amp;quot;) when visiting the image (no &amp;quot;image&amp;quot; added); same if navigating by word.&lt;/li&gt;
&lt;li&gt;JAWS with Chrome/Edge: adds &amp;quot;graphic&amp;quot; to every text element in read-all and browse/virtualCursor. Using word navigation, there&#39;s no &amp;quot;graphic&amp;quot; at all. With Firefox, similar to NVDA.&lt;/li&gt;
&lt;li&gt;VoiceOver Mac with Safari 26. Read-all (VO+A) reads the text content, with no additional cues that anything is (inside) an svg but secondary information about text elements (selectable text). VO+right/left arrow exploration is similar and stops at every text element, navigation by word doesn&#39;t do much since each text element is treated separately (and contains 1 word). VO with Chrome behaved the same.&lt;/li&gt;
&lt;li&gt;VoiceOver iOS 26: read all includes the text content, no indication that there&#39;s an SVG around. Swipe left/right walks stops at each element; no indication of SVG. Reading by line or word: same result.&lt;/li&gt;
&lt;li&gt;Talkback (Android 16). With Chrome, read all and manual exploration both read all text elements in order; no additional cues that this is (inside) an inline svg.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So despite &lt;a href=&quot;https://github.com/w3c/svg-aam/issues/33&quot;&gt;the possibly questionable SVG-AAM mappings&lt;/a&gt;, things aren&#39;t terrible but also not great. Firefox sticks out negatively in that I couldn&#39;t find a way to step through the text elements individually (i.e. basic exploration).&lt;/p&gt;
&lt;p&gt;What bugs me is that the SVG context is never surfaced. The addition of &amp;quot;image&amp;quot; and &amp;quot;graphic&amp;quot; is identical to role/element &lt;code&gt;img&lt;/code&gt;; that seems incorrect here. Adding &amp;quot;image&amp;quot; to text element content seems plain wrong (whether once or multiple times). Treating the SVG like a group of some sort and indicating entering/leaving is relevant, but keeping its internal semantics intact is equally so.&lt;/p&gt;
&lt;p&gt;Only VoiceOver on Mac surfaces the nature of the text elements by default. While it probably shows my limitations as a screenreader users that I&#39;m not sure other screenreader surface this information some other way, I doubt few people will consider looking when it&#39;s announced as image.&lt;/p&gt;
&lt;p&gt;I suppose not announcing text element roles is consistent with the view that &amp;quot;real&amp;quot; text is special (or at least it matches the SVG-AAM&#39;s mapping to paragraphs). It&#39;s the default so it needs no additional information/noise. Still, in the context of an SVG, I wonder if this assumption holds up.&lt;/p&gt;
&lt;p&gt;VO announcing text selectability points to the core interaction model of text: you can select it (e.g. for copy&amp;amp;paste). If that&#39;s what text is about, then couldn&#39;t a graphics-text role be used for similar functionality (e.g. selectability of the accessible name)? Probably a dangerous idea. Move along, nothing to see.&lt;/p&gt;
&lt;h2&gt;Rambling a bit&lt;/h2&gt;
&lt;p&gt;When I attended my first W3C event (too) many moons ago, I was amazed by a demo exploring the classic Ghostscript tiger as SVG, deeply annotated with non-visual information. (I want to say it was done by Janina Sajka using Presto-era Opera but I&#39;m not sure I would&#39;ve known at the time). This stuck with me. It helped me when we incorporated ChromeVox&#39;s equation support into MathJax later on and more when Volker further expanded MathJax&#39;s non-visual rendering. You can also see that influence in &lt;a href=&quot;https://www.peterkrautzberger.org/0209/&quot;&gt;my abuse of trees for content&lt;/a&gt; and my more recent &lt;a href=&quot;https://www.peterkrautzberger.org/0219/&quot;&gt;experiment with a granularity walker&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you stay as academic as those, you can find a lot of interesting stuff &lt;a href=&quot;https://vis.csail.mit.edu/pubs/customization/&quot;&gt;like this one from the MIT Visualization Group&lt;/a&gt;. But in recent years I&#39;ve lost faith in this kind of thing. It&#39;s too academic to ever make a significant difference in the real world. There never seems to be enough understanding of &lt;a href=&quot;https://frankchimero.com/blog/2015/the-webs-grain/&quot;&gt;the web&#39;s grain&lt;/a&gt; to help move things forward to the benefit of the wider (accessibility) community. Neither are these academic tools becoming robust solutions for data journalism (or whatever), nor are they helpful in identifying cow paths (and gaps) in the accessibility infrastructure of the web.&lt;/p&gt;
&lt;p&gt;In the general web development world we instead get regular (yearly) posts where someone explains how &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;desc&lt;/code&gt; elements work to set the accessible name nd description. And how to use role=img to make an SVG behave like &lt;code&gt;img&lt;/code&gt;. That makes me a bit sad. At most, you get a post that linearizes something like a simple graph chart. Like, there&#39;s this &lt;a href=&quot;https://www.youtube.com/watch?v=Bqhh0ZOSEq8&quot;&gt;10 year old talk by Léonie Watson and Chaals McCathie Nevile&lt;/a&gt; when that was still fairly new.&lt;/p&gt;
&lt;p&gt;That&#39;s basically still where we are - unless you build role=application-style solutions like the MIT stuff (or client-side MathJax). Take &lt;a href=&quot;https://www.a11y-collective.com/blog/svg-accessibility/&quot;&gt;this recent article from a11y-collective last year&lt;/a&gt;. Don&#39;t get me wrong: it&#39;s good stuff! But just. so. basic. (Obligatory shout-out to one of my old favorites: &lt;a href=&quot;https://css-tricks.com/accessible-svgs/&quot;&gt;Heather Migliorisi&#39;s 2016 post on CSS Tricks&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I feel there&#39;s a massive divide and we&#39;re not working to bridge it. Complex data visualization is one sliver of graphical documents, no matter how important it may be. Nothing good will come from focusing on just its use cases. Take something like &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Strazak_gadzety.svg&quot;&gt;this odd cloth-a-person SVG on wikicommons&lt;/a&gt; or &lt;a href=&quot;https://mermaid.js.org/syntax/quadrantChart.html&quot;&gt;this quadrant chart from the mermaidjs docs&lt;/a&gt; - I doubt any data viz ARIA module will help those much simpler (yet not simple) use cases.&lt;/p&gt;
&lt;h2&gt;A slightly more real world example&lt;/h2&gt;
&lt;p&gt;Here&#39;s a common pattern I see at work. Many graphics in scientific writing contain diagrammatic content and that content is often annotated by text (in particular, labeled). For example, &lt;a href=&quot;https://www.ams.org/journals/jams/2020-33-03/S0894-0347-2020-00942-0/viewer/#fig&quot;&gt;Figure 2 in this paper&lt;/a&gt;,&lt;/p&gt;
&lt;p&gt;Usually, these graphics come in a form that allow for fairly simple linearization of their text alternatives. These are not flowcharts with complex chains and loops, these are not complex data visualizations.&lt;/p&gt;
&lt;p&gt;Here&#39;s a simplified example. (The embedded raster image is by Mamoru800, CC BY-SA 4.0, &lt;a href=&quot;https://commons.wikimedia.org/w/index.php?curid=176062519&quot;&gt;Wikimedia source&lt;/a&gt;).&lt;/p&gt;
&lt;div style=&quot;overflow-x:scroll;overflow-y:visible&quot;&gt;
&lt;svg style=&quot;width:60ch; height: auto;&quot; viewBox=&quot;0 0 600 225&quot;&gt;
  &lt;image href=&quot;https://upload.wikimedia.org/wikipedia/commons/thumb/0/0d/Bildschirmfoto_2025-10-04_um_13.25.54.png/960px-Bildschirmfoto_2025-10-04_um_13.25.54.png&quot; width=&quot;200px&quot; height=&quot;200px&quot; x=&quot;0&quot; y=&quot;0&quot; aria-label=&quot;abstract size comparison of humans and giant calamars. Three black line drawings over  a linear scale with 2 meter increments.&quot;&gt;&lt;/image&gt;
  &lt;text x=&quot;50&quot; y=&quot;210&quot;&gt;Left: Average Human specimen (~1.7m)&lt;/text&gt;
  &lt;text x=&quot;120&quot; y=&quot;160&quot;&gt;Middle: Average Giant Calamar specimen (~9.5m)&lt;/text&gt;
  &lt;text x=&quot;150&quot; y=&quot;100&quot;&gt;Right: Largest known Giant Calamar specimen (12m)&lt;/text&gt;
&lt;/svg&gt;
&lt;/div&gt;
&lt;p&gt;The SVG structure in this contrived example is as follows: first, an image tag linking to a raster graphic; the raster graphic shows a size comparison of giant squids and humans. Next, 3 well positioned text elements annotating 3 areas of the graphic, namely the diagrammatic depiction of a human being as well as the 2 diagrammatic depictions of giant squids (one average and one large specimen).&lt;/p&gt;
&lt;p&gt;It&#39;s contrived insofar as text is rarely done as text elements and I&#39;ve chosen an image tag to reduce complexity (and also because &lt;a href=&quot;https://github.com/dequelabs/axe-core/issues/3969&quot;&gt;I have an old issue open on axe-core&lt;/a&gt; about a false positive for image tags with aria-label). Nevertheless this is representative of a significant chunk of content I come across daily and, biased as I am by my own experience, I feel the world would be better if this had a better solution than sticking a long-description somewhere.&lt;/p&gt;
&lt;p&gt;Coming back to the example, here&#39;s another round of notes from testing this.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ORCA with Chrome: in read all, it reads the image&#39;s aria-label (followed by &amp;quot;image&amp;quot;) and then the text content, again no cues this is an svg. In browse mode similarly, it stops after image and each text element (though sometimes it just moved across all text elements at once). By word, orca reads the entire aria-label (plus &amp;quot;image&amp;quot;) but is able to step through words in the text elements.&lt;/li&gt;
&lt;li&gt;NVDA with Chrome: read all gives the aria-label followed by the text content in DOM order; the image element&#39;s aria-label gets &amp;quot;graphic graphic&amp;quot; added (while text elements still get one &amp;quot;graphic&amp;quot;); browse mode stops at the image element and each text element. Moving by word gives additionally gives cues when exiting the image element as well as exiting the SVG element (&amp;quot;out of graphic&amp;quot;). In Firefox, read all opens with &amp;quot;graphic&amp;quot;, provides aria-label and text, no indication of the end of the graphic or the image and text elements. In browse mode, it reads the same but stops at the usual character limit, i.e. somewhere in the aria-label and in the second text element&#39;s content.&lt;/li&gt;
&lt;li&gt;Narrator with Edge: read-all announces &amp;quot;graphic&amp;quot; when entering the SVG and also after aria-label is announced, otherwise no indications. Browse mode stops with &amp;quot;graphic&amp;quot;, then steps through image (with another &amp;quot;graphic&amp;quot; before the label), then steps through each text element as before. No announcement on exiting the SVG. When going back, visual outline is thrown off a bit. By word, announces graphic when entering from either direction, skips the image tag&#39;s aria-label, steps through the text elements properly (though the visual outline gets tripped up a lot in text elements, possibly due to overflow in my testing viewport); no exit announcement of course.&lt;/li&gt;
&lt;li&gt;JAWS Chrome: read all reads the aria-label and all text content in the DOM order, adding &amp;quot;graphic&amp;quot; after each element&#39;s name. Browse mode steps through each of these, adding &amp;quot;graphic&amp;quot; after aria-label / text contents. As before, no entry or exit announcement for the SVG. Moving by word steps through aria-label and text content in the right order with no additions (i.e., no &amp;quot;graphic&amp;quot; for the image element).&lt;/li&gt;
&lt;li&gt;VoiceOver Safari 26: read all: reads image aria-label and text contents in expected/DOM order, adds &amp;quot;image&amp;quot; after aria-label of image element; no indication of the SVG, secondary information on text elements (selectable text). VO+left/right: steps through image (adding &amp;quot;image&amp;quot;) and text elements, no indication of SVG, secondary information on text. By word: again steps through everything by word; no indication of SVG, secondary information on text. Same with Chrome.&lt;/li&gt;
&lt;li&gt;VoiceOver iOS 26: read all: reads image aria-label and text contents in DOM order, adding &amp;quot;image&amp;quot; after aria-label; no indication of the SVG. swipe left/right: steps through image (adding &amp;quot;image&amp;quot;) and text elements, no indicatio of SVG. By word: again steps through everything by word (funky highlighting of the text elements while stepping through the image elements aria-label); no indication of SVG. By line reads every text element as a whole.&lt;/li&gt;
&lt;li&gt;Talkback (Android 16). With Chrome, read all and manual exploration both read the aria-label (with &amp;quot;image&amp;quot;) and all text elements in order. I also got an announcement (&amp;quot;page&amp;quot;) before the aria-label was read which may again have been due to scrolling; similarly a sound cue after the last text fragment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overall, these results matched my expectations after seeing the behavior for text elements.&lt;/p&gt;
&lt;p&gt;It&#39;s not terribly bad and yet full of gaps in AT behavior, I&#39;d say. While using text elements is rarely an option, I suspect it&#39;s not too relevant here (but I don&#39;t want to sit on this another season). The SVG-AAM mappings seem to work and it looks like the ATs are falling short here. Very likely because this pattern is not encountered much. Possibly also because graphics-aria offers too little. Chicken and egg (and resources).&lt;/p&gt;
&lt;p&gt;Obligatory quote from &lt;a href=&quot;https://adrianroselli.com/2025/04/tweaking-text-level-styles-reprised.html#Update01&quot;&gt;Adrian&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So please test on your own, using this post as a template — not the final word.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Basic&lt;/h2&gt;
&lt;p&gt;This is just one sliver of SVG content and yet improving these &amp;quot;linearizable&amp;quot; SVG structure seems like a fairly small improvement that would provide building blocks (e.g. improving inline svg announcements, text element announcements, and maybe even adding a role) that help here as well as in other scenarios. And they also don&#39;t strike me as high risk for hindering further improvements.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Can this go? On role presentation inheritance.</title>
		<link href="https://www.peterkrautzberger.org/0226/"/>
		<updated>2026-09-16T00:00:00Z</updated>
		<id>https://www.peterkrautzberger.org/0226/</id>
		<content type="html">&lt;p&gt;As co-editor I often feel like the janitor of the ARIA specification. I tend to spend time on things that feel like going into the basement, full of stuff nobody remembers putting there, much of it questionable, and very little that can just be thrown out.&lt;/p&gt;
&lt;h2&gt;As small dent&lt;/h2&gt;
&lt;p&gt;A few weeks months, I stumbled upon &lt;a href=&quot;https://github.com/dequelabs/axe-core/pull/5171#discussion_r3435303382&quot;&gt;this thread on axe&#39;s tracker&lt;/a&gt; where they were discussing test cases for ARIA markup in invalid HTML. I was fairly certain that no ARIA spec says much about invalid HTML markup but off I went to double check the basement.&lt;/p&gt;
&lt;p&gt;This brought me to the spec&#39;s section discussing ARIA&#39;s concept of &lt;a href=&quot;https://www.w3.org/TR/wai-aria-1.3/#presentational-role-inheritance&quot;&gt;Presentational Role Inheritance&lt;/a&gt;. If you don&#39;t know this concept, here&#39;s the gist: if you have complex markup that turns out to not deserve semantics, then it can be cumbersome to remove its semantics (by adding role &lt;code&gt;none&lt;/code&gt;). Take a big HTML table that turns out to be for layout, not data. Adding role &lt;code&gt;none&lt;/code&gt; to not just the &lt;code&gt;table&lt;/code&gt; element but to its caption, body, each row and each cell - that&#39;s just a drag (and error prone). So ARIA wants to make things easier for authors: slap the role on the &lt;code&gt;table&lt;/code&gt; root and the rest is inherited.&lt;/p&gt;
&lt;p&gt;Sounds nice - and it is: disable semantics of a complex HTML snippet with a single use of role. What&#39;s not to like?&lt;/p&gt;
&lt;p&gt;Now what caught my eyes was the following language (in the current ED):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When an explicit or inherited role of none/presentation is applied to an element with the implicit semantic of a WAI-ARIA role that has Allowed Accessibility Child Roles, in addition to the element with the explicit role of none/presentation, the user agent MUST apply an inherited role of none to any accessibility descendants that do not have an explicit role defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The last bit about &lt;strong&gt;any accessibility descendants&lt;/strong&gt; struck me as too broad and - as it turned out - this had only referred to owned elements until &lt;a href=&quot;https://github.com/w3c/aria/commit/bb6baf146e227498a503825a30cbc5d8cc6844d5#diff-0eb547304658805aad788d320f10bf1f292797b5e6d745a3bf617584da017051R506&quot;&gt;a recent search&amp;amp;replace to introduce this new terminology&lt;/a&gt; so it should probably read &lt;strong&gt;accessibility children&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The janitor in me was happy - just a small &lt;s&gt;dent&lt;/s&gt; typo to fix. Issue filed, ARIA WG triage agreed, add it to the list.&lt;/p&gt;
&lt;h2&gt;What&#39;s behind this wall?&lt;/h2&gt;
&lt;p&gt;When I looked again a few weeks ago, however, I became confused. Here&#39;s how the spec currently continues:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Also, when an explicit or inherited role of none/presentation is applied to a host language element which has specifically allowed children as defined by the host language specification, in addition to the element with the explicit role of none/presentation, the user agent MUST apply an inherited role of none to any specifically allowed children that do not have an explicit role defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the one hand, this confirms that we didn&#39;t need to talk about descendants since the concept is applied recursively to &lt;strong&gt;specifically allowed children&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, it&#39;s unclear what the phrase &lt;em&gt;specifically allowed children&lt;/em&gt; means here (just like Allowed Accessibility Child Roles doesn&#39;t really make sense in the earlier quote).&lt;/p&gt;
&lt;p&gt;This stems in part from &lt;a href=&quot;https://github.com/w3c/aria/pull/1454&quot;&gt;another wider terminology change&lt;/a&gt; in ARIA 1.3 replacing &amp;quot;required children&amp;quot; with &amp;quot;allowed accessibility child roles&amp;quot;.&lt;/p&gt;
&lt;p&gt;Going back to ARIA 1.2, here&#39;s the equivalent part (within &lt;a href=&quot;https://www.w3.org/TR/wai-aria-1.2/#presentation&quot;&gt;ARIA 1.2 role presentation&lt;/a&gt;):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Also, when an explicit or inherited role of presentation is applied to a host language element which has required children as defined by the host language specification, in addition to the element with the explicit role of presentation, the user agent MUST apply an inherited role of presentation to any required children that do not have an explicit role defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This seems better at first. Plain &amp;quot;children&amp;quot; make more sense in a host language context. However, the phrase &amp;quot;required children as described by the host language&amp;quot; does not.&lt;/p&gt;
&lt;p&gt;Intuitively, you know what it means. As the spec puts it&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Some elements are only complete when additional descendant elements are provided.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A table needs rows and cells to be complete. A list needs list items. Obvs.&lt;/p&gt;
&lt;p&gt;Unfortunately, that&#39;s not actually true. The closest matching concept is probably the content model. But HTML does not require any children or descendants for, say, a table element. Nor for virtually any other element. Sure, it might make no sense and cause accessibility issues but it&#39;s perfectly valid. So how could we decide when something is required?&lt;/p&gt;
&lt;h2&gt;Does this old projector even work?&lt;/h2&gt;
&lt;p&gt;One idea might be to invert this then. Some elements do require particular parents. Maybe those work? At this point I was too suspicious and a reality check seemed in order.&lt;/p&gt;
&lt;p&gt;So &lt;a href=&quot;https://github.com/w3c/aria/issues/2832#issuecomment-5507424740&quot;&gt;I tested some simple snippets&lt;/a&gt; outside the given examples of table and list. For example, &lt;code&gt;figcaption&lt;/code&gt; can only occur withing &lt;code&gt;figure&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt; only in &lt;code&gt;details&lt;/code&gt;. So what happens when you add role &lt;code&gt;none&lt;/code&gt; to &lt;code&gt;figure&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;While HTML-ARIA doesn&#39;t allow role &lt;code&gt;none&lt;/code&gt;, it seems browser happily make these elements presentational - but neither &lt;code&gt;figcaption&lt;/code&gt; nor &lt;code&gt;summary&lt;/code&gt; then inherit this role. (Thankfully neither would any other content alongside them such as an image in a &lt;code&gt;figure&lt;/code&gt; or the contents of a &lt;code&gt;details&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Perhaps a better example is &lt;code&gt;hgroup&lt;/code&gt;. &lt;a href=&quot;https://w3c.github.io/html-aria/#el-hgroup&quot;&gt;HTML-ARIA allows any role&lt;/a&gt; and &lt;a href=&quot;https://html.spec.whatwg.org/#the-hgroup-element&quot;&gt;its content model&lt;/a&gt; is fairly restrictive - essentially only one heading and surrounding paragraphs are allowed. Lo and behold, if you set role none to an &lt;code&gt;hgroup&lt;/code&gt;, its children&#39;s roles remain untouched.&lt;/p&gt;
&lt;p&gt;And that seems like a good thing! I&#39;d hate to lose a perfectly good heading this way. More generally it makes little sense to inherit role &lt;code&gt;none&lt;/code&gt; when the content model allows all flow or phrasing content.&lt;/p&gt;
&lt;h2&gt;Is there room in the other building?&lt;/h2&gt;
&lt;p&gt;So &lt;a href=&quot;https://github.com/w3c/aria/issues/2832#issuecomment-5631222828&quot;&gt;I went through all HTML elements to look at their content models&lt;/a&gt; and there really isn&#39;t much left, especially once you take into account what HTML-ARIA already prevents (by not allowing role &lt;code&gt;none&lt;/code&gt; on some elements). Maybe &lt;code&gt;canvas&lt;/code&gt; will need an update as part the &lt;a href=&quot;https://github.com/w3c/aria/issues/2758&quot;&gt;html-in-canvas proposal&lt;/a&gt;. Maybe &lt;code&gt;audio&lt;/code&gt; and &lt;code&gt;video&lt;/code&gt; need a look because they are not fully spec&#39;ed on the ARIA end. But beyond that?&lt;/p&gt;
&lt;p&gt;Looking at SVG only muddies the water further. Very few SVG elements allow any roles and their content models are in most cases extremely limited (e.g. &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;ellipse&lt;/code&gt;, &lt;code&gt;line&lt;/code&gt;) and otherwise extremely broad (e.g. &lt;code&gt;svg&lt;/code&gt;, &lt;code&gt;g&lt;/code&gt;, &lt;code&gt;symbol&lt;/code&gt;, &lt;code&gt;use&lt;/code&gt;). Given how SVG is in a poor state regarding ARIA, it wouldn&#39;t really mean much anyway.&lt;/p&gt;
&lt;p&gt;Given all that, tables and lists still make sense to me. Those are very old HTML elements and we need a workaround for legacy table/list layouts. Thankfully, for these elements inheritance also seems to work. (Well, mostly anyway  - Safari does not seem to inherit onto list items.)&lt;/p&gt;
&lt;p&gt;But keeping a complicated general rule for inheritance because two elements have been used poorly in the past? Not a great idea.&lt;/p&gt;
&lt;p&gt;Instead, this fits much better with host-language integration. HTML-ARIA and HTML-AAM already handle orphaned elements. It seems straight forward to me that we can extend this language to role &lt;code&gt;none&lt;/code&gt; inheritance where needed (i.e. tables and lists ). Any other host language is free to do the same. If we ever come to a point where this is so abundant that a simplification is needed, then a new mechanism for this is probably the wiser choice. (I admit I lack the imagination for this.)&lt;/p&gt;
&lt;h2&gt;Trash day&lt;/h2&gt;
&lt;p&gt;So I think this part of the ARIA spec could just g?. Would be nice to clear that room in the basement. We&#39;ll have to see what the &lt;s&gt;teachers&lt;/s&gt; WG will say.&lt;/p&gt;
</content>
	</entry>
</feed>
