<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Sarah Higley</title>
	<subtitle></subtitle>
	<link href="https://sarahmhigley.com/feed.xml" rel="self"/>
	<link href="https://sarahmhigley.com/"/>
	<updated>2025-09-17T00:00:00Z</updated>
	<id>https://sarahmhigley.com</id>
	<author>
    <name>Sarah Higley</name>
	</author>
	
  
  <entry>
    <title><![CDATA[Tooltips in the time of WCAG 2.1]]></title>
    <link href="https://sarahmhigley.com/writing/tooltips-in-wcag-21/"/>
    <updated>2019-08-17T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/tooltips-in-wcag-21/</id>
    <content type="html"><![CDATA[
      <p>TL;DR recommendation: Narrow down your definition of &quot;tooltip,&quot; and jump to <a href="#best-practices-summary">the last section</a>.</p>
<p>Tooltips have been a reliable source of web accessibility woes from the very beginning; or at least from the beginning of graphical web browsers. They have gone by many names: &quot;tooltip,&quot; &quot;infotip,&quot; &quot;toggle tip,&quot; &quot;hint text,&quot; &quot;balloon help,&quot; &quot;info bubble,&quot; &quot;inaccessible overlay of shame&quot;... the list goes on. No matter the name, the same core issues just keep popping up:</p>
<ul>
<li>How do keyboard users access the content?</li>
<li>How do non-mouse pointers (e.g. touchscreens and eye trackers) access the content?</li>
<li>How do blind and low vision users even know the tooltip is there, let alone read it?</li>
<li>If (Internet Lords forbid) there is interactive content inside, how does one access it without accidentally dismissing the tooltip?</li>
<li>How does a user with magnification software move their field of view to read the tooltip without accidentally dismissing it?</li>
</ul>
<h2 id="so-why-do-tooltips-have-so-many-problems%3F">So why do tooltips have so many problems?</h2>
<p>The first hint of a graphical tooltip on the web came in an <a href="https://www.w3.org/MarkUp/draft-ietf-iiir-html-01.txt">early draft of HTML</a> when &quot;title&quot; appeared as an optional attribute on links with the following note:</p>
<blockquote>
<p>The browser software may chose to display the title of the document as a preliminary to retrieving it, for example as a margin note or on a small box while the mouse is over the anchor, or during document fetch.</p>
</blockquote>
<p>At the time that spec was written, graphical <a href="https://en.wikipedia.org/wiki/OutSpoken">screen readers had already existed for over four years</a>. Now, 26 years in the future, we're doing a little bit better. The same inaccessible mouse-based <code>title</code> behavior exists, but at least the <a href="https://html.spec.whatwg.org/multipage/dom.html#the-title-attribute">latest version of the HTML spec</a> explicitly calls out the accessibility problems while warning against using it. Also, <a href="https://www.w3.org/TR/WCAG21/">WCAG</a> is a thing now. But back in the earlier days of the web, it was open season on defining experiences solely for sighted mouse users.</p>
<p>The <a href="http://1997.webhistory.org/www.lists/www-talk.1993q1/0182.html">image tag was first proposed</a> by a browser representative (from Mosaic, since this happened in 1993) as <a href="https://thehistoryoftheweb.com/the-origin-of-the-img-tag/">more of an advance notice than a suggestion</a>. Although the alt attribute was included in the <code>img</code> specification from the very start, <a href="http://jkorpela.fi/html/alt.html#old">practical support was slow</a> and for years screen readers and text-only browsers had no good way of communicating a graphic.</p>
<p>If the history of the image tag seems like an odd digression for an article about tooltips, the reason lies in what happened next: browsers began to actually implement the alt attribute, but they chose to <a href="http://jkorpela.fi/html/altshow.html#tooltip">visually display it as a tooltip</a>, just like the <code>title</code> attribute on links. Although <code>alt</code> still functioned as a text alternative, the tooltip implementation changed how website authors wrote alt text content. There are articles written about the <a href="http://jkorpela.fi/html/alt.html#tooltip">harmful effect of the tooltip treatment</a> as well a <a href="http://www.alanflavell.org.uk//alt/alt-text.html#howlers">compilation of hilarious examples</a> of real live alt text from that time (imagine image after image with the alt text &quot;Click here!&quot;).</p>
<p>Although alt text no longer gets the tooltip treatment in any modern browser, looking back at that era illustrates a deeper issue with the design itself. From the very beginning, the behavior of a native tooltip has made it easy to create content solely for mouse users with good vision while forgetting about everyone else. The alt text tooltip directly demonstrated how easily that type of design can degrade the experience for anyone relying on non-mouse-based interaction or assistive tech. Continuing problems caused by the title attribute's non-inclusive tooltip have also been thoroughly documented (try this wonderfully comprehensive <a href="https://www.24a11y.com/2017/the-trials-and-tribulations-of-the-title-attribute/">24a11y article by Scott O'Hara</a> for a start).</p>
<h2 id="beyond-the-title-attribute%3A-what-are-tooltips-now%3F">Beyond the title attribute: what are tooltips now?</h2>
<p>Usually &quot;use native controls&quot; is the mantra of accessibility professionals, so if the native tooltip is flawed all the way down to its very design, where does that leave designers and developers? The short answer is out in the rain without a popup (or &quot;umbrella&quot; for those who live in the real world) for shelter.</p>
<p>Before diving into the details of how to implement a custom tooltip, let's take a moment to define what a tooltip really <em>is</em>. Conventionally, the term has referred to a purely visual treatment: text that appears in a small overlay on demand, usually when hovering over the thing it describes. This presents a problem when trying to create a specification for a consistent, accessible experience, since visual patterns do not always have a one-to-one relationship with interaction patterns.</p>
<p><em>(Wait. What?)</em></p>
<p>A major pitfall of approaching web design as a visual medium is conflating <em>visual</em> patterns with <em>functional</em> or <em>interaction</em> patterns. One classic example of this is that the word &quot;menu&quot; in web UI has acquired both a broad, generic meaning as well as a specific and technical one. In what passes for casual conversation among web professionals, the word &quot;menu&quot; might refer to a set of links used as site navigation, the file/edit/etc. bar of actions along the top of most applications, or a list of appetizers printed on paper at a restaurant.</p>
<p>Leaving off that last one, keyboard and assistive tech users expect a navigation menu and traditional application menu to function differently: a navigation menu is a list of links that is tabbed through, and an application menu is a collection of menu actions that are reached by arrow keys and often shortcuts. One visual pattern, multiple interaction patterns. To go further down that particular rabbit hole, try this shot: <a href="https://inclusive-components.design/menus-menu-buttons/">inclusive-components.design/menus-menu-buttons</a> followed by this chaser: <a href="https://github.com/w3c/aria-practices/issues/353">github.com/w3c/aria-practices/issues/353</a>.</p>
<p><em>(Back to tooltips)</em></p>
<p>Much like &quot;menu,&quot; the word &quot;tooltip&quot; has come to mean nearly any small, non-modal overlay. While the most traditional use is to provide simple hint text for UI controls (tips for tools, after all), the same visual pattern could be used to display a text alternative for an icon button, a form error message, rich text content (e.g. bold text or a list), or even interactive content. Although all of these use cases could share the same base implementation if visual presentation and mouse interaction were all that mattered, the differences are important for accessibility for the following reasons (in order):</p>
<ol>
<li>Hint text is purely supplemental, and should not override the existing accessible name for a control.</li>
<li>The text alternative for an icon button is its accessible name, and should be associated with the button accordingly.</li>
<li>Rich text formatting can not be conveyed through the usual means of associating hint text with a control (namely <code>aria-describedby</code>).</li>
<li>Interactive content within a popup introduces a whole new set of requirements. It must be easily discoverable by screen readers, follow a logical tab order, be easy to access without dismissing the tooltip and without relying on fine motor control.</li>
</ol>
<p>There is no single DOM structure or javascript implementation that could fill all the requirements of even the few use cases mentioned here, so for the purposes of arriving at some sort of concrete recommendation, popups with rich or interactive content are not considered tooltips. Those patterns would benefit from using a <a href="https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure">disclosure button pattern</a> under the hood. Similarly, a tooltip-style popup that is not tied to an existing interactive control would also benefit from the disclosure pattern. This would prevent it from spawning a useless button purely for the purpose of introducing a tab stop for keyboard access.</p>
<p>With those out of the way, let's take a stab at writing a visually-agnostic spec for a tooltip. The rest of the article, particularly the accessibility recommendations, will assume that a tooltip fits the following definition:</p>
<blockquote>
<p>A &quot;tooltip&quot; is a non-<a href="https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal">modal</a> (or non-blocking) overlay containing text-only content that provides supplemental information about an existing UI control. It is hidden by default, and becomes available on hover or focus of the control it describes.</p>
</blockquote>
<p>That definition could even be narrowed down even further by saying tooltips must provide only descriptive text -- essentially defining it as a custom, accessible version of the title attribute -- but all of the same interaction requirements apply whether the tooltip is used to display a name, description, or error message even if the semantics differ slightly.</p>
<h2 id="the-accessibility-requirements-of-tooltips">The accessibility requirements of tooltips</h2>
<p>Tooltips must be discoverable and readable with a mouse, other pointer devices, keyboard, screen reader, zoom software, and any other assistive technology. They should provide relevant information that may be helpful to learn the UI, but is not required to operate it. Tooltips also should not block a user from performing any other task on the screen.</p>
<p>Not so complicated, right? Let's dive into some of the specifics, broken down into semantics, interaction, and content.</p>
<h3 id="semantics">Semantics</h3>
<p>Meaningful semantics form the backbone of good HTML structure, and help screen readers and other assistive tech provide so many helpful shortcuts for moving around an interface. Headings are headings, links are links, accordions let you know if they are expanded or collapsed, and tooltips are... what exactly?</p>
<p>As described in detail earlier, tooltips can be used for a number of purposes. Even in our stripped-down definition, they could function as a name or a description, and the semantics would be different for each. The trick is to first decide what purpose the tooltip text has, and then assign semantics accordingly.</p>
<h4>Descriptive tooltip semantics</h4>
<p>For the most canonical tooltip purpose -- hint text -- the only two semantic additions are:</p>
<ol>
<li>associate the tooltip trigger with the tooltip via <code>aria-describedby</code> and <code>id</code></li>
<li>ensure the tooltip is unreachable when hidden via <code>aria-hidden</code></li>
</ol>
<p>A full HTML snippet for a sample text field asking for a name, with hint text in a tooltip follows. This HTML snapshot assumes the tooltip is in an open state.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Full Name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">aria-describedby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name-hint<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name-hint<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  Please enter your given name followed by your family name<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<h4>Label tooltip semantics</h4>
<p>Using a tooltip as the accessible name is similar; instead of <code>aria-describedby</code>, the association would be made with <code>aria-labelledby</code>, and the tooltip container would not necessarily need <code>aria-hidden</code>. It is also possible to drop <code>aria-labelledby</code> entirely and have the tooltip text be a child of the control (at least for controls that support children). The main caveat for the name use case is that UI controls should always have some sort of label visible. This technique would not replace the need for a visible label next to an input, for example. A good use would be to add a text alternative for icon buttons.</p>
<h4>Semantics to avoid</h4>
<p>There are certain attributes that are missing in the above descriptions that the more ARIA-conscious may have already noticed. In no particular order, these are some accessibility-related attributes that may seem relevant, but are not currently recommended:</p>
<ul>
<li><code>role=&quot;tooltip&quot;</code>: the unloved child of roles, this omission is perhaps the weirdest in an article specifically about tooltips. While it's not a <em>bad</em> idea to add it to the tooltip element, it doesn't seem to do much good either. The <code>tooltip</code> role does not appear to affect screen reader announcements in any meaningful way -- <code>aria-describedby</code> and <code>aria-labelledby</code> do all the heavy lifting. If you do decide to add it, use it only for descriptive tooltips along with <code>aria-describedby</code>. There is more context in this Github thread: <a href="https://github.com/w3c/aria/issues/979">github.com/w3c/aria/issues/979</a></li>
<li><code>aria-haspopup</code>: although a tooltip may visually look like a popup, this attribute is for more interactive popups -- specifically only menus, listboxes, trees, grids, and dialogs are allowed in conjunction with <code>aria-haspopup</code>. Using a generic value of <code>aria-haspopup=&quot;true&quot;</code> will be interpreted as if it were <code>aria-haspopup=&quot;menu&quot;</code>. Since tooltips are not intended to be interacted with or navigated to, <code>aria-haspopup</code> should not be used to indicate a tooltip.</li>
<li><code>aria-live</code>: as the ultimate fallback for communicating any sort of dynamic page change, <code>aria-live</code> can be a tempting solution to complaints of tooltip text not being read by screen readers. However, live regions have some significant drawbacks for tooltip use: they can not be reliably re-read by a screen reader user, they may interrupt other content (e.g. announcing the name of the control on focus), and a screen reader user can't opt out of hearing them. It's true that <code>aria-describedby</code> may or may not be announced depending on a number of factors including user-selected verbosity settings, but that is the desired behavior for hint text.</li>
</ul>
<h3 id="interaction">Interaction</h3>
<p>The interaction support for displaying, hiding, and reading the tooltip content is the same whether the tooltip is used for a control's name or description.</p>
<h4>Focus and hover</h4>
<p>The first step is to ensure that the visual display can be controlled by either a keyboard or a mouse. To do this, the tooltip should open on focus or mouse over, and closes on blur or mouse out. Combining pointer and keyboard events should not create multiple tooltips or other buggy behavior. Since the UI control associated with the tooltip presumably has some sort of default action on click/enter or space/input, those interactions are not available for use in displaying or hiding the tooltip. If the UI control does not perform any actions or accept user input, it probably shouldn't have a tooltip at all (see the <a href="https://www.w3.org/TR/wai-aria-practices-1.1/#disclosure">disclosure button pattern</a> instead).</p>
<h4>Pointer (lack of) access</h4>
<p>Now that phone browsing has taken over, providing touch access has become imperative and this also benefits users of other non-mouse pointer device such as eye trackers. Unfortunately, one of the major drawbacks to tooltips is that they are inaccessible to touch devices when attached to buttons or links. This is because hover is unavailable on a touch device, and it is also impossible to focus a button or link without activating it. The same limitation exists for other pointer-controlled assistive tech like eye gaze. There is currently no workaround, although tooltips on form inputs will still work as expected.</p>
<h4>WCAG 2.1: dismissable, hoverable, and persistent</h4>
<p>One of the rules added in the Web Content Authoring Guildelines (WCAG) update from 2.0 to 2.1 is the <a href="https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html">1.4.13: Content on Hover or Focus</a> criterion. This new guideline expands the work required to make a compliant tooltip, but should make overlays in general significantly more accessible and less disruptive. WCAG 2.1 requires any content appearing on hover or focus to be dismissable, hoverable, and persistent.</p>
<p>To achieve those, a tooltip should:</p>
<ul>
<li>Hide when a keyboard user presses &quot;Escape&quot; (unless the tooltip will never overlap other content).</li>
<li>Allow a mouse or pointer user to hide the tooltip, ideally through a close button (unless the tooltip will never overlap other content). The tooltip should not re-appear on subsequent hovers -- think of a zoom user trying to center their view on a specific area without triggering the tooltip and obscuring content.*</li>
<li>Allow a mouse user to move their mouse over the tooltip content without dismissing the tooltip -- ideally in a manner that does not require laser focus and precision mouse control.</li>
<li>Remain in view until the user actively dismisses it, or it is no longer valid (e.g. if a loading tooltip appeared, then it could disappear after the content loaded).</li>
</ul>
<p>An extra note for those who closely read WCAG: &quot;unless the tooltip will never overlap other content&quot; is less of an exception than it appears. Even if a tooltip does not appear to overlay other content at a certain screen size, it may do so at a different screen size or level of zoom.</p>
<p>(*) An even more nitpicky side note: WCAG 1.4.13 says that a tooltip should be dismissable &quot;without moving pointer hover or keyboard focus,&quot; then goes on to suggest the Escape key to fulfill this requirement. This makes sense for keyboard users, but not at all for mouse users. On a usability level, people who primarily rely on a mouse or pointer are less likely to know keyboard shortcuts, and may not be able to use them. On a technical level, it is impossible to capture an escape press on a control that is hovered but not focused. A global escape listener would not be able to differentiate between a user wanting to dismiss a tooltip vs. close a dialog, if one were open. In short, the only feasible solution is to provide both keyboard and pointer methods of dismissal.</p>
<h3 id="content">Content</h3>
<p>Tooltips should only ever contain non-essential content. The best approach to writing tooltip content is to always assume it may never be read. As mentioned above, touch devices and other alternative pointers can't reach tooltips on buttons or links, and screen readers sometimes ignore descriptive text by default. It should be possible to infer how to use the UI without reading any tooltips. If that is not the case, it would be best to move all necessary content out of tooltips and into an area with more robust access and discoverability.</p>
<p>In addition to writing only supplemental content, the following should also be true:</p>
<ul>
<li><strong>Write concise tooltip text.</strong> Imagine someone on a small screen or with high zoom needing to pan around just to read the tooltip.</li>
<li><strong>Avoid rich content.</strong> Formatting such as bold text, italics, headings, icons, etc. will not be conveyed through <code>aria-describedby</code> or <code>aria-labelledby</code>.</li>
<li><strong>No interactive content.</strong> Any interactive content such as links or buttons should not be placed within a tooltip.</li>
</ul>
<h2 id="best-practices-summary">Best practices summary</h2>
<ul>
<li>Only interactive elements should trigger tooltips</li>
<li>Tooltips should directly describe the UI control that triggers them (i.e. do not create a control purely to trigger a tooltip)</li>
<li>Use <code>aria-describedby</code> or <code>aria-labelledby</code> to associate the UI control with the tooltip. Avoid <code>aria-haspopup</code> and <code>aria-live</code></li>
<li>Do not use the <code>title</code> attribute to create a tooltip</li>
<li>Do not put essential information in tooltips</li>
<li>Provide a means to dismiss the tooltip with both keyboard and pointer</li>
<li>Allow the mouse to easily move over the tooltip without dismissing it</li>
<li>Do not use a timeout to hide the tooltip</li>
</ul>
<p>If this article leaves you hungry for more tooltip drama and intrigue, never fear. You can continue your journey into tooltip mastery by perusing any of the links under &quot;Further reading.&quot; Also: get involved! Comment on this <a href="https://github.com/w3c/aria/issues/979">W3C tooltip issue</a> with thoughts, questions, and concerns to influence the future of expected tooltip behavior.</p>
<h3 id="code-samples">Code samples</h3>
<p>Here is a simple <a href="https://codepen.io/smhigley/pen/KjoerX">Codepen example</a>, also viewable without the code editors as a <a href="https://s.codepen.io/smhigley/debug/KjoerX">full page pen</a>. Scott O'Hara also has a much more comprehensive, documented <a href="https://github.com/scottaohara/a11y_tooltips">example on github</a>.</p>
<h2 id="further-reading%3A">Further reading:</h2>
<ul>
<li><a href="https://www.24a11y.com/2017/the-trials-and-tribulations-of-the-title-attribute/">www.24a11y.com/2017/the-trials-and-tribulations-of-the-title-attribute</a></li>
<li><a href="https://ebay.gitbook.io/mindpatterns/disclosure/tooltip">ebay.gitbook.io/mindpatterns/disclosure/tooltip</a></li>
<li><a href="https://inclusive-components.design/tooltips-toggletips/">inclusive-components.design/tooltips-toggletips</a></li>
<li><a href="https://a11yproject.com/posts/title-attributes/">a11yproject.com/posts/title-attributes</a></li>
<li><a href="https://developer.paciellogroup.com/blog/2013/01/using-the-html-title-attribute-updated/">developer.paciellogroup.com/blog/2013/01/using-the-html-title-attribute-updated</a></li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Escaping 101]]></title>
    <link href="https://sarahmhigley.com/writing/escaping-101/"/>
    <updated>2019-10-11T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/escaping-101/</id>
    <content type="html"><![CDATA[
      <p>Earlier this week when filling out an expense report -- a tedious chore that nearly everyone wishes would be over roughly five minutes before starting -- I got about halfway through before inadvertently deleting all my progress and forcing myself to start over.</p>
<p>Why? I had used the escape key to collapse a select menu, and ended up closing the modal dialog containing the entire expense form instead. This happened because the select menu really should have been a <code>&lt;select&gt;</code>, but wasn't. Instead it was a custom dropdown, and whoever made it remembered that dropdowns should dismiss on escape, but didn't test that behavior in context. So of course, being an a11y-focused developer, my immediate response was to put off finishing the expense report and start writing a blog post about the escape key instead.</p>
<h2 id="why-escape%3F">Why escape?</h2>
<p>When starting out on the long, pothole-ridden journey to better keyboard accessibility, the three most common pit stops along the road (the Shell, BP, and Chevron, if you will) are Enter/Space, arrow keys, and escape. There are other keys, of course -- Home, End, PageUp, PageDown all make appearances as the occasional drive-through coffee stand or 2am IHOP.</p>
<p>Then there are <a href="https://webaim.org/techniques/keyboard/accesskey">accesskeys</a>, the weird roadside attractions of accessibility: you periodically see signs for them, but almost never actually visit. In the general run of things, however, a developer working on custom keyboard handling for the web will generally spend the most time on these keys:</p>
<ul>
<li><a href="https://marcysutton.com/links-vs-buttons-in-modern-web-applications">Enter or Space</a>: perform the primary action of the control.</li>
<li>Arrow keys: move focus to the previous or next control, when appropriate.</li>
<li>Escape: exit the current context.</li>
</ul>
<p>Enter, Space, and arrow keys are all fairly predictable. By that I mean it is very rare to encounter uncertainty from either the developer or the user around what should happen when the spacebar is pressed.</p>
<p>...and please don't come at me with your split buttons or selectable editable tree items, write your own article ;)</p>
<p>This is a slight oversimplification, but: some combination of enter and space perform the primary action of the currently focused control, and arrow keys shift focus in one or two dimensions.</p>
<p>Escape, on the other hand, is a little trickier. It has become an all-purpose &quot;get me out of here!&quot; key; something like <code>array.pop()</code> but for UI contexts.</p>
<h2 id="when-should-you-escape%3F">When should you escape?</h2>
<p>(Or: what do I mean when I say &quot;UI context&quot;?)</p>
<h3 id="part-1%3A-overlays">Part 1: Overlays</h3>
<p>In its simplest incarnation, a new context is created when an overlay opens and fully or partially covers some other stuff (technical term) in the same window. When this happens, escape should allow the user to return to their previously un-obscured view. Almost all of the definitions of escape-based interactions in the <a href="https://www.w3.org/TR/wai-aria-practices-1.1/">Aria Authoring Practices</a> fall in this bucket:</p>
<ul>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#combobox">Combobox</a></li>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal">Modal dialog</a></li>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton">Menu</a></li>
<li><a href="https://www.w3.org/TR/wai-aria-practices-1.1/#tooltip">Tooltip</a></li>
</ul>
<p>A few more overlays you may find yourself needing to escape from:</p>
<ul>
<li>Slidepanes</li>
<li>Non-modal dialogs (e.g. <a href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/disclosure/disclosure-navigation.html">disclosure buttons with popups</a>)</li>
<li>Custom context menus</li>
</ul>
<p>Generally overlays are pretty easy to recognize. Anything that appears on top of the regular flow of the document, whether <a href="https://www.nngroup.com/articles/modal-nonmodal-dialog/">modal or non-modal</a>, interactive or not, counts as an overlay and should be easy to dismiss. See the <a href="https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html">WCAG 2.1 content on hover or focus</a> criterion, or my earlier <a href="https://sarahmhigley.com/writing/tooltips-in-wcag-21/">long tooltip <s>ant</s> article</a> for more context on why.</p>
<h3 id="part-2%3A-tab-traps">Part 2: Tab traps</h3>
<p>The most common example of a tab trap was already covered in the previous section -- the modal dialog. However, there are other examples of UI elements that block tabbing without visually breaking the page flow:</p>
<ul>
<li>A rich text editor or code editor: tabbing within these inserts an actual tab character, so there needs to be another way of exiting and moving past them.</li>
<li>Too many tab stops: counter-intuitively, too many focusable items can also effectively block keyboard navigation. Imagine a table with 50 rows and a few interactive items per row; it would take a lot of patience to get through 100+ tab stops. A <a href="https://a11yproject.com/posts/skip-nav-links/">skip link</a> at the beginning would help someone who wants to bypass the table entirely, but not someone who wants to browse through the table's data and then either return to the beginning of the table (maybe there are filters or other controls there), or skip past remaining rows.</li>
</ul>
<h3 id="part-3%3A-cancel-changes">Part 3: Cancel changes</h3>
<p>This category of escapable contexts has nothing to do with page navigation, and everything to do with editing content. Any interface that allows the user to switch between reading and editing (like a cell within an editable grid) should provide the option to cancel all changes and return to the read mode at any point when editing.</p>
<p>If edit mode vs. read mode is a full page change (for example, the github gist editor), then a cancel button makes more sense than escape for switching back to read mode. However, for in-place edits (for example, changing a single cell within an editable grid) it makes sense to allow the user to hit escape to cancel and return to read mode.</p>
<h2 id="so-you've-escaped.-now-what%3F">So you've escaped. Now what?</h2>
<p>(Or: where should focus go next?)</p>
<p>Some popups like combobox menus and tooltips never take focus, so no active focus handling needs to occur when they are dismissed.</p>
<p>Other popups should take focus when opened -- modal dialogs, modal slidepanes, some menus -- and should send focus back to the element that originally opened the popup when closed. If that element no longer exists, it gets a bit trickier. The ARIA Authoring Practices Guide has a <a href="https://www.w3.org/TR/wai-aria-practices-1.1/#h-note-7">long note on focus handling when closing modals</a>. The short version is: take a second to think about where the user would expect to be, and send focus there.</p>
<blockquote>
<p><img src="/writing/assets/nope-octopus.jpg" alt="still frame of the nope octopus meme"><br>
Start by putting yourself in your users' frame of mind</p>
</blockquote>
<p>It's also possible to put focus in different places depending on how a modal is closed: for example, a modal form that creates a new table row may move focus to that row when submitted. Even if this type of logic exists, closing via escape should take the user back to the original trigger button.</p>
<p>Our third category, non-modal tab traps, are a little harder. Escape should jump the user out of the tab trap but there's no obvious place to put focus, and the user shouldn't end up immediately re-entering the tab trap on the next tab or shift + tab. There's no way of knowing whether the user intends to move forwards or backwards after exiting, so you can't simply place focus before or after the trap. Combining escape with a skip link can be a powerful workaround, which is what this <a href="https://www-nzgzcsougj.now.sh/studies/grid/simple-actions">grid example</a> does.</p>
<h2 id="handling-conflicts-when-escaping">Handling conflicts when escaping</h2>
<p>(stopPropagation: Just Do It™)</p>
<p>When handling escape key event listeners within an application, it's important to always stop the event from propagating. This prevents multiple nested components from all trying to respond to the same escape, so a user trying to dismiss a toolitp or combobox menu within a modal won't accidentally close the modal (thus bringing us back to the original modal that kicked all this off). A well-placed <code>event.stopPropagation()</code> would have prevented that original bug.</p>
<p>A good rule of thumb when coding UI components is if you ever write a listener for a key event and do anything at all in response to the escape key, then also stop event propagation.</p>
<p>Example:</p>
<pre class="language-javascript"><code class="language-javascript">myCombobox<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keydown'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>  <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">'Escape'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br>    myCombobox<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>    event<span class="token punctuation">.</span><span class="token function">stopPropagation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<h3 id="external-conflicts">External conflicts</h3>
<p>It's uncommon for web apps or websites to ever run into a situation where custom escape handling might interfere with native platform behavior, but when testing with Windows screen readers it's good to know that the first escape press might cause a switch from <a href="https://tink.uk/understanding-screen-reader-interaction-modes/">application mode to browse mode</a>. If this happens, the event may or may not be sent to the DOM as well -- NVDA does not send a keyboard event to the DOM when switching modes, but JAWS does. This is worth remembering, since it is entirely possible for a JAWS user to accidentally dismiss a popup when intending to switch modes. The only real workaround to this is to make escaped contexts easily recoverable -- modals and dropdowns can be re-opened, and tab traps can be re-entered. Ideally something like a modal form would either save user input if closed and re-opened or, even better, live within its own separate page.</p>
<h2 id="final-thoughts">Final Thoughts</h2>
<p>Escape has been used to do some weird things in native applications. In Internet Explorer, escape will clear a text input; same in Microsoft Outlook for Windows and File Explorer. Browser URL bars in Windows and MacOS will not only clear input but also restore the current URL on escape. On Windows, escape lets you exit the application menu and takes you back to whatever last had focus. Pressing escape in the chat window of Microsoft Teams jumps you to the most recent message. And there are certainly more escape oddities out there, waiting to be discovered.</p>
<p>While many of those seem uninituitive to me, they may make sense to someone else. When venturing off the beaten path of documented patterns and simple escape behavior, the ultimate test is always a well-rounded usability study and real-world feedback.</p>
<p>In the meantime, check out <a href="https://www-nzgzcsougj.now.sh/studies/grid/simple-actions">the grid example page</a> for a practical example containing several different uses of escape.</p>
<p>Now to finish that expense report...</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Playing with state]]></title>
    <link href="https://sarahmhigley.com/writing/playing-with-state/"/>
    <updated>2019-11-04T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/playing-with-state/</id>
    <content type="html"><![CDATA[
      <p>In an effort to convince everyone (who reads these blog posts) that I don't only obsess over tooltips, let's talk about another seemingly simple concept and make it unexpectedly complex. This time, we're tackling the play/pause toggle button.</p>
<p>By a play/pause toggle, I mean this thing:</p>
<p><img src="/writing/assets/play-video.jpg" alt="Screenshot of the youtube player with the play button highlighted. Maru is staring at a box in the backgroud."></p>
<p>i.e. that thing you click on (or key press/touch/switch/etc) to get your daily cute cat fix. You press it, it switches from a play icon to a pause icon, Maru jumps into a box, and bliss ensues.</p>
<p>That's where I barge in and ask about that middle part -- switching from play to pause -- and ruin it all (sorry Maru). The thing is, a play/pause button is effectively a <a href="https://inclusive-components.design/toggle-button/">toggle button</a>, by which I mean it switches between two binary states based on user interaction. The established pattern for accomplishing this is by updating the <code>aria-pressed</code> state attribute, which accepts a true/false value.</p>
<p><img src="/writing/assets/toggle-buttons.png" alt="two side by side bookmark buttons, one grey with aria-pressed set to false, and one blue with aria-pressed set to true"></p>
<p>Play/pause buttons (and by extension, start/stop buttons) are the black sheep of the toggle button family. They generally do not have any <code>aria-pressed</code> on/off state defined, and instead change their accessible name from &quot;play&quot; to &quot;pause&quot; when activated. When I say &quot;generally,&quot; I mean this was true for the following sites, chosen for no formal reason other than that I happened to know they would contain media players:</p>
<ul>
<li><a href="https://www.youtube.com/">Youtube</a>: dynamically changes <code>aria-label</code></li>
<li><a href="https://mixer.com/">Mixer</a>: dynamically changes <code>aria-label</code></li>
<li><a href="https://vimeo.com/">Vimeo</a>: dynamically changes name from contents (by swapping out labelled graphics)</li>
<li><a href="https://soundcloud.com/">Soundcloud</a>: dynamically changes both text content and the <code>title</code> attribute</li>
<li><a href="https://www.w3.org/WAI/tutorials/carousels/working-example/">WAI carousel tutorial</a>: dynamically changes text content</li>
<li><s>Twitter's media player</s>: Twitter's media player actually has no accessible name and no state defined for its play button. Whoops.</li>
</ul>
<p>So why does this matter? Traditional toggle buttons switch <code>aria-pressed</code> from true to false, and play/pause buttons change their calculated name from &quot;play&quot; to &quot;pause.&quot; No big deal?</p>
<h2 id="property-vs.-state">Property vs. State</h2>
<p>Most dynamic changes to a UI component (at least, changes that happen while a user is interacting with it) are communicated through state changes rather than property changes. The ARIA spec has this to say about <a href="https://www.w3.org/WAI/PF/aria/states_and_properties#statevsprop">states vs. properties</a>:</p>
<blockquote>
<p>One major difference is that the values of properties (such as aria-labelledby) are often less likely to change throughout the application life-cycle than the values of states (such as aria-checked) which may change frequently due to user interaction. Note that the frequency of change difference is not a rule; a few properties, such as aria-activedescendant, aria-valuenow, and aria-valuetext are expected to change often.</p>
</blockquote>
<p>This comes across as fairly similar to the use of states vs. properties in javascript application frameworks as well -- a common convention is that a change in application state will trigger a re-render, while a change to a property will not (unless manually triggered).</p>
<p>It might therefore seem reasonable to also expect a screen reader to pick up and announce state changes but not property changes. However, as with many things related to ARIA, it is not that simple.</p>
<p>Some properties such as <code>aria-activedescendant</code>, <code>aria-valuenow</code>, and <code>aria-valuetext</code> can be expected to be announced by screen readers when changed (support issues aside). Some state changes (such as <code>aria-disabled</code>) are not consistently announced when changed. However, as a very general rule of thumb, it is safer to assume that a change in state will be communicated than a change in property (and please never change a role during a user interaction).</p>
<p>Side note: when I reference changes that are announced by screen readers, I mean a change to the element that currently has focus that causes some sort of screen reader-generated feedback without the user moving focus.</p>
<p>Most screen readers nowadays rely on <a href="https://alistapart.com/article/semantics-to-screen-readers/">Accessibility API</a> events to get notifications about changes to the DOM. So, for example, when <code>aria-pressed</code> updates from <code>true</code> to <code>false</code> a <code>PropertyChangedEvent</code> will fire, allowing a screen reader to listen to that event and react to it. Each platform's Accessibility API handles this slightly differently (<code>PropertyChangedEvent</code> is specific to <a href="https://docs.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32">UIA</a> on Windows), but the principal is roughly the same. This <a href="https://w3c.github.io/core-aam/#mapping_events_state-change">list of state and property change events</a> details which states and properties should raise API events when changed. Not all of those states and properties will necessarily be communicated by all screen readers, but these are the ones that at least have a mechanism to do so.</p>
<p>All this is relevant since <code>aria-pressed</code> is a state, and the accessible name is a property.</p>
<h2 id="name-changes">Name Changes</h2>
<p>The conventional wisdom is to not change the name of a control while the user is interacting with it. It turns out this is pretty good conventional wisdom: upon testing, I found that while a name change is sometimes announced, it is not nearly consistent enough to be relied upon.</p>
<p>Using this <a href="https://jsfiddle.net/czsnj9xp/show">button code sample</a>, I tested whether name changes and <code>aria-pressed</code> changes were announced using the following screen reader/browser combinations, and using a few different methods of defining the accessible name:</p>
<div class="table-wrapper">
  <table class="table support-table">
    <thead>
      <tr>
        <th></th>
        <th scope="col"><code>aria-label</code></th>
        <th scope="col"><code>aria-labelledby</code></th>
        <th scope="col">content</th>
        <th scope="col"><code>aria-pressed</code></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <th scope="row">NVDA + Firefox</th>
        <td class="true">yes</td>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">NVDA + Chrome</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">NVDA + Edge</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">JAWS + Firefox</th>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">JAWS + Chrome</th>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">JAWS + Edge</th>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">JAWS + IE 11</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">Narrator + Edge</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">iOS VoiceOver + Safari</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">macOS VoiceOver + Safari</th>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
        <td class="true">yes</td>
      </tr>
      <tr>
        <th scope="row">Talkback + Chrome</th>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="false">no</td>
        <td class="true">yes</td>
      </tr>
    </tbody>
  </table>
</div>
<p>Side note: NVDA, you drunk?</p>
<h2 id="actual-recommendations">Actual Recommendations</h2>
<p>You made it this far, congratulations! Time for some concrete recommendations. The big takeaway should be this:</p>
<p><strong>Change the name, but not the state, of play/pause buttons. Use state for all other toggle buttons.</strong></p>
<p>Why? First, because the mechanics of a play/pause (or start/stop) button are so well understood by now that immediate state change feedback is not critical.</p>
<p>This is combined with the fact that using <code>aria-pressed=&quot;false&quot;</code> for a pause button would result in some variation of &quot;play button off&quot; to be read by screen readers, which is not particularly reflective of a visual pause icon. Creating a difference between the programmatic label and visual text (or icon) can cause issues for speech control users, sighted screen reader users, and effective communication between blind screen reader users and visual users (e.g. during a support call).</p>
<p>This one edge case does not take away the general recommendation to change the <code>aria-pressed</code> state, and not the name, for other toggle buttons. Toggle buttons that are part of less well-known interfaces would benefit more from providing immediate feedback, and the only reliable cross-screen-reader cross-browser way to do that is to use <code>aria-pressed</code>.</p>
<p>As a final note, never change both the name and <code>aria-pressed</code> state in tandem. That way, confusion lies. Just imagine trying to parse the meaning of &quot;play button, on&quot; vs. &quot;pause button, off&quot;.</p>
<h2 id="further-reading">Further Reading</h2>
<ul>
<li><a href="https://scottaohara.github.io/a11y_styled_form_controls/src/toggle-button-switch/">Styled Toggle Buttons</a> from Scott O'Hara</li>
<li><a href="https://www.smashingmagazine.com/2017/09/building-inclusive-toggle-buttons/">Building Inclusive Toggle Buttons</a> from Heydon Pickering</li>
<li><a href="https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/toggles">UWP Toggle Switch description</a>: a Windows desktop toggle pattern, for reference</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[&lt;select&gt; your poison]]></title>
    <link href="https://sarahmhigley.com/writing/select-your-poison/"/>
    <updated>2020-01-03T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/select-your-poison/</id>
    <content type="html"><![CDATA[
      <p>I've been thinking, testing, and talking about custom select components and comboboxes a lot over the past year, and finally condensed all of it into a two-part series of articles written for <a href="https://twitter.com/24accessibility">24 Accessibility</a>. It covers both why this whole mess is so difficult and what patterns are out there, including the new ARIA 1.2 pattern in part 1, and then goes into the results of usability testing and concrete implementation recommendations in part 2.</p>
<ol>
<li>Part 1: <a href="https://www.24a11y.com/2019/select-your-poison/">24a11y.com/2019/select-your-poison</a></li>
<li>Part 2: <a href="https://www.24a11y.com/2019/select-your-poison-part-2/">24a11y.com/2019/select-your-poison-part-2</a></li>
</ol>
<p>The end result is a set of three recommended implementations for a select-only or read-only <code>&lt;select&gt;</code> variant, an editable combobox, and an editable multiselect combobox. While I highly recommend reading the recommendations in the context of the second article to get the nuances of why choices were made and what other options there are, here's the quick cheat sheet:</p>
<p>The links each go to a github repository of web components written using <a href="https://stenciljs.com/">StencilJS</a> and Typescript. There is a <a href="https://codepen.io/smhigley/pen/gObMVzv">Codepen implementation</a> with all three implementations written in vanilla JS.</p>
<ol>
<li><a href="https://github.com/microsoft/sonder-ui/tree/master/src/components/select">Select-only combobox</a></li>
<li><a href="https://github.com/microsoft/sonder-ui/tree/master/src/components/combobox">Editable combobox</a></li>
<li><a href="https://github.com/microsoft/sonder-ui/tree/master/src/components/multiselect">Multi-select</a></li>
</ol>
<h2 id="more-links">More Links</h2>
<p>For anyone who would like to play around with all the select component variations tested for this article, they can be found in three separate codepens here:</p>
<ol>
<li><a href="https://codepen.io/smhigley/pen/JjoKgxb">All select-only variations</a></li>
<li><a href="https://codepen.io/smhigley/pen/BayzXbO">All editable combobox variations</a></li>
<li><a href="https://codepen.io/smhigley/pen/GRgjRVN">All multi-select variations</a></li>
</ol>
<p>Finally, the environments for the two usability studies that were run with disabled participants are available here:</p>
<ol>
<li><a href="https://select-study-epxfpuejic.now.sh/studies/combobox">Main combobox study</a></li>
<li><a href="https://select-study-epxfpuejic.now.sh/studies/combobox-apg">Additional mini ARIA 1.2 study</a></li>
</ol>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Roles and relationships]]></title>
    <link href="https://sarahmhigley.com/writing/roles-and-relationships/"/>
    <updated>2020-01-21T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/roles-and-relationships/</id>
    <content type="html"><![CDATA[
      <p>Sometimes after dabbling in gateway ARIA semantics like <code>aria-current</code>, landmark roles, and link-button hybrids, a budding accessibility practitioner might find themselves experimenting with more serious roles like <code>menu</code>, <code>listbox</code>, or even <code>treegrid</code>. These are tantalizing, powerful patterns that allow you to create experiences that are not supported by only vanilla HTML. Unfortunately, they are also brittle; even small mistakes in using these roles can take a user on a very bad trip.</p>
<p>Talk to your kids about ARIA before it's too late.</p>
<h2 id="basic-vs.-composite-patterns">Basic vs. composite patterns</h2>
<p>Composite widget patterns like trees and grids differ from basic controls in both expectations for keyboard behavior and semantic structure. Re: keyboard interaction, they generally contain multiple interactive elements, but are only one stop in the tab order. <a href="https://w3c.github.io/aria/#managingfocus">Custom key handling</a> (primarily arrow keys) is required to provide access to all interactive descendants of the container widget.</p>
<p>Composite widgets also have much more rigid requirements for semantic structure. While a button or a checkbox will have rules regarding what ARIA states and properties they support, they function as single isolated interactive elements. A composite widget role will also determine the allowed roles, states, and properties of its descendants. For instance, a tablist must contain only tabs, and those tabs must be its direct children. In contrast, a set of links within a navigation region could be marked up with or without a list, or four levels deep in divs without interfering with parsing the semantics of either the navigation region or the links.</p>
<p>We're not going to spend any time here on when and why to use a composite widget role over a group of simple interactive elements, though that is certainly an important discussion to have. Instead, let's dive straight into the accessibility tree.</p>
<h3 id="the-accessibility-tree%3A-a-quick-definition">The Accessibility tree: a quick definition</h3>
<p>The accessibility tree is an internal browser construct that is used as an intermediate step between converting the DOM into the low-level <a href="https://alistapart.com/article/semantics-to-screen-readers/">accessibility APIs</a> that screen readers (and potentially other assistive tech) consume. It is also currently distinct from the <a href="https://www.24a11y.com/2019/web-components-and-the-aom/">Accessibility Object Model (AOM)</a>, which is a proposed spec for an API similar to the DOM.</p>
<p>The per-browser instructions for inspecting the accessibility tree are below:</p>
<ul>
<li><a href="https://developers.google.com/web/tools/chrome-devtools/accessibility/reference#pane">Chrome and Chromium Edge</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Tools/Accessibility_inspector">Firefox</a></li>
<li><a href="https://support.apple.com/guide/safari-developer/view-node-properties-for-a-dom-node-dev160f70435/mac">Safari</a></li>
</ul>
<p>Since the accessibility tree is an internal browser abstraction, there are some minor differences between browsers. For example, a plain <code>&lt;div&gt;</code> is represented as a <code>GenericContainer</code> in Chrome, and a <code>section</code> in Firefox. Still, the differences are minor and all implementations allow you to inspect which nodes exist in the accessibility tree, as well as check their calculated names, roles, values, states, and properties.</p>
<figure>
  <img src="/writing/assets/chrome-a11ytools.png" alt="Chrome Elements inspector showing accessibility information about a list on a blog post on this site">
  <figcaption>Chrome shows a subset of the accessibility tree in the Elements pane when inspecting DOM nodes</figcaption>
</figure>
<figure>
  <img src="/writing/assets/firefox-a11ytools.png" alt="Firefox accessibility inspector pane showing the same list">
  <figcaption>Firefox has a separate devtools pane showing the entire accessibility tree</figcaption>
</figure>
<p>I personally prefer the Firefox Accessibility inspector, since it allows you to pick nodes from the rendered page and walk the entire accessibility tree, much like inspecting the DOM in the Elements pane.</p>
<h2 id="relationships-between-nodes">Relationships between nodes</h2>
<p>Composite widgets like listbox, grid, tree, etc. rely on strict parent/child and sibling relationships between accessibility nodes<br>
to communicate calculated information about those relationships to screen reader users. Information like item position within a list, column and row information in a table or grid, and level information in a tree may be missing or incorrect if node hierarchy is not properly defined. The practical impact varies based on browser and screen reader.</p>
<p>Inserting an extra <code>&lt;div&gt;</code> between a table element and a row, or a row and a table cell, can break screen reader shortcuts, column header/row header/cell association, and indexing of columns and rows. This is easy to debug by inspecting the table's generated accessibility tree in the Firefox devtools accessibility pane:</p>
<figure>
  <img src="/writing/assets/table-incorrect-semantics.png" alt="accessibility pane in Firefox devtools showing a broken table structure">
  <figcaption>Grid and row nodes are separate by extra section nodes caused by <code>&lt;div&gt;</code> elements in the DOM</figcaption>
</figure>
<figure>
  <img src="/writing/assets/table-correct-semantics.png" alt="devtools accessibility tree showing a clean table structure">
  <figcaption>No non-grid roles are present between grid/row/cell roles</figcaption>
</figure>
<h3 id="all-composite-widget-roles">All composite widget roles</h3>
<p>Here's a list of all roles that require specific parent/child relationships to function correctly (as of ARIA 1.2):</p>
<ul>
<li><code>combobox</code> - <code>listbox</code> - <code>option</code> (this one is slightly different, see the <a href="https://sarahmhigley.com/writing/select-your-poison/">combobox post</a> for specifics)</li>
<li><code>grid</code> - <code>caption</code> (optional, must be first child if used) / <code>rowgroup</code> (optional) - <code>row</code> - <code>gridcell</code> / <code>columnheader</code> / <code>rowheader</code></li>
<li><code>listbox</code> - <code>group</code> (optional) - <code>option</code></li>
<li><code>menu</code> / <code>menubar</code> - <code>group</code> (optional) - <code>menuitem</code> / <code>menuitemcheckbox</code> / <code>menuitemradio</code></li>
<li><code>radiogroup</code> - <code>radio</code></li>
<li><code>tablist</code> - <code>tab</code></li>
<li><code>tree</code> - <code>group</code> (optional) - <code>treeitem</code></li>
<li><code>treegrid</code> - <code>rowgroup</code> (optional) - <code>row</code> - <code>gridcell</code> / <code>columnheader</code> / <code>rowheader</code></li>
</ul>
<p>These document structure roles are not interactive, but also rely on a strict semantic hierarchy:</p>
<ul>
<li><code>associationlist</code> - <code>associationlistitemkey</code> / <code>associationlistitemvalue</code></li>
<li><code>feed</code> - <code>article</code></li>
<li><code>figure</code> - <code>caption</code> (optional, must be first or last child)</li>
<li><code>list</code> - <code>listitem</code></li>
<li><code>table</code> - <code>caption</code> (optional, must be first child if used) / <code>rowgroup</code> (optional) - <code>row</code> - <code>cell</code> / <code>columnheader</code> / <code>rowheader</code></li>
</ul>
<h3 id="looking-up-correct-semantics">Looking up correct semantics</h3>
<p>The <a href="https://w3c.github.io/aria/">ARIA spec</a> details which roles need specific parent or child relationships, though it's easy to miss. When looking up the documentation for a role, check for entries for <a href="https://w3c.github.io/aria/#mustContain">Required Owned Elements</a> and <a href="https://w3c.github.io/aria/#scope">Required Context Role</a> in the role's Characteristics table. If present, the role in question must be the direct child of a required context role, and its own children must have one of the roles in required owned elements.</p>
<p>For example, the entry for <a href="https://w3c.github.io/aria/#grid">grid</a> notes that it can either have <code>row</code> or <code>rowgroup -&gt; row</code> as required owned elements. Follow those links, and you can piece together what the role heirarchy should be:</p>
<ul>
<li><code>grid</code>
<ul>
<li>optional: <code>caption</code></li>
<li>optional: <code>rowgroup</code>
<ul>
<li><code>row</code>
<ul>
<li>optional: <code>rowheader</code></li>
<li>optional: <code>columnheader</code></li>
<li><code>gridcell</code></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="fixing-an-incorrect-accessibility-tree">Fixing an incorrect accessibility tree</h2>
<p>A few techniques for repairing accessibility tree issues, once found.</p>
<h3 id="1.-fix-the-dom-hierarchy">1. Fix the DOM hierarchy</h3>
<p>No ARIA is always the best ARIA. If it's possible to make the DOM structure match the desired accessibility tree structure, that is always the best and most robust solution.</p>
<h3 id="2.-role%3D%22presentation%22-or-role%3D%22none%22">2. <code>role=&quot;presentation&quot;</code> or <code>role=&quot;none&quot;</code></h3>
<p>Sometimes it isn't possible to alter the DOM structure, whether because of styling needs, support requirements, or use of external UI libraries. In those cases, it can be possible to clean up the accessibility tree through the judicious use of `role=&quot;presentation/none&quot;.</p>
<p>For example, the following broken tree structure:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tree<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item-wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      Item 1<br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sub-items<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sub-Item 1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sub-Item 2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item 2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item 3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span></code></pre>
<p>Could be modified to work as follows:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>tree<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>item-wrapper<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>presentation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      Item 1<br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>group<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>sub-items<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>presentation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sub-Item 1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sub-Item 2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item 2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>treeitem<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Item 3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span></code></pre>
<p>You can try inspecting both in the browser in <a href="https://jsfiddle.net/3yd0guk8/">this JSFiddle</a>.</p>
<p>Note that <code>role=&quot;presentation/none&quot;</code> is only required on elements that come <em>between</em> composite widget roles. It would not be necessary to add an extra role to a <code>&lt;div&gt;</code> that is a child of <code>treeitem</code>, or parent of <code>tree</code>.</p>
<p>The two roles, <code>presentation</code> and <code>none</code>, are synonyms. As of writing, <code>presentation</code> has better support, so is slightly preferable over <code>none</code>.</p>
<h3 id="3.-aria-owns">3. <code>aria-owns</code></h3>
<p>Using <code>aria-owns</code> to point to the <code>id</code> of another element entirely separated in the DOM will establish a programmatic parent/child relationship between the two nodes in the accessibility tree.</p>
<h3 id="4.-display%3A-none-and-visibility%3A-hidden">4. <code>display: none</code> and <code>visibility: hidden</code></h3>
<p>CSS can sometimes affect the accessibility tree. Two examples are <code>display: none</code> and <code>visibility: hidden</code>, both of which will remove a node and all its descendents from the accessibility tree, hiding it from all users equally. If this is not desired, there are <a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">CSS techniques to visually hide content</a> while keeping it accessible to screen readers.</p>
<h2 id="common-(ish)-misconceptions">Common (ish) Misconceptions</h2>
<ol>
<li><code>role=&quot;presentation&quot;</code> is not <code>aria-hidden=&quot;true&quot;</code></li>
</ol>
<p>Applying <code>aria-hidden</code> to an element will remove that element and all its descendants from the accessibility tree entirely, while <code>role=&quot;presentation/none&quot;</code> will only remove the element's default role. Both <code>role=&quot;presentation/none&quot;</code> and <code>aria-hidden=&quot;true&quot;</code> will have the same effect on an <code>&lt;img&gt;</code> tag, but not on an element with content or children.</p>
<ol start="2">
<li><code>aria-controls</code> is not <code>aria-owns</code></li>
</ol>
<p>Even though the combobox pattern has swapped out <code>aria-owns</code> for <code>aria-controls</code>, the two are not interchangeable elsewhere. <code>aria-owns</code> will establish a parent-child relationship between the root node and the referenced node (though reading order will still follow DOM order). <code>aria-controls</code> is a bit more abstract, and has little to no practical impact; only use it for comboboxes, or optionally when the ARIA spec explicitly says it should be used.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Focus management still matters]]></title>
    <link href="https://sarahmhigley.com/writing/focus-navigation-start-point/"/>
    <updated>2020-02-27T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/focus-navigation-start-point/</id>
    <content type="html"><![CDATA[
      <p>The most difficult challenges in programming are:</p>
<ol>
<li>Debating framework choices with a dogmatic fan, and</li>
<li>Naming things</li>
</ol>
<p>There is seemingly no solution for the first problem; you must accept it, adopt a zen-like calm when faced with invocations of bundle sizes and performance benchmarks, and silently plan your backup career doing literally anything else.</p>
<p>For the second problem, naming things, it's generally best to just pick something and move on. After all, any name is better than wasting hours agonizing over whether <code>toggleMenuItemSelection</code> is adequately understandable, descriptive, and concise.</p>
<p>And then, as a counterpoint, we have sequential-focus-navigation-starting-point.</p>
<p><em>You</em> try saying &quot;sequential focus navigation starting point&quot; five times over the course of two minutes, while also trying to explain to someone exactly why they still need to handle focus after closing their dropdown, even though &quot;tabbing works in Chrome.&quot; To prove my point, here is a brief list of names I would much rather use in said calls:</p>
<ul>
<li>focus-start-point</li>
<li>focus-bookmark</li>
<li>starty-focus-party</li>
<li>fast-focus</li>
<li>2-focus-2-furious</li>
<li>fate-of-the-focus</li>
</ul>
<figure>
  <img src="/writing/assets/focus-party-parrot.gif" alt="The party parrot meme, with overlaid text: starty focus party">
  <figcaption>Thank you for reviewing my application to name all future HTML features</figcaption>
</figure>
<h2 id="why-are-we-talking-about-something-focus-something-point%3F">Why are we talking about Something Focus Something Point?</h2>
<p>The sequential focus navigation starting point is a browser feature that enables three types of interaction that were not possible without it:</p>
<ol>
<li>You can follow an internal link to a section or heading, and then start tabbing from that section/heading</li>
<li>You can click anywhere on the page, and begin tabbing from that point</li>
<li>You can hide an element with focus, e.g. by removing it from the DOM or applying <code>display: none</code>, without breaking tabbing.</li>
</ol>
<p>All three behaviors enhance user experience, but the last one in particular has sometimes lured developers into a false sense of security about not handling focus when closing their popups, dropdown menus, or accordions. Although tabbing seems to work fine in these cases, the sequenced-focus-starts-somewhere-around-here-point is only a bandaid; manual focus handling is still necessary for robust accessibility.</p>
<h2 id="where-did-sequential-focus-whatever-come-from%3F">Where did Sequential Focus Whatever come from?</h2>
<p>The idea of a sequential focus navigation starting point began with the need to make same-page anchors work with keyboard navigation. Although same-page linking has been available for as long as <code>&lt;a&gt;</code> elements, keyboard support has often <a href="https://sarahmhigley.com/writing/tooltips-in-wcag-21/">lagged behind visual mouse support</a>, even at a platform level. So although sighted users would immediately have access to the linked section via scroll and could then interact with it using a mouse, keyboard users would have a different experience. As soon as an internal link was activated, focus would jump to the top of the document, and the next tab press would bring you back to the first focusable element in the page.</p>
<p>To fix this, browsers (<a href="https://www.dhs.state.il.us/IITAA/IITAAWebImplementationGuidelines.html#web9.4">possibly starting with IE5</a>) began to send focus to the link target when following a same-page link. This only works if the target is focusable, though. Thus began the long period of recommending <code>tabindex=&quot;-1&quot;</code> be added to to every heading, named anchor, or other static element targeted by an internal link.</p>
<p>The problem is, the visual behavior of internal links still works when the link target isn't focusable, and -- let's be honest -- that's all most developers test for. There was still an opportunity for browsers to improve the keyboard accessibility of this common built-in feature.</p>
<p>Firefox had a solution for this in place <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=262171">as early as 2013</a>, which implemented starting-something-focus-whatsit-navigation-point in all but name. Although keyboard focus would still reset to <code>&lt;body&gt;</code> after activating a link with an unfocusable target, the next tab press would move focus as if the link target had been the active element.</p>
<p><a href="https://www.w3.org/Bugs/Public/show_bug.cgi?id=26907#c0">Internet Explorer was the only other browser</a> that handled non-focusable link targets, though it took a different approach and simply forced the target element to receive keyboard focus even if it was otherwise unfocusable.</p>
<p>Chrome took a bit longer (read: three years) to come around, but did <a href="https://chromium.googlesource.com/chromium/src/+/8870612f01ca55a123efbd7bf7024b236d6c12a6">finally implement the same focus behavior</a> as Firefox in February 2016, with one addition:</p>
<blockquote>
<p>If the element pointed by sequential focus navigation starting point is removed from the document tree, a point where there was the element at would be the starting point.</p>
</blockquote>
<p>To summarize: when Chrome fixed tab order for internal links, they also used the same mechanism to save the location of the last focused item, if that item was removed from the page.</p>
<p>Around the same time, Rob Dodson wrote what is still the most thorough, reader-friendly explanation of sequential focus navigation starting point: <a href="https://developers.google.com/web/updates/2016/03/focus-start-point">Removing Headaches from Focus Management</a>, in which he also mentioned that side benefit of gracefully handling focus when active element was hidden.</p>
<p>Incidentally, the phrase &quot;sequential focus navigation why is this so long starting point&quot; first appeared in the HTML specification in <a href="https://github.com/whatwg/html/commit/7456ed7a11c5e205eb795fba0e1583d5939761ab">November 2014</a>, where it was defined as an optional user agent behavior.</p>
<h2 id="how-sfnsp-works-today">How SFNSP works today</h2>
<p>The <a href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point">HTML spec for starting focus sequentially point</a> still notes that it is optional, but if it exists it does the following (simplified slightly):</p>
<ul>
<li>Is set to the position of a user's click (or more broadly &quot;when the user indicates that it should be moved&quot;)</li>
<li>Is used to determine which element should get focus when the user presses the tab key</li>
<li>When a user navigates to a fragment (e.g. by clicking on a same-page link, or visiting a URL with a &quot;#&quot; identifier), the start point is set to the target element.</li>
</ul>
<p>There is no mention in the spec of using focus-nav-start-point to handle keyboard navigation when the currently focused element is hidden or removed from the DOM. Despite the lack of official sanction, this behavior seems to be increasingly relied upon by web developers when closing open dialogs, dropdowns, accordions, or any other toggleable region. This conflicts with the prevailing wisdom that when removing a focused element from the DOM, an author must manually set focus on another (logical) node. However, if browsers save focus-navigation-starts-here-point where the removed element used to be, keyboard navigation seems to work fine even if focus is not handled.</p>
<p>As of writing, the following browsers match Chrome's behavior and set the sequential focus navigation starting point when the currently focused element is removed or hidden:</p>
<ul>
<li>Chrome</li>
<li>Safari</li>
<li>Firefox</li>
<li>Edge (Chromium)</li>
</ul>
<p>Of all the browsers on Windows and Mac that I have access to, only Internet Explorer and Edge (pre-Chromium) do not do this. You can try it out now by <a href="#assistive-tech-support">jumping to the next section</a> and then hitting tab.</p>
<p>Overall, this is a win for accessibility. For someone relying on keyboard navigation (or any other assistive tech that navigates via focus, like switch devices), getting sent back to the top of the page every single time a developer forgot to handle focus is an enormous pain. The drawback comes when developers don't fully understand exactly what sequential-focus-belaboring-the-point does and doesn't do, or they don't notice focus bugs when keyboard testing.</p>
<p>Focus navigation starting point is not the same as actual keyboard focus. When focus is lost, through following an internal link or hiding a dialog, keyboard focus moves to the <code>&lt;body&gt;</code> tag. If you query <code>document.activeElement</code> in either of these cases, you can observe this. Alternatively, paste the following snippet into your browser console, <a href="#assistive-tech-support">jump to the next section</a> and watch every focus update in real time:</p>
<pre class="language-javascript"><code class="language-javascript">document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'focusin'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br>   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Focus moved to:'</span><span class="token punctuation">,</span> document<span class="token punctuation">.</span>activeElement<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>That difference is extremely important when using assistive tech, since screen readers (for example) do not pay attention to the internal browser concept of focus-starts-here-point. While testing with a keyboard alone may make everything seem fine, trying the same interaction while running a screen reader exposes some rough edges.</p>
<h2 id="assistive-tech-support">Assistive Tech support</h2>
<p>Tests were run against this (extremely simple and not otherwise very accessible) example that contains a few accordions and a dialog, only one of which handles focus on dismissal: <a href="https://jsfiddle.net/eo8x3pcu/show">https://jsfiddle.net/eo8x3pcu/show</a>.</p>
<h3 id="results">Results</h3>
<div class="table-wrapper">
  <table class="table support-table">
<thead>
<tr>
<th>Screen Reader/AT</th>
<th>Browser</th>
<th>Tab Support</th>
<th>Virtual cursor/browse mode</th>
<th>Context change feedback</th>
</tr>
</thead>
<tbody>
<tr>
<td>NVDA</td>
<td>Firefox</td>
<td class="true">Yes</td>
<td class="true">Yes</td>
<td class="false">No</td>
</tr>
<tr>
<td>NVDA</td>
<td>Chrome</td>
<td class="true">Yes</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>NVDA</td>
<td>Edge</td>
<td class="true">Yes</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>JAWS</td>
<td>All</td>
<td class="true">Yes</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>Narrator</td>
<td>Edge</td>
<td class="true">Yes</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>Narrator</td>
<td>Edge (pre-Chromium)</td>
<td class="false">No</td>
<td class="true">Yes</td>
<td class="false">No</td>
</tr>
<tr>
<td>VoiceOver</td>
<td>iOS Safari</td>
<td>-</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>VoiceOver</td>
<td>macOS Safari</td>
<td class="true">Yes</td>
<td class="true">Yes</td>
<td class="true">Yes</td>
</tr>
<tr>
<td>Talkback</td>
<td>Chrome</td>
<td>-</td>
<td class="false">No</td>
<td class="false">No</td>
</tr>
<tr>
<td>ZoomText</td>
<td>All</td>
<td class="true">Yes</td>
<td>N/A</td>
<td class="false">No</td>
</tr>
</tbody>
</table>
</div>
<h3 id="detailed-notes%3A">Detailed Notes:</h3>
<ul>
<li>NVDA + Firefox: both tab and cursor navigation work from the start point, but nothing was announced on close.</li>
<li>NVDA + Chrome: tab works from the start point; scan mode is flaky, and seems to start from whatever is nearest the last (x, y) position of the dismissed focused element. Nothing is announced on close.</li>
<li>NVDA + Edge Chromium: tab works, but NVDA reads &quot;document&quot; when focus is lost.</li>
<li>Narrator + Edge (pre-Chromium): tab starts again from the top, though cursor navigation starts from where the hidden content used to be. Nothing is announced on close</li>
<li>Narrator + Edge Chromium: tab works, but Narrator reads &quot;document&quot; when focus is lost and cursor navigation starts over from the top of the page</li>
<li>JAWS with all Windows browsers: tab works, but the virtual cursor starts over from the top of the page. JAWS with Firefox reads &quot;frame&quot; when focus is lost, and was silent with other browsers.</li>
<li>VoiceOver + Safari on iOS: the VoiceOver cursor jumps somewhere weird and inconsistent (seems to track (x, y) position on screen like NVDA and Chrome), and immediately begins reading from that point. The example that handled focus works as expected.</li>
<li>VoiceOver + Safari on macOS: both tab and the VoiceOver cursor both start where they should, from where focus was lost.</li>
<li>Talkback on Android: the screen reader cursor jumps to the top of the page, and begins reading it from the start.</li>
<li>ZoomText (without a screen reader): When focus is lost, the screen does not re-center on the correct place (most noticeable with the dialog). It works correctly when focus is actively managed.</li>
</ul>
<p>As is (hopefully) clear, there is absolutely no consistency in how screen readers handle lost focus. At worst, the user gets no feedback about the change of context on close, and then the screen reader starts over from the top of the page. ZoomText (and potentially other assistive tech that wasn't tested) is also affected, with the screen failing to re-center somewhere useful to the user.</p>
<p>Scott O'hara also has support results for <a href="https://scottaohara.github.io/testing/skip-link/testing.html">testing skip link focus and virtual cursor</a>.</p>
<h2 id="solutions">Solutions</h2>
<p>The fix is easy: always manage focus when you remove the active element from the DOM!</p>
<p>Supplemental links:</p>
<ul>
<li><a href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point">https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation-starting-point</a></li>
<li><a href="https://developers.google.com/web/updates/2016/03/focus-start-point">https://developers.google.com/web/updates/2016/03/focus-start-point</a></li>
</ul>
<hr>
<blockquote>
<p>sequential focus navigation starting point<br>
sequential focus navigation starting point<br>
sequential focus navigation starting point<br>
sequential foction navigatus starting point<br>
sequential focusing navigation start point<br>
sequential focal navigation startus point<br>
sequential fo-cat nappigation flop point</p>
</blockquote>
<img src="/writing/assets/cat-flop.jpg" alt="kitten flopped over, face down, sleeping">
    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[What&#39;s in a name?]]></title>
    <link href="https://sarahmhigley.com/writing/whats-in-a-name/"/>
    <updated>2020-04-13T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/whats-in-a-name/</id>
    <content type="html"><![CDATA[
      <p>Often when we talk about accessibility problems, we end up talking about a number of different errors that all boil down to a missing accessible name: form fields without labels, images without <code>alt</code>s, icon buttons without readable text, and many more. A missing or incorrect accessible name in some form or other is right up there with poor color contrast in the list of <a href="https://webaim.org/projects/million/#wcag">most common accessibility errors</a> across the web.</p>
<p>More recently, a greater awareness of accessibility and an increase in the use of <a href="https://www.w3.org/TR/wai-aria-1.2/">ARIA attributes</a> has resulted in a sort of reverse naming problem, where elements that cannot or should not be named get artificial names through ARIA. You see this in <code>&lt;div&gt;</code>'s and <code>&lt;span&gt;</code>'s with <code>aria-label</code>'s, or links and buttons whose programmatic name has been manually defined to be different than their visible text.</p>
<p>While an improperly added name is a step up from a missing one, it will often give the illusion of accessibility while masking an underlying problem that remains unaddressed. Both forgetting a name entirely and adding too many names result from not understanding when and how to properly give an element a name.</p>
<p><img src="/writing/assets/give-a-div-a-name.jpg" alt="An illustrated mouse with a cookie, with the text: if you give a div a name, it'll ask for a role to go with it"></p>
<h2 id="what-is-a-%22name%22%3F">What is a &quot;name&quot;?</h2>
<p>There are many different terms that are used to talk about a missing or incorrect accessible name, depending on context. For images, it can be &quot;alternative text&quot; or &quot;the <code>alt</code> attribute&quot;; for tables, &quot;caption&quot;; and for form fields, we usually talk about labels. Here's a non-comprehensive list of terms and concepts that all boil down to talking about names:</p>
<ul>
<li>labels, the <code>&lt;label&gt;</code> element, and associating labels with form fields</li>
<li>table captions</li>
<li>image alt text</li>
<li>the <code>&lt;legend&gt;</code> element</li>
<li>an SVG <code>&lt;title&gt;</code> element</li>
<li><code>aria-label</code> and <code>aria-labelledby</code></li>
</ul>
<p>The reason I'm using &quot;name&quot; or &quot;accessible name&quot; to refer to all of these different concepts is because although they differ in HTML, they all end up mapping to the &quot;name&quot; property (or equivalent) in accessibility APIs and the browser's internal accessibility tree. For example, this screenshot shows how an <code>&lt;img&gt;</code> element's alt attribute is represented in Microsoft Edge's accessibility dev tools:</p>
<figure>
  <img src="/writing/assets/edge-name-a11y.png" alt="A screenshot of a party parrot meme in a previous article, next to the dev tools pane showing computed name and role">
  <figcaption>The computed name that a browser will expose to accessibility APIs can be viewed in developer tools.</figcaption>
</figure>
<p>Most browsers will also let you view exactly where that computed name came from; in this case, the alt attribute:</p>
<figure>
  <img src="/writing/assets/edge-name-a11y-expanded.png" alt="the same party parrot screenshot, with the computed name in dev tools expanded to show the alt attribute defined underneath">
  <figcaption>This can be helpful if the accessible name is not what you think it should be.</figcaption>
</figure>
<p>If you wished to open up <a href="https://accessibilityinsights.io/docs/en/windows/overview">Accessibility Insights for Windows</a>, you could see the same string defined in the image node's Name property in the <a href="https://docs.microsoft.com/en-us/windows/win32/winauto/ui-automation-specification">underlying accessibility API</a>.</p>
<h2 id="what-needs-a-name%3F">What needs a name?</h2>
<p>Not every element can support an accessible name, and even among those that can, not every one should. For example, a <code>&lt;section&gt;</code> element <em>can</em> be named with <code>aria-label</code> or <code>aria-labelledby</code>, but doing so will promote it to being a landmark, which isn't always desired. A list can also be named, though doing so is uncommon and not needed unless there's some context-specific reason for doing so. So, if some elements always need names, others may or may not benefit from a name, and others cannot be named at all, how do you know which is which?</p>
<p>There are a few general categories of elements that should always be named:</p>
<ol>
<li>
<p><strong>Interactive elements</strong>:<br>
These include all form controls, links, buttons, and also complex interactive widgets like tabs, menus, listboxes, and so on.</p>
</li>
<li>
<p><strong>Other focusable elements</strong>:<br>
Although generally non-interactive elements should not be focusable, there are some exceptions like <a href="https://www.gatsbyjs.org/blog/2019-07-11-user-testing-accessible-client-routing/">client-side routing with focus management</a>, a modal dialog, or the target of a skip link. Anything that can receive keyboard focus, whether through tabbing or scripting, should have an accessible name.</p>
</li>
<li>
<p><strong>Charts and graphics</strong>:<br>
Meaningful images, graphs, canvas visuals, and other graphical content needs to be labelled with alternative text. Images and graphics that are not meaningful should be removed from the accessibility tree: with an <code>&lt;img&gt;</code> tag, this is done with an empty <code>alt=&quot;&quot;</code>, and <code>aria-hidden=&quot;true&quot;</code> works for other elements.</p>
</li>
<li>
<p><strong>Landmarks and labelled form groups</strong>:<br>
Sets of checkboxes and radio buttons are nearly always within a form group, or a form group could contain multiple related text inputs, e.g. for a credit card. They should always have an accessible name that matches the visible group label. Traditionally this is done with the <code>&lt;fieldset&gt;</code> and <code>&lt;legend&gt;</code> elements, but can also be accomplished with <code>role=&quot;group&quot;</code> and <code>aria-label</code>/<code>aria-labelledby</code>.</p>
<p>Landmarks need an accessible name when there are multiple instances of the same type of landmark on a page, such as a main navigation region together with a supplementary navigation region. Generic landmarks (with <code>role=&quot;region&quot;</code>) always need an accessible name.</p>
</li>
</ol>
<p>There are multiple different WCAG criteria that cover failing to give an accessible name to an element that needs one. There's <a href="https://www.w3.org/WAI/WCAG21/Understanding/name-role-value.html">Name, Role, Value</a> for interactive controls lacking a name; <a href="https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html">Non-text content</a> for naming, well, non-text content; <a href="https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html">Link Purpose (in context)</a> for links with missing or poorly written names; then there's the catch-all <a href="https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html">Info and Relationships</a> for any other scenario where meaning is implied by visual cues or context but not present programmatically.</p>
<h2 id="how-do-you-name-an-element%3F">How do you name an element?</h2>
<p>Contrary to what you may think, naming an element involves neither a birth certificate nor the HTML <code>name</code> attribute. The <code>name</code> attribute is never directly exposed to the user, and is used only when submitting forms. Birth certificates have thus far been ignored by spec authors as a potential method for naming controls, but perhaps when web UI becomes sentient and self-propagating, we'll need to revisit that.</p>
<p>Right now, there are four different types of ways an element can be assigned a name: from author, from content, from encapsulation, and from legend. The last two are highly specific to certain form elements:</p>
<p>Name from encapsulation, using the <code>&lt;label&gt;</code> element:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  This text will be the checkbox input's name<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span></code></pre>
<p>Name from legend, which sounds epic but ultimately only refers to <a href="https://html.spec.whatwg.org/multipage/form-elements.html#the-legend-element">the <code>&lt;legend&gt;</code> element</a>:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span>This text will name the fieldset element, as long as the legend is the first child<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br>  <span class="token comment">&lt;!-- some form elements go here --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span></code></pre>
<p>Most of the time when naming elements, you'll be using either a name from author, or name from content. Both of these techniques are likely familiar, even if the particular phrases &quot;name from author&quot; and &quot;name from content&quot; sound more like dry spec terms than actual words real people would say to each other.</p>
<p>&quot;Name from content&quot; in its simplest form means an element's accessible name comes from its text content. Take the following example of a submit button that reads &quot;Submit&quot; (because I am a creative person):</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Submit<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>If you were to view that in a browser, you would see that the calculated name of the button is &quot;Submit&quot;, and that the source is the button's contents:</p>
<p><img src="/writing/assets/name-from-contents.png" alt="Edge's accessibility developer tools show the submit button's name is Submit, and that the name comes from its contents"></p>
<p>A button with text content is a simple example; it's also possible to have non-text content contribute to an element's accessible name. In the following example, a link contains both text and an image with alt text. In it, the image gets its name from the <code>alt</code> attribute, and the link is named by the combination of all plain text and other named elements within it:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>images/twitter-icon.png<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Twitter.<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span>Your daily source of all current-events-related anxiety.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre>
<figure style="max-width:450px">
  <img src="/writing/assets/twitter-link.png" alt="Visual rendering of a styled block link with an image above a short line of text">
  <figcaption>The accessible name of the link is "Twitter. Your daily source of all current-events-related anxiety"</figcaption>
</figure>
<p>The image's <code>alt</code> attribute is actually an example of a name from author, nested inside the link's name from content. &quot;Name from author&quot; refers to assigning a name to an element directly, either by passing a string to an attribute like <code>alt</code> or <code>aria-label</code>, or through the use of an associated labeling element, like <code>&lt;label for=&quot;input-id&quot;&gt;</code>.</p>
<p>Some examples of defining a name from author include:</p>
<ul>
<li><code>aria-label</code></li>
<li><code>aria-labelledby</code></li>
<li>the <code>&lt;label&gt;</code> element, associated with an <code>&lt;input&gt;</code> by <code>id</code></li>
<li>the <code>alt</code> attribute on an <code>&lt;img&gt;</code></li>
<li>the <code>&lt;caption&gt;</code> element in a <code>&lt;table&gt;</code></li>
</ul>
<p>Some elements with roles such as <code>table</code> and <code>list</code>, or landmark roles like <code>navigation</code> and <code>main</code>, only support name from author. If these elements are given a name through <code>aria-label</code> or <code>aria-labelledby</code>, screen readers will generally read that name when the user first enters the element, and then go on to read the element's content. This is because the content exists separately from the accessible name, so both the content and the name (if provided) are read separately.</p>
<p>In contrast, roles that support both name from author and name from content will only ever read one or the other. If a name from author is provided, the element's content will be entirely ignored. So if we were to consider the following extremely realistic example snippet:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cats are the best<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Dogs are the best<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>The resulting button would have an accessible name of &quot;Cats are the best&quot;, and someone using a screen reader would not encounter the string &quot;Dogs are the best&quot;. A name from author will always override name from content, when both are supported.</p>
<p>There is an <a href="https://www.w3.org/TR/accname-1.1/#mapping_additional_nd_te">entire spec</a> governing the full set of steps that browsers must take to determine the accessible name and description for any nameable or describable element. It is arcane and complex, and can generally be ignored by web developers. Here is a reduced summary of accessible name methods, ordered from what will override everything else (1) to what will only be used if no other methods are present (4):</p>
<ol>
<li><code>aria-labelledby</code></li>
<li><code>aria-label</code></li>
<li>Native HTML methods: alt, the <code>&lt;label&gt;</code> element, text content</li>
<li>Fallback attributes: <code>title</code> and <code>placeholder</code></li>
</ol>
<p>Ideally, a single control will only be named using one method, so the exact way browsers calculate an accessible name should not matter much (unless you work on browser accessibility, in which case, thank you). Generally when in doubt, consult the <a href="https://sarahmhigley.com/writing/roles-and-relationships/#the-accessibility-tree%3A-a-quick-definition">accessibility information in your browser's developer tools</a>. That will tell you both the calculated accessible name, and where it's coming from.</p>
<p>While there are a good number of possible ways to name a control, not all options are created equal. Rather than go into the nuances of why to choose a <code>&lt;label&gt;</code> element over <code>aria-label</code> here, I'd rather link to the article Adrian Roselli has already written on his <a href="https://adrianroselli.com/2020/01/my-priority-of-methods-for-labeling-a-control.html">priority of methods for labeling a control</a>. The TL;DR is:</p>
<ol>
<li>Native HTML techniques (the <code>&lt;label&gt;</code> element, text content, <code>alt</code>, etc.)</li>
<li><code>aria-labelledby</code></li>
<li>visually hidden text content</li>
<li><code>aria-label</code></li>
</ol>
<p>His article goes into more of the reasons around why some methods are better than others, which I won't duplicate.</p>
<p>One last note on adding an accessible name: the name must be added directly to the element that you intend to be named. If you want to name a table using the <code>aria-labelledby</code> attribute, then that attribute <em>must</em> be on the <code>&lt;table&gt;</code> or <code>role=&quot;table&quot;</code> element. Putting <code>aria-labelledby</code> on a parent of the table will do nothing at all.</p>
<h2 id="which-elements-and-roles-can-be-named%3F">Which elements and roles can be named?</h2>
<p>Every <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles">ARIA role</a> falls into one of three naming possibilities: either it must have a name, it can optionally be named, or it cannot be named at all. Throw in the different possibilities for naming (from author, from content, from encapsulation, and from legend), and it gets to be a bit too much to keep track of.</p>
<p>Luckily, the ARIA spec has a section listing every single role that can be named, broken down by the naming method. Roles that require names are designated with &quot;(name required)&quot;. Here are the sections where you can find the naming information:</p>
<ul>
<li><a href="https://w3c.github.io/aria/#namefromauthor">Name from author</a></li>
<li><a href="https://w3c.github.io/aria/#namefromcontent">Name from content</a></li>
<li><a href="https://w3c.github.io/aria/#namefromencapsulation">Name from encapsulation</a></li>
<li><a href="https://w3c.github.io/aria/#namefromlegend">Name from legend</a></li>
<li><a href="https://w3c.github.io/aria/#namefromprohibited">Name prohibited</a></li>
</ul>
<p>The ARIA spec lists these only by role and not by element, but if you're not sure which role an element has by default, you can look it up in the <a href="https://w3c.github.io/html-aam/#html-element-role-mappings">HTML AAM spec</a>. If an explicit <code>role</code> attribute is present, it will override the default role mapping in HTML AAM. For example:</p>
<ul>
<li><code>&lt;button&gt;</code>: look up the <code>button</code> role</li>
<li><code>&lt;button role=&quot;tab&quot;&gt;</code>: look up the <code>tab</code> role</li>
</ul>
<p>While the most common problem is for one of the &quot;name required&quot; roles to lack an accessible name, sometimes the reverse occurs and an element that <a href="https://w3c.github.io/aria/#namefromprohibited">cannot be named</a> -- e.g. a <code>&lt;div&gt;</code> or <code>&lt;span&gt;</code> -- is given an <code>aria-label</code> or <code>aria-labelledby</code>. A couple cases where I've seen this happen include a <code>&lt;span aria-label=&quot;some text&quot;&gt;</code> wrapping a font icon, or an <code>aria-label</code> added to a div in a <a href="https://inclusive-components.design/cards/">card pattern</a>.</p>
<p>Both of these examples come from good intentions, and an active attempt to create better accessibility. On the plus side, a few extra <code>aria-label</code>'s on unnameable elements like this won't cause any negative side effects. The main danger is that the illusion of having given something an accessible name will obscure underlying issues. Usually the fix is to revisit which elements and roles should be used, or to reconsider what really needs a name.</p>
<p>In the case of the font icon example, the best solution would likely change the element into an image with <code>role=&quot;img&quot;</code>, which can then be given a name:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>name of icon<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity" title="&#xe900;">&amp;#xe900;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>A generic wrapping <code>&lt;div&gt;</code> for something like a card pattern is more complex. Does that section of content really need its own name, separate from the content within it? If this grouping of content is important enough to be named, then it should probably be exposed as some sort of group. Would an existing HTML pattern like a list fit your use case? Is there another element or role that would fit better?</p>
<p>A card pattern could be exposed as a list item within a list (as Heydon does in his <a href="https://inclusive-components.design/cards/">card pattern</a>), or perhaps as an <a href="https://w3c.github.io/aria/#article">article role</a>. Both those roles (<code>listitem</code> and <code>article</code>) can be given a name from author, but that doesn't mean they need one. To decide whether a name-optional element would benefit from a name in this case as in others, the best thing to do is to consider how a programmatic name will translate into actual user experience. And then, of course, check those assumptions with people who rely on assistive technologies.</p>
<h2 id="how-is-the-accessible-name-used-by-assistive-tech%3F">How is the accessible name used by assistive tech?</h2>
<p>The programmatically exposed accessible name is primarily used by screen readers (including screen readers with braille displays) and voice control software. Voice control software can vary a lot in whether it uses the accessible name, text content, or some combination of the two. The programmatic name is not presented directly to the user, but consumed under the hood when a user issues a command like &quot;press submit button.&quot;</p>
<p>Screen readers do present the accessible name directly to the user, in a number of different ways depending on the mode of interaction and the type of element that is named:</p>
<h3 id="direct-interaction">Direct interaction</h3>
<p>The most straightforward way a screen reader exposes the accessible name is when the user interacts directly with the named element. This can come in the form of tabbing to a focusable element like a form field, link, or button, which is often the most beginner-friendly way of using a screen reader.</p>
<p>Another way to directly interact with an element is to use the screen reader's virtual cursor to move through a page, using sets of commands specific to each screen reader. This will present either the accessible name of a nameable control (heading, link, button, etc.), or a text string in the case of unnameable elements (divs, paragraphs, and so on).</p>
<h3 id="container-roles">Container roles</h3>
<p>There is another type of nameable element that I'm going to call container roles, which include landmark regions, forms, lists, dialogs, tables, tabpanels, and generic groups. Usually when a screen reader user navigates the page, they will enter and exit these elements without interacting with them directly (emphasis on &quot;usually&quot;). If named, the name will generally be read when the screen reader's cursor first enters the container role, depending on the screen reader's support for that role (test everything).</p>
<h3 id="elements-list">Elements list</h3>
<p>One last way of consuming an accessible name is in an elements list. With an elements list, a screen reader user can review all elements of a particular type (e.g. links) out of context. Here's an example of what that looks like in NVDA:</p>
<figure>
  <img src="/writing/assets/nvda-links-list.png" alt="Screenshot of NVDA's elements list, showing links in an article">
  <figcaption>All links within an <a href="https://a11yproject.com/posts/operating-system-and-browser-accessibility-display-modes/">A11y Project article</a> displayed in the Elements List dialog</figcaption>
</figure>
<p>The elements list is one example of when it is important to write unique, descriptive accessible names. Multiple duplicate buttons under different headings, or link names that only make sense in context will be confusing and unhelpful in the elements list.</p>
<h2 id="how-do-you-write-a-good-name%3F">How do you write a good name?</h2>
<p>A developer's blog (or at least this developer's blog) is probably not the best place to look for advice on the writing process. I personally have enough trouble trying to name variables in code that no one else will ever read. Instead, try this page of <a href="https://developer.apple.com/videos/play/wwdc2019/254/">writing tips from the Web Accessibility Initiative (WAI)</a>, or this <a href="https://developer.apple.com/videos/play/wwdc2019/254/">video on writing good labels</a> from Jordyn Castor at Apple.</p>
<p>Instead, let's go over some general accessibility tips for writing a good name:</p>
<ul>
<li><strong>Avoid including the type of control in the name</strong><br>
The type of control will already be communicated by screen readers. For example, naming a submit button &quot;Submit button&quot; will result in a redundant announcement, e.g. &quot;Submit button button&quot;.</li>
<li><strong>Make names unique</strong><br>
Unique names help screen reader users scanning controls directly without needing to examine surrounding context, and they also help sighted users by reducing the cognitive load required to determine the purpose of a control. Visually scanning an online store's buttons is easier when each has a unique name like &quot;shop computers&quot; and &quot;shop laptops&quot;, and harder when all of them read &quot;shop now&quot;. The exception is when multiple controls do the exact same thing. For example, there are two links named &quot;card pattern&quot; on this page, but they both go to the same URL.</li>
<li><strong>Make names easy to skim</strong><br>
Concise, descriptive names are easier to quickly skim, and reduce the time required to process and comprehend each individual control. This is particularly important when many similar named items appear grouped together, such as row headers in a table, or options in a listbox. If all the names share a common string, putting the most unique, relevant information first will make them faster to skim. So a list of dates would do better as &quot;13 April 2020&quot; rather than &quot;April 13, 2020&quot;.</li>
</ul>
<h3 id="when-should-you-override-the-visual-name%3F">When should you override the visual name?</h3>
<p>Sometimes the desire to provide a unique, descriptive name to screen readers can conflict with an existing design, or lead to arguments with others whose opinions are not entirely based in a desire for accessibility. In those cases, it can be tempting to accept less-than-ideal visual labels while modifying the accessible name behind the scenes. For example, a developer tasked with improving the accessibility of an online storefront with 10 &quot;Shop now&quot; links might end up adding a more descriptive <code>aria-label</code> to each link while keeping the visual text the same:</p>
<pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- Don't do this --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/shop/dresses<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Shop Dresses<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Shop Now<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre>
<p>This is not ideal for a number of reasons, even though on first consideration it seems to improve the screen reader experience without the uncomfortable messiness of revisiting design. Unfortunately, creating separate visual and programmatic names causes its own new set of problems.</p>
<p>To start with, not all screen reader users are blind. People with low vision who use some form of magnification or high contrast might use a screen reader as a supplement. Others with no visual impairments might prefer to listen to information rather than look at it for cognitive or sensory reasons. It is not all that unusual for someone to listen to the programmatic name with a screen reader while also looking at the visual label. If those two diverge, it could be jarring and cause confusion. Even fully blind screen reader users sometimes need to communicate with tech support or sighted colleagues, and separate content can make that difficult.</p>
<p>Different names can also cause problems for someone using a voice control software like Dragon Naturally Speaking. A Dragon user confronted with 10 &quot;shop now&quot; links would not be able to take advantage of the unique but invisible &quot;shop dresses&quot; programmatic name. In WCAG 2.1 there is a new criterion, <a href="https://www.w3.org/WAI/standards-guidelines/wcag/new-in-21/#253-label-in-name-a">Label in Name</a>, specifically for improving this experience.</p>
<p>All this doesn't mean that there are never good reasons to create separate programmatic and visual names. In cases where the visual name is an icon or graphic, or the visual context is conveying information that cannot be easily captured as a text label, a custom programmatic name might make sense.</p>
<p>A good time to use hidden text or an <code>aria-label</code> is when you have no visible text labeling an element, but where a sighted user gets that information through visual context. One concrete example is for landmark regions of the page: you might have a main navigation menu at the top of the page, and a secondary sidebar navigation for something like content categories. The position and visual style of each navigation region tells the user its importance, and adding short accessible names like &quot;main&quot; and &quot;categories&quot; communicates that same information programmatically. Some other examples of good use cases for hidden text, an <code>aria-label</code>, or other non-visual label include:</p>
<ul>
<li>Alt text for images</li>
<li>Naming an icon button</li>
<li>Naming a container widget like a dialog, feed, tabpanel, or list. If a heading or other text exists that visually labels the container, <code>aria-labelledby</code> is a better choice than <code>aria-label</code></li>
</ul>
<p>Practical accessibility doesn't always match ideal accessibility, and there will almost certainly be times when a design or content change just isn't happening, for whatever reason. In those cases, improving the accessible name is better than nothing. Just take care to include the visual label within the accessible name (ideally at the start), and try to write something that will not cause confusion when used together with the visual text.</p>
<h2 id="tooltips%2C-placeholders%2C-names%2C-and-duplicate-announcements">Tooltips, placeholders, names, and duplicate announcements</h2>
<p>A while back, I wrote a <s>rant</s> small article about some of the accessibility challenges of tooltips. One section dealt with whether the tooltip should form <a href="/writing/tooltips-in-wcag-21/#semantics">the name or the description</a> of the control it is attached to. Essentially, sometimes a tooltip will function visually as the accessible name (common with icon buttons) and sometimes it will provide additional descriptive text. The danger here is that using a <code>title</code> attribute to provide both a tooltip and an accessible description that is the same as the control's accessible name can create <a href="https://www.scottohara.me/testing/title-attr/results.html">duplicate announcements</a>. Sometimes. It depends.</p>
<p>The same inconsistent double announcements exist with a name and placeholder, although the specifics of which screen readers and browsers double up may differ. (Full disclosure: I only tested in a few screen readers to verify inconsistency, and testing results go out of date quickly in any case).</p>
<p>Both the <code>title</code> and <code>placeholder</code> attributes will be used as fallback sources for the accessible name, so using them that way can be a tempting way to resolve the duplicate announcement problem. However, relying on either <code>title</code> or <code>placeholder</code> as the label for a control causes many, many accessibility problems that go beyond simple programmatic support.</p>
<p>Seriously, don't do it:</p>
<ul>
<li><a href="https://www.24a11y.com/2017/the-trials-and-tribulations-of-the-title-attribute/">The Trials and Tribulations of the Title Attribute</a> by Scott O'Hara</li>
<li><a href="https://www.mediacurrent.com/blog/dont-rely-title-attribute-accessibility-seo/">Don't Rely on the Title Attribute for Accessibility</a> by the mediacurrent team</li>
<li><a href="https://developer.paciellogroup.com/blog/2013/01/using-the-html-title-attribute-updated/">Using the HTML title attribute – updated March 2020</a> by Steve Faulkner</li>
<li><a href="https://www.smashingmagazine.com/2018/06/placeholder-attribute/">Don’t Use The Placeholder Attribute</a> by Eric Bailey</li>
<li><a href="https://a11yproject.com/posts/placeholder-input-elements/">How-to: Use placeholder attributes</a> from the A11y Project</li>
<li><a href="https://codepen.io/stevef/post/placeholder-the-piss-take-label">placeholder - the piss-take label</a> by Steve Faulkner</li>
<li><a href="https://www.w3.org/WAI/GL/low-vision-a11y-tf/wiki/Placeholder_Research">Placeholder research</a> from the Web Accessibility Initiative's low vision accessibility task force</li>
<li><a href="https://www.nngroup.com/articles/form-design-placeholders/">Placeholders in Form Fields Are Harmful</a> by Kate Sherwin</li>
</ul>
<h2 id="further-reading">Further reading</h2>
<p>Thanks for reading this all the way to the end! I highly recommend also reading Adrian's naming article, and the AccName specification for particularly intrepid and detail-oriented developers.</p>
<ul>
<li><a href="https://adrianroselli.com/2020/01/my-priority-of-methods-for-labeling-a-control.html">Priority of methods for labeling a control</a> from Adrian Roselli</li>
<li><a href="https://w3c.github.io/accname/">Accessible name and description computation</a></li>
<li><a href="https://www.interactiveaccessibility.com/blog/whats-name">What's in a Name</a>, an article by the same name from Glen Walker</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Quick Tips for High Contrast Mode]]></title>
    <link href="https://sarahmhigley.com/writing/whcm-quick-tips/"/>
    <updated>2020-05-26T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/whcm-quick-tips/</id>
    <content type="html"><![CDATA[
      <p>Windows High Contrast Mode behavior can be a bit of a surprise if you haven't already spent time studying its ways. Unlike <a href="https://a11yproject.com/posts/operating-system-and-browser-accessibility-display-modes/">other operating system display modes</a> that invert colors or set a dark mode flag, Windows High Contrast Mode (WHCM) completely overrides authored colors with user-set colors.</p>
<p>A mode like invert colors will take author-set colors and transform them, which allows something like an authored background change on hover to still happen, albeit with different colors than originally defined. WHCM, in contrast, will completely ignore the authored background color. Colors on websites viewed in WHCM are determined solely based on HTML element, CSS property, and certain states (e.g. <code>disabled</code>). While authors still control things like whether a border exists and outline width, the colors themselves will be overridden (at least without a high contrast mode media query, covered below).</p>
<p>WHCM also cares not for your ARIA roles, states, or properties. Screen readers will read a <code>&lt;a role=&quot;button&quot;&gt;</code> as a button, but to WHCM it remains a link and receives link colors. Just as a <code>role=&quot;button&quot;</code> will not apply the <code>&lt;button&gt;</code> element's default browser styles, it will also not affect WHCM styles.</p>
<p>If the heavy-handed nature of WHCM color overrides makes bugs seem daunting or out of your control, you're not alone. However, there are often very simple solutions to most high contrast mode issues:</p>
<h2 id="1.-custom-focus-styles-%2B-a-transparent-outline">1. Custom focus styles + a transparent outline</h2>
<p>Windows High Contrast Mode wantonly ignores border or background color changes and box shadows. Both common of those common custom <code>:focus</code> styles will be invisible in WCHM. Luckily, if the default CSS outline property doesn't give you the visual effect you want for focus states, there's a very simple fix. Instead of overriding default browser focus styles with <code>outline: none</code>, make it transparent instead: <code>outline 3px solid transparent</code>. An example using the universal <code>*</code> selector might look like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">*:focus</span> <span class="token punctuation">{</span><br>  <span class="token property">background-color</span><span class="token punctuation">:</span> rebeccapurple<span class="token punctuation">;</span><br>  <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 4px 1px rebeccapurple<span class="token punctuation">;</span><br>  <span class="token property">outline</span><span class="token punctuation">:</span> 3px solid transparent<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p><img src="/writing/assets/button-focus-outline-whcm.png" alt="A side-by-side comparison of background and box-shadow styles out of high contrast mode, and an outline in HCM"></p>
<p>WHCM will override the transparent outline color, making it visible only when high contrast mode is turned on. This trick also works with transparent borders if, for example, your button has a distinct background color but no separately visible borders.</p>
<h2 id="2.-svgs-and-currentcolor">2. SVGs and currentColor</h2>
<p>The SVG <code>fill</code> and <code>stroke</code> properties are exceptions to the rule that WHCM overrides all colors. SVG colors will be left as-is, which is great if the SVG is being used as a logo or general image, but less useful if it's intended to be a meaningful icon. This is where the <a href="https://www.w3.org/TR/css-color-3/#currentcolor">CSS keyword <code>currentColor</code></a> comes in. <code>currentColor</code> is defined as the value for the text <code>color</code> property, which <em>will</em> be overridden by high contrast mode.</p>
<p>This is generally a great way to define SVG icon color if you want to make it match the surrounding text color, because then you don't need to define both <code>color</code> and <code>fill</code> separately, or separately update <code>fill</code> if the text color changes (e.g. on <code>:hover</code>). If you don't want the SVG to match the surrounding text, then you can define a unique <code>color</code> on the immediate parent of the SVG. Theoretically you could define <code>color</code> on the SVG itself, but this <a href="https://twitter.com/codingchaos/status/1225909446909415425?s=20">doesn't consistently get overridden</a> correctly, while <code>color</code> on a non-SVG parent element will.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* SVG icons within a link will be blue */</span><br><span class="token comment">/* SVGs not in a link will be black */</span><br><br><span class="token selector">body</span> <span class="token punctuation">{</span><br>  <span class="token property">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">a</span> <span class="token punctuation">{</span><br>  <span class="token property">color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">svg</span> <span class="token punctuation">{</span><br>  <span class="token property">fill</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<h2 id="3.-css-properties">3. CSS properties</h2>
<p>The specific CSS properties used to style visual cues can make a huge difference in WHCM. For example, using only <code>background-color</code> to distinguish alternating rows on a table with no borders will work outside of high contrast mode, but disappear entirely within it.</p>
<p>Another example that comes up frequently is custom styled form controls. Sometimes UI like a text input is re-styled to remove borders and rely on a contrasting background color, or radio button and checkbox indicators are created entirely with CSS. There's no reason not to do this, but relying on either background color or transitions between different colors to indicate state can make entire components impossible to use in high contrast mode.</p>
<figure>
  <img src="/writing/assets/radio-custom-whcm.png" alt="Two selected radio buttons shown side-by-side in and out of HCM. One of the radios selected indicator shows up in high contrast mode, and the other does not.">
  <figcaption>Two selected radio buttons, in and out of high contrast mode. The first radio button's indicator is styled with <code>background-color</code>, and the second uses <code>border</code>.</figcaption>
</figure>
<p>Adding in a change in border or outline, or using SVGs instead, will result in accessible UI in both modes without impacting the experience outside of high contrast mode.</p>
<h2 id="4.-media-queries">4. Media Queries</h2>
<p>There is a media query that has traditionally allowed developers to target Windows High Contrast Mode and override its color modifications. This is the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/-ms-high-contrast"><code>-ms-high-contrast</code> media query</a>, which supports targeting any use of high contrast mode, or specifically <code>black-on-white</code> or <code>white-on-black</code>. If using this, the best practice is to reference system color keywords rather than defining colors directly, so that the user's color choices will still be respected. This would let you do something like invert the foreground/background colors of a selected list item, or apply a visible box shadow on focus.</p>
<p>Rather than reproduce a full guide to using the <code>-ms-high-contrast</code> media query, I'd suggest reading <a href="https://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast/">Greg Whitworth's fantastic explainer</a>, which includes both explanation and examples.</p>
<p>However, this technique comes with an enormous caveat: the <a href="https://twitter.com/cssrossen/status/1217667508791959552?s=20"><code>-ms-high-contrast</code> media query will be retired</a> in favor of the standards-based <code>forced-colors</code> media query. Forced colors has the benefit of being CSS spec, with a cross-browser cross-OS set of <a href="https://drafts.csswg.org/css-color-4/#css-system-colors">standardized system color keywords</a>.</p>
<p>As of writing, we're in an odd in-between time when <code>-ms-high-contrast</code> is on its way out, and <code>forced-colors</code> is still experimental. For now, the best approach is likely to not rely on a media query at all.</p>
<h2 id="5.-browser-testing">5. Browser testing</h2>
<p>Edge, Internet Explorer, Firefox, and Chrome all support Windows High Contrast Mode, but not equally. Edge and Internet Explorer both support the <code>-ms-high-contrast</code> media query, and both will correctly map colors to user-set (or default) system colors. Firefox will adopt high contrast mode styles, but not fully respect system colors. Chrome does not adopt high contrast mode styles out of the box, but can be made to do so by going to  chrome://flags/ and setting &quot;Forced Colors: Enabled&quot;.</p>
<p>Based on media query support, correct system color mapping, and probable WHCM user base, Edge seems the best bet for Windows High Contrast Mode testing. As support shifts in the future, that will likely change to include other browsers as well.</p>
<h2 id="further-reading">Further Reading</h2>
<p>All the links below are especially good this week (not that they usually aren't 😄). I'd particularly recommend Melanie's talk; she's done a bunch of work around the standardization of <code>forced-colors</code>, and is largely responsible for me knowing much of anything about high contrast media queries.</p>
<ul>
<li><a href="https://a11yproject.com/posts/operating-system-and-browser-accessibility-display-modes/">Operating System and Browser Accessibility Display Modes</a> - The A11y Project</li>
<li><a href="https://adrianroselli.com/2017/11/os-high-contrast-versus-inverted-colors.html">A great all-round HCM explainer</a> - Adrian Roselli</li>
<li><a href="https://www.gwhitworth.com/blog/2017/04/how-to-use-ms-high-contrast/">How to use -ms-high-contrast</a> - Greg Whitworth</li>
<li><a href="https://www.w3.org/2019/09/Meetup/speaker-melanie.html">Finessing forced colors (talk)</a> - Melanie Richards</li>
<li><a href="https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/Accessibility/HighContrast/explainer.md">MS Edge High Contrast explainer</a></li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Grids Part 1: To grid or not to grid]]></title>
    <link href="https://sarahmhigley.com/writing/grids-part1/"/>
    <updated>2020-07-10T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/grids-part1/</id>
    <content type="html"><![CDATA[
      <p>In my day-to-day work, I find myself talking to a lot of people who have a lot of accessibility problems with different tables and grids. Grids are right up there with <a href="https://sarahmhigley.com/writing/select-your-poison/">combobox woes</a> and <a href="https://sarahmhigley.com/writing/tooltips-in-wcag-21/">tooltip mishaps</a> in causing an outsize amount of pain to developers who are trying to get accessibility right.</p>
<p>Part of this flood of grid accessibility questions might be caused by a quirk of where I happen to work, but I don't think that's the only reason tables and grids are over-represented in accessibility bugs. The semantics for tables and grids are relatively complex, and can unexpectedly break due to <a href="https://sarahmhigley.com/writing/roles-and-relationships/">extra <code>&lt;div&gt;</code>s</a> or CSS display values; grids can have some pretty complex keyboard interaction, and good accessibility documentation is scarce; the visual layout does not play well with mobile viewports or high levels of zoom; most out-of-the-box solutions for things like row selection, sorting, filtering, and virtualization break the screen reader experience. Actually, most out-of-the-box grid components out there have pretty poor accessibility across the board -- even the ones that claim otherwise. All of these combine to make it difficult to get tables and grids right.</p>
<p>So, partly for fun and partly in a futile attempt to reduce my daily email load, let's use a series of posts to take a deeper look at some of the ways tables and grids can go wrong:</p>
<ol>
<li>Using a grid when a table is needed, or vice versa</li>
<li>Semantics and keyboard navigation</li>
<li>Small viewports, zoom, and other visual accessibility issues</li>
<li>Sorting and filtering</li>
<li>Row selection</li>
<li>Virtualization (i.e. loading rows on demand in a very large grid or table)</li>
</ol>
<p>This first post is going to cover what should be the first question asked when creating a table or a grid, which is: <em>should</em> I be creating a table or grid?</p>
<h2 id="what-is-a-table%2C-really%3F">What is a table, really?</h2>
<figure>
  <img src="/writing/assets/modern-table-furniture.jpg" alt="That weird-looking modern table -- the furniture -- that everyone seems to love.">
  <figcaption>I want a version of the <a href="https://isthisasandwich.netlify.app">sandwich app</a>, but for tables.</figcaption>
</figure>
<p>The minimum requirement for both tables and grids is that they contain information whose structure is characterized by a two-dimensional relationship.</p>
<p>Since that's a bit of a mouthful, what I mean is that the information in each cell of a table gains additional context from its relationship to both a row and a column. For example, here are two different ways to present the same information, one of which delivers a two-dimensional relationship, and one does not:</p>
<p><strong>Example 1:</strong><br>
<img src="/writing/assets/event-simple-table.png" alt="A table of pandemic event listings including a concert, restaurant week, and hamilton the musical, all at home"></p>
<p><strong>Example 2:</strong><br>
<img src="/writing/assets/event-simple-cards.png" alt="The same events, formatted as a semantic list and styled as cards." style="width:500px"></p>
<p>The first example makes it easy for users to skim times and locations by organizing the information as tabular data, and the second example makes it easier to skim event titles by using a simpler list structure with headings. Both are valid choices, depending on what is deemed most important for a user in the context in which the events are displayed.</p>
<p>The important thing is that if a design decision is made to structure information as a table to surface column and row relationships, then it needs to be semantically marked up as a table to provide that same experience to screen reader users.</p>
<p>Here's another example of a good use of a table; in this case, the information would not be nearly as understandable in a list:</p>
<p><img src="/writing/assets/inventory-table-simple.png" alt="A table of book inventory data, including Between the World and Me, So you want to talk about race, The color of law, and How to be an antiracist."></p>
<p>I've actually used tables on this blog previously to display browser and screen reader support information in the <a href="https://sarahmhigley.com/writing/playing-with-state/#name-changes">Playing with State</a> article (this table has both column and row headers):</p>
<p><img src="/writing/assets/name-support-table.png" alt="A screenshot of the support table used in the linked Playing with State post"></p>
<p>So, in essence: a table is a method of displaying information to enable the easy consumption of two-dimensional relationships within that information.</p>
<h2 id="what-is-a-grid%3F">What is a Grid?</h2>
<p>All of the requirements around two-dimensional information structure that apply to a table also apply to a grid, but with the addition of interactivity. Visually, tables and grids can look the same or very similar, and both should support consuming information in the same way.</p>
<p>Grids differ from tables in how they support interactivity: all cells are focusable and keyboard navigable, and the general idea is that many if not all of the cells will support some type of user action.</p>
<p>Possible user actions within a grid could include editing a cell value (common in a spreadsheet), selecting or deselecting a cell, and reordering, inserting, and deleting rows or columns. If most of the cells are not interactive, then it seems likely that a table would be a better choice than a grid.</p>
<p>Grids should also be a single stop in the tab order regardless of how many interactive elements they contain, since keyboard navigation is handled by the author by listening to arrow keys, home, end, page up, page down, etc. For that reason, keyboard navigation within a grid can be much more efficient than tabbing between focusable elements within a table.</p>
<p>These semantic and interaction differences between a table pattern and a grid pattern are only really relevant to keyboard and screen reader users; there is no meaningful change in visuals or pointer interaction when switching from one to the other.</p>
<p>An editable spreadsheet is a very straightforward use case for a grid. Sort of like an Excel-lite, like this spreadsheet for entering expenses:</p>
<p><img src="/writing/assets/expense-grid-editable.png" alt="A grid with two rows of pre-pandemic travel expenses, one row of post-pandemic home office expenses, and one empty row with the cursor in the first cell"></p>
<p>The following example of a grid for managing virtual machines doesn't involve editing cell values, but does provide multiple actions per row:</p>
<p><img src="/writing/assets/vm-actions-grid.png" alt="A grid with two Linux VMs and one Windows VM, each with a start or stop button, an edit button, and a delete button."></p>
<p>There are even some pretty creative uses you can put to a grid, such as an online board game like bingo or chess. Cordelia McGee-Tubb explored this in her article about <a href="https://www.24a11y.com/2019/building-an-accessible-bingo-web-app/">building an accessible bingo web app</a>.</p>
<h2 id="what-is-not-a-grid">What is not a grid</h2>
<p>Back in ye olde days of the web, before CSS flexbox and grid, tables were sometimes used for layout since they had excellent cross-browser support and made complex layouts relatively easy to code. This littered the web (and email inboxes) with semantic <code>&lt;table&gt;</code>s that did not contain tabular data, and created some pretty long-lived headaches for browser and screen reader vendors.</p>
<p>Luckily, those days are behind us, and now no developers ever misuse semantic HTML elements or ARIA roles.</p>
<p><em>coughs</em>. What's that? That's not true? Ah well, guess I still need to finish this article and go to work tomorrow.</p>
<p>So what does misusing a grid look like in modern web development, now that layout tables are passé? Generally the not-grid grids I come across fall into two general categories:</p>
<ol>
<li>It's not all that interactive, and should probably be a table</li>
<li>It's highly interactive, but not tabular data. It should be something else entirely.</li>
</ol>
<p>Sometimes there seems to be the misconception that if a table has <em>any</em> interactivity, it must be a grid. Tables can be sortable, filterable, virtualized, and can contain links and buttons without needing to be a grid. We'll go a bit more into how to choose a table vs. a grid in the next section, but for now, remember: tables can be your friend too.</p>
<p>Highly interactive sets of items that do not have meaningful row and column relationships are also not grids. This is still true even if they are visually laid out in rows and columns, if those rows and columns do not provide meaning. A good example is this interface that allows users to pick from a list of apps: it is visually laid out in a grid pattern, but is not semantically a grid.</p>
<figure>
  <img src="/writing/assets/teams-apps-list.png" alt="A screenshot of the Apps tab in Microsoft Teams, with a list of available apps laid out visually in a grid pattern.">
  <figcaption>An excellent giveaway is that as you change the viewport width, the number of columns changes.</figcaption>
</figure>
<p>Then there's the unfortunate wording and example in the ARIA Authoring Practices about <a href="https://w3c.github.io/aria-practices/#grid">&quot;layout grids&quot;</a> that seems to support this misuse. I'm not going to go into it because Adrian Roselli has already written an article about it, provocatively called <a href="https://adrianroselli.com/2020/07/aria-grid-as-an-anti-pattern.html">ARIA Grid as an Anti-Pattern</a>. Suffice it to say that the rationale seems pretty similar to the layout tables of yore: it identifies a pattern that is difficult to implement with the tools we have today, and then decides the best solution is to pretend that pattern is actually a table (or grid) (spoilers: it is not).</p>
<p>OK, now that I've burned that bridge, let's move on to the fun part --</p>
<h2 id="when-do-you-use-a-grid-vs.-a-table%2C-and-why%3F">When do you use a grid vs. a table, and why?</h2>
<p>The examples above were all pretty straightforward: a data table with no interactive children should pretty clearly be a table, and an editable spreadsheet absolutely needs to be a grid. There's a fair amount of grey area in between, though. For example, before reading any further ask yourself whether you'd choose a table or a grid for each of these examples:</p>
<ol>
<li>A table of data primarily for reading, but each row has a few actions, e.g. view details / delete</li>
<li>A table that is primarily static content, but with multiple links per row, and over 50 rows</li>
<li>A table whose primary purpose is to select rows, but has otherwise static, read-only data</li>
</ol>
<p>It's a bit of an unfair question, since both table and grid are valid answers to each of those cases, depending on other factors. I like to split those factors into two categories: purpose and context.</p>
<h3 id="purpose">Purpose</h3>
<p>Earlier, I mentioned that the number of interactive cells might indicate whether a table should be a grid or vice versa. That's not wrong, but there is no magic threshold where because you determine that more than 50% of cells are interactive, it must be a grid. Instead, I like to think about whether the primary purpose of the UI control is:</p>
<p><strong>Reading:</strong><br>
If the primary purpose is for the user to consume and understand tabular information, then it leans towards being a table. This is true even if it contains many links or buttons.</p>
<p><strong>Interacting:</strong><br>
If the primary purpose is to provide an interface for the user to edit, manipulate, or otherwise interact with the content it presents, then it leans towards being a grid. Even if most of the cells are static content.</p>
<p>Using this metric, the first two theoretical examples above (the data table with view/delete buttons, and the large table with multiple links) would lean towards being implemented as tables, and the third example (where the user is expected to select rows of static data) would lean towards being a grid.</p>
<p>Basing table vs. grid roles on primary purpose is more than just being pedantic about semantics. If a control is primarily intended to enable a user action or set of actions, it's more likely that a user will spend most of their time moving between and interacting with controls within the grid. In that case, efficiency in moving keyboard focus has a high priority.</p>
<p>If the primary purpose is consuming information, then a user is less likely to need to shift keyboard focus frequently, and simplicity and discoverability matter more than keyboard efficiency.</p>
<h3 id="context">Context</h3>
<p>Table and grid controls don't exist in a vacuum (unless you get so fed up you chuck your computer into space). The surrounding UI, the type of interface you're building, and the ecosystem it lives in will all contribute to user expectations.</p>
<p>For example, if you're building an admin interface for managing a store, you may find your app contains many highly interactive grids (inventory, employee management, finances, etc.). Then imagine a single view within that app contains a grid that is perhaps less interactive -- let's imagine a table of user accounts, each with a single &quot;Edit&quot; button.</p>
<p>Even if that example seems like a good fit for a table role when considered on its own, in context there's a strong case for using a grid to preserve consistency and predictability across the entire app.</p>
<p>Surrounding context is still important if the table or grid in question is the only control of its type. If the context is more like an app than a website in that it is highly interactive and uses other <a href="https://w3c.github.io/aria/#managingfocus">composite widgets</a> like menus, tabs, and trees, then a grid is less likely to confuse a user. On the other hand, a lone grid on a typical restaurant website is much more likely to surprise and confuse people.</p>
<p>Use of a javascript framework like React or Angular, or client-side routing are not good indicators of app vs. website. Much like table vs. grid, web app vs. web site is all about whether the primary purpose is consuming static content, or enabling rich user interaction.</p>
<p>Another piece of context to examine is how long and how frequently your average user expects to interact with your interface. The more time an average user spends using a particular interface, the more they are likely to care about how quickly they can navigate it.</p>
<p>Grids have a steeper learning curve than tables and more support quirks across assistive tech, but they can also be significantly more efficient once learned (if built well). Table semantics have very robust support across screen readers and just about everyone knows how to mash the tab key, but a large table with many tab stops can really slow down keyboard-only navigation within it.</p>
<p>A web app for managing store data like the one imagined earlier would likely have users who visit daily, perhaps for years. Requiring users to spend some time learning how to use grids, trees, tabs, menus, and custom keyboard shortcuts is a reasonable choice in exchange for long-term efficiency. In an app like that, any table with a significant number of interactive elements could lean towards becoming a grid.</p>
<p>No one expects a rogue grid on your public-facing store website, though. Maybe make that a table, even if it has a lot of interactive children.</p>
<p>Also of note: just because Gmail, Excel Online, or an online tax software (to pick a random example that has nothing to do with my weekend plans) chooses to use a grid (or menu, tree, etc.), it does not mean the same choice makes sense elsewhere.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Tables and grids just want to be your friends, so long as you approach them for the right reasons.</p>
<p>Consider a table when:</p>
<ul>
<li>Displaying tabular data</li>
<li>The primary purpose is for the user to read that data</li>
<li>Discoverability and a low barrier to entry are more important than efficiency</li>
<li>There are not too many interactive children, or the context is primarily other static content</li>
</ul>
<p>Consider a grid when:</p>
<ul>
<li>Displaying tabular data</li>
<li>The primary purpose is to enable user interaction</li>
<li>Efficiency is more important than a low barrier to entry</li>
<li>There are many interactive children, and the context is primarily interactive and app-like</li>
</ul>
<p>And that's it on tables vs. grids! Next up is a more technical look at semantics and keyboard interaction within interactive grids.</p>
<h2 id="further-reading">Further reading</h2>
<ul>
<li><a href="https://w3c.github.io/aria-practices/#grid">Grid description in the APG (beware the &quot;layout grid&quot;)</a></li>
<li><a href="https://www.24a11y.com/2019/building-an-accessible-bingo-web-app/">Building an accessible bingo web app</a> - Cordelia McGee-Tubb</li>
<li><a href="https://adrianroselli.com/2017/11/hey-its-still-ok-to-use-tables.html">Hey, It’s Still OK to Use Tables</a> and <a href="https://adrianroselli.com/2020/07/aria-grid-as-an-anti-pattern.html">ARIA Grid As an Anti-Pattern</a> - Adrian Roselli</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Grids Part 2: Semantics]]></title>
    <link href="https://sarahmhigley.com/writing/grids-part2/"/>
    <updated>2021-01-07T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/grids-part2/</id>
    <content type="html"><![CDATA[
      <p>This is part 2 in a series about ARIA grids (not at all related to CSS grids 🙃)</p>
<p>In the <a href="/writing/grids-part1">first post of this grids series</a>, I wrote that the defining feature of grids and tables is that they present two-dimensional relationships in a set of data. Or, put another way, they are designed to make it easy to skim both row and column relationships.</p>
<p>Row and column relationships are handled visually by lining up each cell horizontally with other cells in the same row, and vertically with other cells in the same column.</p>
<p><img src="/writing/assets/grid-row-col-highlight.png" alt="Screenshot of a table showing marine mammals, with the row and column for the center cell visually highlighted. This table exists on the demo page, linked in the last section of the article."></p>
<p>Screen readers present the same relationships non-visually with a number of different UX features that can vary by screen reader. These generally include:</p>
<ul>
<li>providing custom shortcuts to navigate by column or by row</li>
<li>reading row- and column-header information when changing rows or columns</li>
<li>reading <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableRowElement/rowIndex">row index</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement#Properties">column index</a> information</li>
<li>providing a custom shortcut to read row- or column-headers on demand</li>
</ul>
<figure>
<div class="video-embed"><div style='position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;'><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/NpqwyocK0BU" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border:none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%;"></iframe></div></div>
<figcaption><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + arrow keys are used to move first across a row and then down a column with NVDA, making it easy to navigate and understand table structure.</figcaption>
</figure>
<aside class="note info">
  <span class="note-title">Note:</span>
<p>This behavior is something that screen readers provide as part of their own UX, and cannot be recreated manually by a web developer.</p>
</aside>
<p>Table and grid semantics boil down to creating a programmatic definition of row and column relationships. Screen readers are able to provide their table and grid experience <strong>only</strong> if the software can parse the row and column relationships that are conveyed visually.</p>
<p>Once more for emphasis:</p>
<p><em>Screen readers are able to provide their table and grid experience only if the software can parse the row and column relationships that are conveyed visually.</em></p>
<p>If a developer were to code a collection of plain <code>&lt;div&gt;</code>s to visually look like a table, the resulting non-visual experience would be a bit like if someone had Dali paint a grid, then gave that painting to you and told you that your job depended on you being able to read it.</p>
<figure>
<img src="/writing/assets/dali-melting-watch.jpg" alt="Dali's Melting Watch painting, showing a distorted watch with the numbers and metal pieces flying off.">
<figcaption>Here is the timetable for trains leaving today. Make sure you get to the correct platform before your train leaves.</figcaption></figure>
<h2 id="structural-table-and-grid-semantics">Structural table and grid semantics</h2>
<p>The foundation of table semantics is creating a relationship between cells in the same row, and between cells in the same column. This can only be done with good HTML structure.</p>
<p>In order for a visual row of cells to also behave like a row for screen readers, those cells <em>must</em> all be children of the same row. And in order for a visual column of cells to also behave like a column, they <em>must</em> all have the same child index within their parent row.</p>
<p>Sometimes legacy code or some outside requirement means that you end up working with a less-than-perfect DOM structure. Maybe you’re stuck using <code>&lt;div&gt;</code>s, and there happen to be too many of them. That particular problem -- too many extra elements -- can be fixed, but out-of-order cells or incorrect groupings can only be fixed by re-ordering the HTML.</p>
<p>ARIA can help prune extra non-table nodes from the accessibility tree and it can change what a screen reader says when it encounters a specific cell, but only the structure of the HTML itself can enable necessary screen reader interactions and shortcuts within a table.</p>
<h3 id="practical-example-1%3A-non-table-nodes">Practical Example 1: non-table nodes</h3>
<p>Here is some sample table code that includes both extra non-table elements and checkboxes outside of grid cells that illustrates what I’m talking about. This particular example uses ARIA semantics rather than <code>&lt;table&gt;</code> elements to demonstrate the effect of improper table structure. Don't worry too much about the specific roles right now; we'll cover those later.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Animals at the Seattle Aquarium<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select all rows<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Animal type<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Number<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select sea otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sea Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select river otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>River Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select harbor seal row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Harbor Seal<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>If you run NVDA on this table markup in Firefox, it chokes in two separate ways:</p>
<ol>
<li>on the checkbox that exists outside of a cell</li>
<li>on the extra <code>&lt;section&gt;</code> elements.</li>
</ol>
<p>See if you can catch both problems in this video, or by directly testing the <a href="/examples/grid-semantics">examples page</a>.</p>
<div class="video-embed"><div style='position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;'><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/Qmxo_pLFLb8" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border:none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%;"></iframe></div></div>
<p>Different browsers do different amounts of error correction for authoring errors in table markup, so trying this same markup in a different browser and screen reader pairing may get you different results. If a table works fine in one screen reader but fails in another, it might be a screen reader or browser bug, but it might also be a problem with the markup.</p>
<p>The best way to ensure every screen reader’s table UX works properly is to construct a clean accessibility tree. I wrote about using role=&quot;presentation&quot; or role=&quot;none&quot; to fix the accessibility tree of composite widgets like tables and grids in a previous blog post, <a href="/writing/roles-and-relationships">Roles and Relationships</a>.</p>
<p>Right now, the accessibility tree for that table code looks like this:</p>
<p><img src="/writing/assets/grids2-incorrect-table.png" alt="Screenshot of the Firefox accessibility tree for the table markup, showing extra section nodes around the second cell, and a checkbutton directly under each row."></p>
<p>If we apply <code>role=&quot;presentation&quot;</code> to all the <code>&lt;section&gt;</code> elements, we end up with this accessibility tree:</p>
<p><img src="/writing/assets/grids2-incorrect-table2.png" alt="Screenshot of the Firefox accessibility tree for the updated table markup, showing no section nodes. Still a checkbutton problem though."></p>
<p>There's still one problem: the checkboxes are still not reachable with table navigation, since they're not within a table cell. When creating tables and grids, <em>all</em> content must be contained within the cells. To fix that, we need to wrap the checkbox in an element with <code>role=&quot;cell&quot;</code>:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Animals at the Seattle Aquarium<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select all rows<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Animal type<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Number<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select sea otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>presentation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sea Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select river otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>presentation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>River Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select harbor seal row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>animal-cell<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>presentation<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Harbor Seal<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>And that's all that's needed to fix the table's accessibility tree so that screen readers can navigate through rows and columns, and properly associate cells with their column headers.</p>
<h3 id="aria-table-and-grid-roles">ARIA table and grid roles</h3>
<h4>Tables</h4>
<p>The previous example showed the role hierarchy needed to build a table with ARIA. There are roughly two reasons you might need to use ARIA roles to construct a semantic table:</p>
<ol>
<li>For whatever reason, you cannot use HTML <code>&lt;table&gt;</code> elements</li>
<li>You use HTML table elements, but you need to override the <a href="https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html">table's CSS display property</a>.</li>
</ol>
<p>In both cases, you would need to explicitly declare table semantics with ARIA for them to be recognized by screen readers.</p>
<p>The available ARIA roles for tables are these:</p>
<ul>
<li><code>table</code>: the top-level container role, equivalent to the <code>&lt;table&gt;</code> element
<ul>
<li><code>rowgroup</code> (optional): can denote a related group of rows, equivalent to the <code>&lt;thead&gt;</code> and <code>&lt;tbody&gt;</code> elements
<ul>
<li><code>row</code>: groups cells into rows, equivalent to the <code>&lt;tr&gt;</code> element
<ul>
<li><code>columnheader</code>: used to create a header cell that labels all body cells within the same column, equivalent to <code>&lt;th scope=&quot;col&quot;&gt;</code></li>
<li><code>rowheader</code>: used to create a header cell that labels all cells within the same row, equivalent to <code>&lt;th scope=&quot;row&quot;&gt;</code></li>
<li><code>cell</code>: all table content must be in one of these, equivalent to the <code>&lt;td&gt;</code> element</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>If you use semantic HTML table elements and do not override the CSS <code>display</code> property of any of them, then you do not need to use any ARIA roles.</p>
<h4>Grids</h4>
<p>Grid semantics differ slightly from table semantics, and always need to be explicitly declared. I went over why you might want grid semantics vs. table semantics in the <a href="/writing/grids-part1">first post in the grid series</a>. Assuming you do, in fact, need a grid, these are the available ARIA roles:</p>
<ul>
<li><code>grid</code>: the top-level container role
<ul>
<li><code>rowgroup</code> (optional): can denote a related group of rows, equivalent to the <code>&lt;thead&gt;</code> and <code>&lt;tbody&gt;</code> elements
<ul>
<li><code>row</code>: groups cells into rows, equivalent to the <code>&lt;tr&gt;</code> element
<ul>
<li><code>columnheader</code>: used to create a header cell that labels all body cells within the same column, equivalent to <code>&lt;th scope=&quot;col&quot;&gt;</code></li>
<li><code>rowheader</code>: used to create a header cell that labels all cells within the same row, equivalent to <code>&lt;th scope=&quot;row&quot;&gt;</code></li>
<li><code>gridcell</code>: all grid content must be in one of these, equivalent to a <code>&lt;td&gt;</code> within a grid</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>You may already have noticed that these are identical to the ARIA table roles, with the exception of <code>grid</code> and <code>gridcell</code>.</p>
<p>The easiest way to create a grid is to use HTML table elements, and add <code>role=&quot;grid&quot;</code> to the top-level <code>&lt;table&gt;</code>. No other roles are needed if the table is otherwise semantically correct and the CSS <code>display</code> properties are not overridden.</p>
<p>If that is not the case, all other roles must also be explicitly declared, just as with table semantics. Using <code>rowgroup</code> is optional for both tables and grids, and can be used to group header rows vs. body rows. It can also be used to group related rows within the table or grid body.</p>
<figure>
  <img src="/writing/assets/grids-grouped-rows.png" alt="Screenshot of table of aquarium animals, this time with two groups of rows under mammal and cephalopod categories">
  <figcaption>An example of when you might use named rowgroups within a table or grid body</figcaption>
</figure>
<h4>A quick note on CSS display properties</h4>
<p>Despite their names, the CSS <code>display: table</code> and <code>display: grid</code> styles are not related to ARIA <code>table</code> and <code>grid</code> semantics, and do not (or should not) create table and grid nodes in the accessibility tree.</p>
<p>I've also mentioned above that overriding the default CSS <code>display</code> of table elements (including <code>&lt;tr&gt;</code> and <code>&lt;td&gt;</code> elements) will remove their default semantics. Adrian Roselli's <a href="https://adrianroselli.com/2020/10/a11yto-conf-css-display-properties-versus-html-semantics.html">CSS display properties vs. HTML semantics</a> and Steve Faulkner's <a href="https://developer.paciellogroup.com/blog/2018/03/short-note-on-what-css-display-properties-do-to-table-semantics/">Short note on what CSS display properties do to table semantics</a> include more specifics on how that happens. The TL:DR; is if you do this, you need to use <em>all</em> the ARIA table or grid roles instead of relying on HTML to handle screen reader accessibility by default.</p>
<h2 id="supplementary-table-and-grid-semantics">Supplementary table and grid semantics</h2>
<h3 id="lazy-loading-rows-or-columns">Lazy-loading rows or columns</h3>
<p>There are four ARIA attributes available for both tables and grids that can be extremely useful if you are lazy-loading rows in a very large table or grid. For example, you could have a table that contains 500 rows total, but you only render 25 in the DOM at any given time to improve performance. If you do this without using ARIA, a screen reader will always announce the rendered rows as 1-25 out of 25, even if the user is really on rows 150-175 out of 500.</p>
<aside class="note info">
  <span class="note-title">Note:</span>
<p>Lazy-loaded grids can also be referred to as virtualized grids or on-demand grids. Those terms all mean the same thing: new content is only added to the DOM as the user scrolls.</p>
</aside>
<p>To fix this, you can use these two properties:</p>
<ul>
<li><code>aria-rowcount=&quot;500&quot;</code> on the <code>&lt;table&gt;</code> or <code>role=&quot;table/grid&quot;</code> element</li>
<li><code>aria-rowindex</code> on each <code>&lt;tr&gt;</code> or <code>role=&quot;row&quot;</code> element.</li>
</ul>
<p>If you lazy-load columns as well as rows, you can use these properties as well:</p>
<ul>
<li><code>aria-colcount</code> on the <code>&lt;table&gt;</code> or <code>role=&quot;table/grid&quot;</code> element</li>
<li><code>aria-colindex</code> on each <code>&lt;td&gt;</code> or <code>role=&quot;cell/gridcell&quot;</code> element</li>
</ul>
<p>None of these attributes are necessary if all the rows and columns are present in the DOM at all times. They also are not necessary if your grid has multiple pages, but does not dynamically load content by scrolling.</p>
<h3 id="practical-example-2%3A-out-of-order-rows">Practical example 2: out-of-order rows</h3>
<p>It is possible to use CSS to style elements to visually appear in a different order than they are defined in the DOM. You can brute-force this with <code>position: absolute;</code>, or use reordering options that come with <code>display: grid;</code> and <code>display: flex;</code>.</p>
<p>Screen readers do not follow the visual order, and will always rely on DOM order, which translates to the order in the accessibility tree. This holds true for tables and grids, despite the <code>aria-rowindex</code> and <code>aria-colindex</code> properties.</p>
<p>To illustrate, let's add some <code>aria-rowindex</code> and <code>aria-rowcount</code> properties to the table from earlier, and style so the visual order matches the declared <code>aria-rowindex</code> order. Here is the HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Animals at the Seattle Aquarium<span class="token punctuation">"</span></span> <span class="token attr-name">aria-rowcount</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span> <span class="token attr-name">aria-rowindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select all rows<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Animal type<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>columnheader<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Number<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span> <span class="token attr-name">aria-rowindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>7<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select sea otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>River Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span> <span class="token attr-name">aria-rowindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>6<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select river otter row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Sea Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span> <span class="token attr-name">aria-rowindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row-selection<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>select harbor seal row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Harbor Seal<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>cell<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>And here is how NVDA treats it:</p>
<figure>
<div class="video-embed"><div style='position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;'><iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/vgjopX55na4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="border:none; position: absolute; top: 0; left: 0; right: 0; bottom: 0; height: 100%; width: 100%;"></iframe></div></div>
<figcaption>NVDA will still move through rows following DOM order rather than visual order, but will read the defined row index, resulting in a disorienting experience.</figcaption></figure>
<p>The most important takeaway here is the <code>aria-rowcount</code>, <code>aria-rowindex</code>, <code>aria-colcount</code>, and <code>aria-colindex</code> properties will affect what a screen reader says when encountering a specific element, but will not change any other behavior or navigation. This is also generally true of most ARIA states and properties.</p>
<p>I realize this example may seem fairly contrived, but there is at least one major grid library out there that handles lazy-loading by re-using and visually re-ordering the DOM elements used for rows as the user scrolls through the grid. The result is the visual order does not match the DOM order, and the screen reader accessibility is entirely broken.</p>
<p>Accessible lazy-loaded content takes more than just these four ARIA properties, which is why there will be an entire article about on-demand grids later, hopefully!</p>
<h3 id="complex-headers">Complex headers</h3>
<p>Sometimes column headers and row headers span multiple columns or rows, such as in this table:</p>
<p><img src="/writing/assets/grids-multiheader.png" alt="Screenshot of a 4-column table with the same animal data. It has top-level headers of &quot;Animal&quot; and &quot;Tracking&quot;, and sub-headers of Species, Type, Number, and Location"></p>
<p>If you are using table elements, the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td#Attributes"><code>colspan</code> and <code>rowspan</code> attributes</a> have you covered. These work even if you are using ARIA to re-assert <code>role=&quot;columnheader&quot;</code> or <code>role=&quot;rowheader&quot;</code>.</p>
<p>If, however, you are using <code>&lt;div&gt;</code>s or other non-table elements, the <code>aria-colspan</code> and <code>aria-rowspan</code> correspond directly to the <code>colspan</code> and <code>rowspan</code> attributes.</p>
<h3 id="miscellaneous-other-table-and-grid-aria">Miscellaneous other table and grid ARIA</h3>
<p>We've covered all the table- and grid-specific ARIA attributes, but there are some others you might find useful, perhaps along with a <a href="https://benmyers.dev/blog/aria-labels-and-descriptions/">good overview of ARIA labels and descriptions</a>:</p>
<ul>
<li><code>aria-label</code> or <code>aria-labelledby</code> on the table or grid element: these corresond to using the <code>&lt;caption&gt;</code> element in an HTML <code>&lt;table&gt;</code>, and are a good idea to use to name the table or grid, particularly if there is more than one table or grid in the page.</li>
<li><code>aria-describedby</code> on the table or grid element: if you want to associate a longer supplementary description in addition to the name</li>
<li><code>aria-label</code> or <code>aria-labelledby</code> on the rowgroup element: if you have multiple rowgroups in the table body, adding a name to each is a good idea</li>
<li><code>aria-activedescendant</code> on the grid element (not on tables): we'll go over this in the next article on keyboard interaction</li>
</ul>
<p>There are other supported attributes that I'm not going to cover that are purely use-at-your-own risk, like <code>aria-selected</code> on gridcells. Only use those if you're really sure you know what you're doing.</p>
<p>If you've made a habit of reading the supported attributes table in the ARIA spec, you may have noticed that rows support the following attributes:</p>
<ul>
<li><code>aria-expanded</code></li>
<li><code>aria-level</code></li>
<li><code>aria-posinset</code></li>
<li><code>aria-setsize</code></li>
</ul>
<p>These are a bit of a gotcha &quot;supported&quot; status in the ARIA spec, since they're only supported on rows within treegrids, and are not supported in either grids or tables. For more on treegrids, see the next section.</p>
<h3 id="treegrids">Treegrids</h3>
<p>Not today, Satan.</p>
<h2 id="wrap-up-and-further-reading">Wrap-up and further reading</h2>
<p>Table and grid semantics are... not simple, clearly. The regular old been-around-the-block <code>&lt;table&gt;</code>, <code>&lt;tr&gt;</code>, and <code>&lt;td&gt;</code> elements do a lot of heavy lifting under the hood. There could easily be another article's worth of information on how all of this translates to accessibility APIs, if one wanted to really get in the weeds.</p>
<p>I used the NVDA screen reader for the recorded demos, but all Windows screen readers have very similar table interactions (they even all use Ctrl + Alt + arrow keys). VoiceOver on macOS uses different keyboard interactions, but also presents row and column information. You can try out the demos (plus a few other examples) with a screen reader of your choice on the <a href="/examples/grid-semantics">grid semantics demo page</a>.</p>
<p>This article and the previous one have focused about equally on both tables and grids. The next article on keyboard interaction will focus solely on grids, and hopefully be finished a bit faster than this article's 6-month time frame.</p>
<p>Finally, more eloquent people have already written about table semantics in different and helpful ways. Here are some of those articles:</p>
<ul>
<li><a href="https://inclusive-components.design/data-tables/">Data table design pattern</a> - Heydon Pickering</li>
<li><a href="https://developer.paciellogroup.com/blog/2014/10/notes-on-fixing-incorrect-table-structure-using-aria/">A practical example of fixing a broken table with ARIA</a> - Steve Faulkner</li>
<li><a href="https://adrianroselli.com/2018/02/tables-css-display-properties-and-aria.html">Tables, CSS Display Properties, and ARIA</a> - Adrian Roselli (he also has other worthwhile articles related to table semantics on his site)</li>
<li><a href="https://tink.uk/accessible-svg-tables/">A practical example of creating a semantic table in SVG</a> - Léonie Watson</li>
</ul>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[An oversimplified guide to setting up Mastodon]]></title>
    <link href="https://sarahmhigley.com/writing/mastodon/"/>
    <updated>2022-11-12T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/mastodon/</id>
    <content type="html"><![CDATA[
      <p>This is a guide for anyone who, like me, is casting about for alternatives to Twitter and would like an absolute minimum, bare-bones walkthrough for getting started with Mastodon. No evangelizing or attempted explanations of the &quot;fediverse&quot; will be involved.</p>
<blockquote>
<p><strong>Disclaimer:</strong> although I made an account years ago, I never used it until now. I'm not interested in convincing anyone to use Mastodon, or to leave Twitter. Do what works for you, in this hellscape of the 2020s.</p>
</blockquote>
<h2 id="getting-started">Getting started</h2>
<p>I will do my best to fully describe the steps with semantic accuracy for anyone using a screen reader, or anyone struggling with some of the contrast issues present on the site. If you run into any specific issues creating an account that are not covered here and want to share info, DM me.</p>
<h3 id="step-1%3A-pick-a-server">Step 1: Pick a server</h3>
<p>This can sound daunting, since there are so many options. Don't overthink it -- you can move your account later without losing followers. You can also follow anyone on any server, and search for people across all servers.</p>
<p>Here are four options, primarily geared towards folks in the web accessibility or disability communities:</p>
<ol>
<li>A big general server like <a href="https://mastodon.social/explore">mastodon.social</a> or <a href="https://mas.to/explore">mas.to</a>: these are large enough and old enough to be reliable. The drawback is they're so large that the server-specific &quot;Local&quot; feed isn't going to be helpful.</li>
<li><a href="https://toot.cafe/explore">toot.cafe</a>: a number of web and accessibility people are on here. Slightly smaller, and as of writing is not accepting new users (though that may easily change, it's a weird time right now).</li>
<li><a href="https://a11y.info/explore">a11y.info</a>: a very small server with almost exclusively web accessibility folks. Run by <a href="https://a11y.info/@spellacy">Michael Spellacy</a>.</li>
<li><a href="https://dragonscave.space/explore">dragonscave.space</a>: another very small server, run by a blind admin (<a href="https://dragonscave.space/@talon">Talon</a>). Lots of blind folks and accessibility experts on here. If you're looking for advice on using Mastodon with a screen reader, there are almost certainly people who can help on this instance.</li>
</ol>
<p>If you're not happy with those, you can browse a list of servers on <a href="https://joinmastodon.org/servers">joinmastodon.org</a> or, I dunno, ask someone else for recommendations.</p>
<h3 id="step-2%3A-sign-up">Step 2: Sign up</h3>
<p>If you're already using an app, you can probably sign up through there. If not, go to the <code>/about</code> path of the server you've picked, or use one of the following links for the servers I listed earlier:</p>
<ul>
<li><a href="https://mastodon.social/about">mastodon.social signup</a> (not accepting new users as of writing)</li>
<li><a href="https://mas.to/about">mas.to signup</a></li>
<li><a href="https://toot.cafe/about">toot.cafe signup</a> (not accepting new users as of writing)</li>
<li><a href="https://a11y.info/about">a11y.info signup</a></li>
<li><a href="https://dragonscave.space/about/">dragonscave.space signup</a></li>
</ul>
<p>When you do this on a server accepting new users, you will encounter one of a two things:</p>
<ol>
<li>A page with a heading for the name of the server, immediately followed by a form with four fields and a checkbox.</li>
</ol>
<p><img src="/writing/assets/mastodon-account-form.png" alt="The Dragon's Cave signup page linked above, with a heading of the server name, to the left immediately after the heading is the signup form described below; to the right is a login form, followed by a brief text description of the server."></p>
<p>The fields are:</p>
<ul>
<li>Username</li>
<li>Email address</li>
<li>Password</li>
<li>Confirm password</li>
<li>A checkbox to confirm that you agree to server rules and terms of service (the exact wording can vary by server)</li>
</ul>
<p>Some servers may have additional custom fields. There is a large button with the text like &quot;Sign up&quot; or &quot;Request an invite&quot; immediately following the checkbox.</p>
<blockquote>
<p>Warning for people using screen readers: when a server is not accepting new users, the fields in the sign up form are not disabled. Before you begin filling out the form, verify that the submit button has a label like &quot;sign up&quot; or request an invite&quot;, and not &quot;X server is not accepting new members&quot;.</p>
</blockquote>
<ol start="2">
<li>Some servers ask you to agree to terms and conditions before signing up. For these servers, you might not reach the form directly. Look for a &quot;Create account&quot; link. It is in different places on desktop vs. mobile:</li>
</ol>
<p><img src="/writing/assets/mastodon-create-account-btn.png" alt="Screenshot of the desktop version of the mas.to signup page linked above, with a presentational banner image of the mas.to logo above a heading of the server name. The Create Account button is in the left sidebar under the sign in button. It is effectively at the end of the page and not under any headings."></p>
<p><img src="/writing/assets/mastodon-create-account-mobile.png" alt="Screenshot of the mobile version of the mas.to signup page. The button is in the site header immediately after an image link to mastodon and the sign in button. It is the third interactive element on the page."></p>
<p>After agreeing to any server-specific terms, you should reach the same form described earlier.</p>
<h3 id="step-3%3A-email-confirmation">Step 3: Email confirmation</h3>
<p>After submitting the form, you should receive an email with a title like &quot;Mastodon: Confirmation instructions for [server name]&quot;.</p>
<p>Follow the &quot;Verify email address&quot; link, and you should get to your new profile.</p>
<h3 id="step-4%3A-you-have-an-account%2C-and-can-follow-people!">Step 4: You have an account, and can follow people!</h3>
<p>You can use the very low-contrast search field to find people across all of Mastodon (not just your server) It's at the top of the left sidebar on desktop, or under an magnifying glass icon button labelled &quot;Search&quot; on mobile.</p>
<p><img src="/writing/assets/mastodon-search.png" alt="Screenshot of the left sidebar on the web client at a desktop width. The input has a label of Search, and is the first form field on the page. Below it are links for your profile and the textarea to create a new post, with the label, What's on your mind"></p>
<p>Here's a list of web a11y people I think are worth following to start out (not comprehensive, I haven't been here long myself and I'll probably keep adding to it, if I remember). Eric Eggert has a longer and better-curated list in his very good <a href="https://yatil.net/blog/accessibility-in-the-fediverse-and-mastodon">article on accessibility in the fediverse</a>.</p>
<p>In no particular order:</p>
<ul>
<li>Me! I'm here :) <a href="https://vis.social/@codingchaos">@codingchaos@vis.social</a></li>
<li>Frank Elavsky <a href="https://vis.social/web/@frankelavsky">@frankelavsky@vis.social</a></li>
<li>Léonie Watson <a href="https://front-end.social/@tink">@tink@front-end.social</a></li>
<li>Scott O'Hara <a href="https://mastodon.social/@scottohara">@scottohara@mastodon.social</a></li>
<li>Adrian Roselli <a href="https://vis.social/web/@aardrian@toot.cafe">@aardrian@toot.cafe</a></li>
<li>Eric Bailey <a href="https://front-end.social/@ericwbailey">@ericwbailey@front-end.social</a></li>
<li>Sam Kapila <a href="https://vis.social/web/@samkap@front-end.social">@samkap@front-end.social</a></li>
<li>Sara Soueidan <a href="https://front-end.social/@SaraSoueidan">@SaraSoueidan@front-end.social</a></li>
<li>Alice Boxhall <a href="https://mastodon.social/@sundress">@sundress@mastodon.social</a></li>
<li>Eric Wright <a href="https://mastodon.social/@ewaccess">@ewaccess@mastodon.social</a></li>
<li>Eric Eggert <a href="https://micro.yatil.net/activitypub/yatil">@yatil@micro.yatil.net</a></li>
<li>Chancey Fleet <a href="https://mas.to/@ChanceyFleet">@ChanceyFleet@mas.to</a></li>
<li>Doug Schepers <a href="https://mastodon.social/@shepazu">@shepazu@mastodon.social</a></li>
<li>Crystal Preston-Watson <a href="https://mstdn.social/@ScopicEngineer">@ScopicEngineer@mstdn.social</a></li>
</ul>
<h3 id="step-5-(optional)%3A-find-an-app">Step 5 (optional): Find an app</h3>
<p>There is an official Mastodon app available for Apple and Android, with links to both in the <a href="https://joinmastodon.org/apps">JoinMastodon apps page</a>. There is also a list of alternative apps further down the same page, under the &quot;Browse third-party apps&quot; header.</p>
<p>I have no personal recommendations, but have anecdotally heard good things about Toot! (paid), and that some blind folks have been able to use <a href="https://tweesecake.app/">Tweesecake</a> on Windows and macOS.</p>
<p>If I find out more info, especially on clients with good contrast, distraction-related settings, and animation prevention, I'll update here.</p>
<h2 id="coda%3A-some-resources">Coda: some resources</h2>
<ul>
<li><a href="https://fedi.tips/">Fedi.tips</a>: an unofficial and non-technical guide to using Mastodon. Has some really good tips, and some basic accessibility info.</li>
<li><a href="https://yatil.net/blog/accessibility-in-the-fediverse-and-mastodon">Eric Eggert's Mastodon a11y post</a>: includes a long list of web accessibility people to follow</li>
<li><a href="https://www.starshipchangeling.net/mastodon/">A screen reader guide to Mastodon</a>: note -- this may have some info that is out of date, at least regarding the web interface.</li>
<li><a href="https://midrange.tedium.co/issues/how-mastodon-search-works/">How Mastodon search works</a> -- also goes into why it works the way it does, despite seeming unclear when coming from Twitter</li>
<li>There are some good tips in <a href="https://twitter.com/LFLegal/status/1586876400262414339?s=20&amp;t=sRhxsBY66HV7a9NrvQlk8Q">this twitter thread</a> from Lainey Feingold, and the <a href="https://twitter.com/HabenGirma/status/1586829640827404288">original tweet from Haben Girma</a></li>
</ul>
<hr>
<p>That's it! I'll probably keep my account on twitter till the bitter end, but I hope the wonderful people who have stitched together a community on the bluebird hellsite will still have a place to gather in the coming years, on Mastodon or elsewhere.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[Aria-activedescendant is not focus]]></title>
    <link href="https://sarahmhigley.com/writing/activedescendant/"/>
    <updated>2024-10-16T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/activedescendant/</id>
    <content type="html"><![CDATA[
      <p>For anyone who has not found themselves wading waist-deep through the nigh-unintelligible morass of accessible <a href="https://www.w3.org/WAI/ARIA/apg/patterns/combobox/">combobox patterns</a>, <code>aria-activedescendant</code> might seem like yet another esoteric line item in the ARIA spec, or might be wholly unfamiliar. For those unfortunate souls who <em>have</em> peered into the vortex of combobox accessibility long enough for their eyes to bleed and their ears to echo with the distant chanting of 1,000 cursèd <code>&lt;div&gt;</code>s, <code>aria-activedescendant</code> can feel a little magical.</p>
<p>This is an attribute that is entirely concerned with screen reader accessibility. Specifically, it allows certain scoped exceptions to the screen reader behavior of only enabling interaction with a single element at a time.</p>
<p>Even more specifically, you probably want to start reaching for <code>aria-activedescendant</code> whenever you want to provide a user with immediately relevant actions, autocomplete suggestions, or matching options while they are typing text.</p>
<p>For example: if the user's keyboard focus is on a text input, their screen reader will normally only pass along information and updates about that one text input. Any changes happening elsewhere on the page will not be communicated, with two notable exceptions:</p>
<ol>
<li>Live regions. I'm not going to elaborate on these here, but I have <a href="https://www.youtube.com/watch?v=W5YAaLLBKhQ&amp;t=1497s">talked about them before</a>.</li>
<li>If the focused element has an <code>aria-activedescendant</code> attribute: changes to the element referenced by that attribute, or changing which element is referenced by <code>aria-activedescendant</code>.</li>
</ol>
<p>Diving more into the text input example, <code>aria-activedescendant</code> makes it possible to have screen readers expose both changes to the text input (e.g. the value, valid/invalid state, expand/collapse state, etc.) and also changes to one other associated element, usually in a popup. In other words, it is the primary mechanism that makes comboboxes work:</p>
<p><img src="/writing/assets/combobox-focus-activedescendant.png" alt="image of an open combobox labeled best pet, with screaming hairy armadillo selected. The image is marked up: the input is highlighted and labeled as the focused element, and the screaming hairy armadillo option inside the open listbox popup is highlighted and labelled activedescendant"></p>
<p>Sometimes this causes people to think of the active descendant as a second focus, or talk about the associated <code>aria-activedescendant</code> element as &quot;focused&quot;.</p>
<p><img src="/writing/assets/second-focus.jpg" alt="Merry and Pippin ask you: we've had one, yes, but what about second focus?"></p>
<p>While keyboard focus and <code>aria-activedescendant</code> are quite similar -- maybe even more similar than you might think -- they are still distinct in a number of ways that are important for web developers to be able to identify.</p>
<h2 id="what-is-focus%3F">What is focus?</h2>
<p>This is the easy one, and most developers are probably already familiar with it. A document may have exactly one focused element at a time, and that element can be queried using the <code>document.activeElement</code> DOM attribute.</p>
<p>The focused element of the active document will be the target of any keyboard events, which makes it particularly important when creating custom keyboard interactions.</p>
<p>A screen reader cursor is <a href="https://tink.uk/the-difference-between-keyboard-and-screen-reader-navigation/">not the same as keyboard focus</a>, although screen readers will also usually have their cursor follow any changes in the focused element. Those changes can occur either through user interaction (e.g. tabbing) or programmatic focus changes (e.g. calling <code>element.focus()</code> to move focus back to the triggering button when closing a dialog). However, certain screen readers will sometimes ignore programmatic focus changes in favor of their own internal heuristics about where the screen reader cursor should go (side-eye at VoiceOver intensifies).</p>
<p>Identifying the currently focused element can get a little more tricky in the case of multiple documents in nested iframes, or custom web components with shadow roots.</p>
<p>For example, if focus enters a button within an iframe, querying the top-level document's <code>activeElement</code> will return the <code>iframe</code> and not the focused element within it. Similarly, if focus is within the shadow root of a web component, <code>document.activeElement</code> will return the web component's host node. You would then need to query the iframe's <code>document.activeElement</code>, or host node's <code>shadowRoot.activeElement</code> to find the specific element that has focus.</p>
<p>There are of course other nuances to tracking focus changes that also have the potential to cause premature hair loss in developers:</p>
<ul>
<li>If <code>document.activeElement</code> is queried in a blur event handler, it will return <code>document.body</code>.</li>
<li>Pressing the tab key does not always move focus starting from <code>document.activeElement</code>. There are times when the <a href="https://sarahmhigley.com/writing/focus-navigation-start-point/">sequential focus navigation starting point</a> diverges from the currently focused element.</li>
<li>When navigating the web using a keyboard paired with an iOS device, the web page receives focus events, but <em>no keyboard or pointer events</em>. Someday I will write 5000 words about the focus-handling implications for this and it will never be published because of all the swearing.</li>
<li>Some other weird edge case that I either can't remember or blissfully have yet to encounter.</li>
</ul>
<p>As with many things, focus is simple right up until it isn't.</p>
<h2 id="what-is-an-active-descendant%3F">What is an active descendant?</h2>
<p>It's the element identified by <code>aria-activedescendant</code>, of course.</p>
<p><img src="/writing/assets/nailed-it.jpg" alt="A very young child holds a camera backwards, staring confidently into the lens instead of the viewfinder. The caption says Nailed It."></p>
<p>There's actually the smallest hint of something useful in that tautology, which is that an active descendant on the web only exists with the help of ARIA; there is no way to create one using only HTML or Javascript (apart from literally using JS to set <code>element.ariaActiveDescendant</code>).</p>
<aside class="note info">
  <span class="note-title">Note:</span>
<p>OK fine, <a href="https://adrianroselli.com/2023/06/under-engineered-comboboxen.html"><code>&lt;datalist&gt;</code></a> technically also has an active descendant, but it is a cursed control of which we do not speak. I only include it because leaving out known but irrelevant edge cases is physically painful. This is a personal failing.</p>
</aside>
<p>This means that we can look at the <a href="https://w3c.github.io/aria/#aria-activedescendant">ARIA spec</a> to tell us the allowed uses and limitations of <code>aria-activedescendant</code> -- what roles it can be used in, authoring requirements, and other limitations.</p>
<p>Distilling the information in the spec, there are two patterns that support <code>aria-activedescendant</code>:</p>
<ol>
<li>Patterns similar to a combobox, where focus remains on an editable region to enable typing while <code>aria-activedescendant</code> points to a related item, often an option in a popup listbox:</li>
</ol>
<p><img src="/writing/assets/chat-combobox.png" alt="screenshot of the Slack chat input with the text Help I'm writing about comboboxes again @, with a popup showing two matching people, Eric Bailey and Adrian Roselli, triggered by the at-symbol."></p>
<ol start="2">
<li><a href="https://sarahmhigley.com/writing/roles-and-relationships#basic-vs.-composite-patterns">Composite widgets</a> like <code>tree</code> or <code>grid</code> can accept focus on their parent node, and have <code>aria-activedescendant</code> point to child <code>treeitem</code> or <code>gridcell</code> elements instead of managing focus between those child elements directly.</li>
</ol>
<p><img src="/writing/assets/tree-with-activedescendant.png" alt="Screenshot of a generic tree with placeholder item labels like level 2, item 1. The tree has a thick black focus outline around the entire thing, and one tree item is highlighted with a dark blue background and white text."></p>
<p>I still have no idea why someone would choose to use <code>aria-activedescendant</code> for #2 in practice, but the important thing is that you <em>can</em>, I guess.</p>
<h2 id="how-to-use-aria-activedescendant">How to use aria-activedescendant</h2>
<p>The value of <code>aria-activedescendant</code> must point to the id of an element that is either:</p>
<ol>
<li>a descendant of the composite widget that has <code>aria-activedescendant</code>:</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listbox<span class="token punctuation">"</span></span> <span class="token attr-name">tabindex</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token attr-name">aria-activedescendant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token comment">&lt;!-- this is a descendant of the listbox, so it is OK --></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Opossum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ocelot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<ol start="2">
<li>a descendant of a composite widget that is ponted to via <code>aria-controls</code> on a <code>combobox</code>, <code>textbox</code>, or <code>searchbox</code> with <code>aria-activedescendant</code>:</li>
</ol>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">aria-controls</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listbox<span class="token punctuation">"</span></span> <span class="token attr-name">aria-activedescendant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listbox<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>listbox<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Otter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token comment">&lt;!-- this is a descendant of the listbox, which is pointed to by aria-controls, so it is OK --></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Opossum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br>  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>option<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>opt3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ocelot<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>The value of <code>aria-activedescendant</code> should not point to some random element on the page, even when it is defined on a role that supports it:</p>
<pre class="language-html"><code class="language-html"><span class="token comment">&lt;!-- DON'T DO THIS --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">aria-activedescendant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token comment">&lt;!-- this is a random button, what is even going on here --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>nope<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre>
<h2 id="how-does-aria-activedescendant-work%3F">How does aria-activedescendant work?</h2>
<p>Knowing that <code>aria-activedescendant</code> is an ARIA construct is helpful in one additional way: we can look at the <a href="https://w3c.github.io/core-aam/#ariaActiveDescendant">Core AAM spec</a> (the accessibility API mapping spec for ARIA) to determine what is going on under the hood.</p>
<p>Since <code>aria-activedescendant</code> exists for the benefit of screen reader users, and accessibility APIs mediate most of the DOM-to-screen reader translation, looking at the API mapping can tell us a lot about the intended functionality of <code>aria-activedescendant</code>.</p>
<p>One small side note -- while <code>aria-activedescendant</code> is specifically for screen reader users, this is not true of all ARIA attributes, and also not true for all accessibility API mappings.</p>
<p>The mappings:</p>
<div class="table-wrapper">
  <table class="table">
    <tbody>
      <tr>
        <th><abbr title="Microsoft Active Accessibility">MSAA</abbr> + IAccessible2 </th>
        <td>
          See <a href="https://w3c.github.io/core-aam/#focus_state_event_table">Focus Changes</a>.
        </td>
      </tr>
      <tr>
        <th><abbr title="User Interface Automation">UIA</abbr></th>
        <td>
          See <a href="https://w3c.github.io/core-aam/#focus_state_event_table">Focus Changes</a>.
        </td>
      </tr>
      <tr>
        <th><abbr title="Accessibility Toolkit">ATK</abbr>/<abbr title="Assistive Technology-Service Provider Interface">AT-SPI</abbr></th>
        <td>
          See <a href="https://w3c.github.io/core-aam/#focus_state_event_table">Focus Changes</a>.
        </td>
      </tr>
      <tr>
        <th><abbr title="macOS Accessibility Protocol">AX API</abbr></th>
        <td>
          See <a href="https://w3c.github.io/core-aam/#focus_state_event_table">Focus Changes</a>.<br>
          <span class="property">Property: <code>AXSelectedRows</code>: pointer to active descendant node</span>
        </td>
      </tr>
    </tbody>
  </table>
</div>
<p>...</p>
<p>So, uh, about that second focus thing...</p>
<figure>
  <img src="/writing/assets/second-focus-hits.jpg" alt="A two-frame still of Pippin getting hit in the head with an apple after asking for second breakfast">
  <figcaption>Much like Pippin, those expecting a true second focus will still be disappointed.</figcaption>
</figure>
<h2 id="why-use-focus-vs.-aria-activedescendant">Why use focus vs. aria-activedescendant</h2>
<p>The thing about ARIA is that it does not affect browser behavior or functionality -- only semantics and accessibility API mappings. All keyboard events will still fire on the true focused element, and there are no global DOM methods to query the currently relevant active descendant in the manner of <code>document.activeElement</code>. The only context in which the active descendant behaves like a second focus is when it comes to a screen reader's virtual cursor.</p>
<p>Even for screen readers, <code>aria-activedescendant</code> is not entirely the same as a second focus. As an example, let's look at what happens if a Windows screen reader users toggles between <a href="https://tink.uk/understanding-screen-reader-interaction-modes/">browse mode and forms mode</a> after navigating through elements using <code>aria-activedescendant</code> vs. focus:</p>
<ul>
<li>With factory settings, NVDA, Narrator, and JAWS all update keyboard focus to follow their virtual cursor when moving over focusable elements. This means that switching between browse mode and forms mode will usually keep you in the same place.</li>
<li>Using NVDA or Narrator, the element identified through <code>aria-activedescendant</code> will not update to match the screen reader's cursor location. Switching between browse mode and forms mode may cause unintended side effects.</li>
<li>JAWS is the one Windows screen reader to handle mode switching with <code>aria-activedescendant</code> gracefully.</li>
</ul>
<p>Syncing the screen reader cursor and page focus is a little more complicated than even this list would imply, but in ways that are not directly relevant here.</p>
<h3 id="automatic-mode-switching">Automatic mode switching</h3>
<p>One way in which <code>aria-activedescendant</code> is similar to focus is that the <code>role</code> of an active descendant will cause Windows screen readers to switch modes in exactly the same way that the <code>role</code> of a focused element does. For example, if you are using NVDA and are interacting with an input in forms mode, tabbing to a button will switch you to browse mode (unless you've disabled this behavior in user settings). By the same mechanism, if we bring back this cursed code:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">aria-activedescendant</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>nope<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>Updating <code>aria-activedescendant</code> to point to that button while the input is in focus will also switch the user to browse mode. Subsequent keyboard interactions will thereafter be handled by NVDA instead of by the page, at which point the script updating <code>aria-activedescendant</code> in response to keyboard events will stop doing anything. The user will also no longer be able to type without manually switching back to forms mode. This is the primary reason to avoid having <code>aria-activedescendant</code> point to elements that are not part of a composite widget like options in a listbox.</p>
<figure>
  <img src="/writing/assets/combobox-load-more.png" alt="A screenshot of an open combobox with two options, group one and group two. Below the two options in the popup is a highlighted option with the text Load all results.">
  <figcaption>If your combobox's popup listbox has actions in addition to selectable options, you should still mark up the actions with <code>role="option"</code> to prevent unhelpful mode switching.</figcaption>
</figure>
<p>Mobile screen readers will essentially ignore <code>aria-activedescendant</code>, since they move their cursor through the accessibility tree in response to touch input (simplifying a little here; mobile screen readers also support other types of input). This means that an iOS VoiceOver or Android Talkback user will generally swipe through all the options without the <code>aria-activedescendant</code> value changing at all. In some cases, this might be an issue for scrolling if items are also not focusable. If items are focusable, they will generally gain focus when the VoiceOver or Talkback cursor lands on them, natively triggering scroll when needed.</p>
<p>Also VoiceOver on Safari on macOS will ignore <code>aria-activedescendant</code> if you so much as look at it funny.</p>
<h2 id="when-to-use-aria-activedescendant">When to use aria-activedescendant</h2>
<p>Comboboxes.</p>
<p><img src="/writing/assets/nailed-it.jpg" alt="Once again: a very young child holds a camera backwards, staring confidently into the lens instead of the viewfinder. The caption says Nailed It."></p>
<p>OK but really -- I have not found a real-world use case for <code>aria-activedescendant</code> that does not also involve editing text.</p>
<p>Even in complex use cases of giant virtualized trees, data tables, tree grids, and SVG charts and graphs, managing keyboard focus ends up being simpler to implement and more robust. If anyone has a good example for needing <code>aria-activedescendant</code> outside of a combobox/editing scenario, I'd love to hear about it. I'm sure it exists in theory out there somewhere, presumably right next to a perfectly accessible combobox full of tooltips.</p>
<p>There are a few text editing use cases for <code>aria-activedescendant</code> beyond just comboboxes, however. Some examples include:</p>
<ul>
<li>Freeform search inputs that expand a popup of suggestions. I'd generally recommend against putting a <code>role=combobox</code> on a site-wide search for two reasons: comboboxes generally do not imply freeform input, and using that role might make the searchbox harder to find for screen reader users explicitly looking for edit boxes.</li>
<li>@-mentions inside a chat input. This usually involves a floating listbox or menu anchored to your text insertion cursor. Focus remains in the chat input to enable continued typing, and <code>aria-activedescendant</code> makes the popup accessible. The entire pattern is very similar to a combobox, but without the combobox role.</li>
<li>Any floating suggestions in a document canvas editing experience (not sure what to call this exactly). Think Google Docs, Word Online, website creation canvases, etc. You might have something like a spelling or grammar suggestions popup appear without pulling the user's focus away from where they currently are in the document.</li>
</ul>
<p>Generally the theme is to consider <code>aria-activedescendant</code> in any use case where you need to present some immediately relevant supplemental actions related to user input.</p>
<h2 id="where-to-use-aria-activedescendant">Where to use aria-activedescendant</h2>
<p>The <code>aria-activedescendant</code> attribute must be defined directly on the element that has keyboard focus for the active descendant to be picked up by screen readers.</p>
<p>I know this was already covered in an earlier section, but I wasn't sure how else to shoehorn in a section on &quot;Where&quot;.</p>
<h2 id="who-is-aria-activedescendant">Who is aria-activedescendant</h2>
<p>Truly we are all <code>aria-activedescendant</code>, at least in our hearts.</p>
<p>I'm not sure where I was going with this. I think this article might be losing focus.</p>
<hr>
<p>Many thanks and apologies to <a href="https://ericwbailey.design/">Eric Bailey</a> and <a href="https://adrianroselli.com/">Adrian Roselli</a> for being kind enough to review this article ages ago when I first wrote it.</p>

    ]]></content>
  </entry>
	
  
  <entry>
    <title><![CDATA[forced-color-adjust: none is an unavoidable foot gun]]></title>
    <link href="https://sarahmhigley.com/writing/forced-color-adjust-none/"/>
    <updated>2025-09-17T00:00:00Z</updated>
    <id>https://sarahmhigley.com/writing/forced-color-adjust-none/</id>
    <content type="html"><![CDATA[
      <p>Over the past seven (?!) years at Microsoft, high contrast styles have been a surprisingly consistent source of accessibility issues from partner teams. The surprise is because other common sources of bugs that bubble up to me tend to be either technically complex (e.g. live regions, focus management, assistive tech behavior), or theoretically complex (understanding semantics, assistive tech behavior). In contrast (heh), high contrast styles tend to be relatively simple and more easily understood by non-accessibility-focused devs since they involve relatively straightforward CSS.</p>
<p>There are two exceptions that cause non-trivial issues to arise:</p>
<ol>
<li>SVGs (written about more in <a href="https://css-tricks.com/accessible-svgs-high-contrast-mode/">Eric Bailey's article</a>)</li>
<li>Sprinkling <code>forced-color-adjust: none</code> throughout a codebase like cloves in biryani. They may sometimes be helpful, but stumbling across one unexpectedly can make you gag.</li>
</ol>
<p>This post is aimed at anyone working in an environment with multiple other developers or development teams while needing to maintain a codebase over time. If: you lovingly handcraft all your styles, and everyone who contributes is familiar with all CSS in the codebase, <em>and</em> this stays true both now and in perpetuity, then this probably won't be a problem.</p>
<p>However if you, like me, work in a large and chaotic system where code you write is copied, consumed, customized, and maintained away from your control by many other people, then it is a matter of <em>when</em> and not <em>if</em> a <code>forced-color-adjust: none</code> style will cause high contrast mode bugs.</p>
<h2 id="the-problem">The problem</h2>
<p>By default, forced color modes work by converting UI from author-defined colors into a reduced palette of system colors based on HTML semantics, not authored CSS. Static text will adopt a <code>CanvasText</code> color against a <code>Canvas</code> background. Buttons will use <code>ButtonText</code> on <code>ButtonFace</code>, and disabled controls will have content and borders appear as <code>GrayText</code>.</p>
<figure>
  <img src="/writing/assets/hcm-colors.png" alt="Two side-by-side screenshots of the same content, in and out of high contrast mode. The content is a heading, paragraph text, a button, a link, and a disabled text input. THe high contrast screenshot shows a dark blueish background, light beige text, a teal button, blue link, and grey input.">
  <figcaption>Some basic content in and out of Windows high contrast mode, with no CSS overrides. The right screenshot shows a customized high contrast theme.</figcaption>
</figure>
<p>Even though this is how it works <em>by default</em>, it is still possible to override the default styles and customize forced color styles for both good and evil. There are a few ways to go about this:</p>
<ol>
<li>
<p>Intentionally create transparent borders or outlines, knowing they'll only show up in forced colors mode. (This and some other tips are covered in my <a href="/writing/whcm-quick-tips/">Quick Tips for High Contrast Mode</a> article)</p>
</li>
<li>
<p>Using a forced colors mode media query, tweak styles using system color keywords:</p>
</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br>  <span class="token selector">.primary-button</span> <span class="token punctuation">{</span><br>    <span class="token property">border-color</span><span class="token punctuation">:</span> Highlight<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
<p>(note this only works with system color keywords, other colors will not be rendered without <code>forced-color-adjust: none</code>)</p>
<ol start="3">
<li>Use a forced colors mode media query with <code>forced-color-adjust: none</code> to completely control colors, ideally still using system colors:</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br>  <span class="token selector">button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>    <span class="token property">forced-color-adjust</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br>    <span class="token property">background-color</span><span class="token punctuation">:</span> Highlight<span class="token punctuation">;</span><br>    <span class="token property">color</span><span class="token punctuation">:</span> HighlightText<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
<p>So the question you, a savvy reader, may already be asking is: if system color keywords work without needing <code>forced-color-adjust: none</code>, then why include it in that third example?</p>
<p>The reason is because of an unspec'd (unspecced? unspec'èd?) browser behavior: text backplates. In what appears to be a well-intended attempt to save authors from themselves, browsers render a solid-colored backplate to any text over a styled background (this happens with backgrounds on the same element or on ancestor elements). The color of this backplate is <code>Canvas</code>, regardless of the text color.</p>
<p>So if you want to have, for example, a toggle button that inverts colors when pressed, you might start with this CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br>  <span class="token selector">button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>    <span class="token property">background-color</span><span class="token punctuation">:</span> Highlight<span class="token punctuation">;</span><br>    <span class="token property">color</span><span class="token punctuation">:</span> HighlightText<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
<p>And you would end up with this:</p>
<p><img src="/writing/assets/hcm-text-backplate.png" alt="A screenshot of two buttons. The top clearly says an unpressed toggle button in light blue text on a dark button. The second button has unreadable dark blue text on a dark rectangle within a light blue button"></p>
<p>That cursed second button says &quot;A pressed toggle button&quot; for those of you who do not have perfect vision and did not press your eyeball against the screen.</p>
<p>Adding a backplate does make sense and provide a safeguard for readability in some situations: text over images, for example:</p>
<figure>
  <img src="/writing/assets/hcm-image-text-backplate.png" alt="Two lines of text overlaid on a brightly colored photo of a curious siamese cat approaching the camera against a blurred outside background of a yurt, some people, and a sunny day. The text is a heading saying Cats are cool, and a smaller line saying and some subtitle text too, I guess.">
  <figcaption>This is pretty useful, and I believe images were the original use case for text backplates.</figcaption>
</figure>
<p>However, adding a <code>Canvas</code> backplate for text that has an explicit (system) <code>color</code> style is risky at best, even more so when an explicit (system) background color was also provided. The use of system color keywords presumably in a forced-colors media query already implies a certain amount of intentionality from the author, so dealing with a text backplate often feels like fighting with the browser over who can deliver better colors to the user.</p>
<p>Additionally, the backplate is fully outside web authors' control, and the only thing we can do to fix it is add <code>forced-color-adjust: none</code>:</p>
<p><img src="/writing/assets/hcm-correct-button-colors.png" alt="The same two buttons. Now the second button looks just fine, with dark text on a light blue background."></p>
<p>And then weep.</p>
<h2 id="wait%2C-why-are-we-crying%3F">Wait, why are we crying?</h2>
<p>The problem with adding <code>forced-color-adjust: none</code> is that it allows <em>all</em> CSS-styled colors to come through on that element, not just system colors. So even if you (a responsible developer who cares enough about accessibility to have stumbled on this blog) craft beautifully accessible forced-colors CSS using only system color keywords, someone else may come along and unintentionally screw it up. Usually without ever knowing that they affected high contrast mode styles at all.</p>
<p>It looks a bit like this:</p>
<p>Your code: beautiful, lovely, perfect.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br>  <span class="token selector">button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>    <span class="token property">forced-color-adjust</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br>    <span class="token property">background-color</span><span class="token punctuation">:</span> Highlight<span class="token punctuation">;</span><br>    <span class="token property">color</span><span class="token punctuation">:</span> HighlightText<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
<p>Their code: terrible. (But if we're being honest this is perfectly normal and reasonable)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.custom-button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>  <span class="token property">background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span><br>  <span class="token property">color</span><span class="token punctuation">:</span> purple<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre>
<p>Since the addition of a <code>.custom-button</code> class creates a higher specificity style, it overrides the original style even though it's outside a forced-colors media query and clearly not intended to affect high contrast mode. And because of the original <code>forced-color-adjust: none</code>, the random red and purple colors come through:</p>
<figure>
  <img src="/writing/assets/hcm-color-override.png" alt="The same two buttons. This time, the second button has a bright red background with purple text.">
  <figcaption>Sorry about the unhinged color choice, that one's on me.</figcaption>
</figure>
<p>In a collaborative environment, someone somewhere will always take your code and extend or customize it. Often this involves writing styles with a higher specificity selector. Or even a same-specificity selector that comes after your CSS. None of the code involved is unusual or bad practice, which makes it all the worse that it trips up so spectacularly and unpredictably on <code>forced-color-adjust: none</code>.</p>
<p>Individual bugs are fixable, but the downstream CSS author has to manually re-add the forced-color styles back in. Which means the author of that code needs to be aware that this is necessary, even though they themselves never touched or directly overrode forced-colors styles:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.custom-button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>  <span class="token property">background-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span><br>  <span class="token property">color</span><span class="token punctuation">:</span> purple<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">forced-colors</span><span class="token punctuation">:</span> active<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br>  <span class="token selector">.custom-button[aria-pressed=true]</span> <span class="token punctuation">{</span><br>    <span class="token property">background-color</span><span class="token punctuation">:</span> Highlight<span class="token punctuation">;</span><br>    <span class="token property">color</span><span class="token punctuation">:</span> HighlightText<span class="token punctuation">;</span><br>  <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre>
<p>Authors are especially unlikely to realize when they need this, because most of the time they don't. And in practice, web authors should not need to be aware of this level of minutiae when making small style tweaks. If you're the unlucky schmuck maintaining this stuff, good luck trying to keep up-to-date documentation on which specific selectors will need forced-color styles re-asserted every time they are customized.</p>
<p>The library I work on consistently gets a trickle of high contrast questions from partner teams that are caused by variations of this issue. And those are just the ones that get caught. Changing the colors of a shared base control is the most common style customization out there, and every single use of <code>forced-color-adjust: none</code> is one CSS line away from breaking in high contrast.</p>
<h2 id="ok-i-get-it%2C-i-just-won't-use-it">OK I get it, I just won't use it</h2>
<p>So if <code>forced-color-adjust: none</code> is a foot gun, is it possible to avoid it?</p>
<p>No.</p>
<p>Well, I suppose it could be possible for projects that have fairly simple controls and content, but usually no. Here is a reduced list of use cases for overriding colors off the top of my head:</p>
<ul>
<li>A primary button style to differentiate the primary action from secondary actions (e.g. for Save / Cancel buttons)</li>
<li>The pressed state of a toggle button</li>
<li>The selected date of a datepicker</li>
<li>Many other selected item styles (tabs, options, rows in a grid, etc.)</li>
<li>Highlighting the current page in a navigation section</li>
</ul>
<p>Some of those could potentially use a design that doesn't rely on color at all (e.g. using a check icon to denote selection), but in practice at least sometimes you will need custom high contrast text colors. In theory, putting effort into high contrast styles is a good practice that can improve the user experience quite a bit, making it easier to identify buttons, selected items, and other visual cues at a glance.</p>
<p>In practice, doing so also makes the high contrast UX much more fragile and likely to break over time. And since this is accessibility, the bugs will likely go uncaught for a frustrating length of time as well.</p>
<h2 id="so-what-should-we-do%3F">So what should we do?</h2>
<p>Cry. Drink. Drunk cry.</p>
<p>But really, it's still probably good to customize high contrast colors when needed, which means you'll need to use <code>forced-color-adjust: none</code> sometimes. Try to keep those instances both infrequent and tightly scoped. Never add <code>forced-color-adjust</code> at a higher level than you need to. And if it's only necessary in one state (e.g. hover or pressed), only add it to that state.</p>
<p>Perhaps someday text backplates will go away (at least for non-image backgrounds) and we can all stop using <code>forced-color-adjust: none</code> entirely. (Except for those weird instances like color pickers where you actually want it. Please don't @ me with your random good use cases, I know they exist.)</p>

    ]]></content>
  </entry>
	
</feed>